Browse Source

Merge branch 'accompaniment' into online

wolyshaw 4 years ago
parent
commit
6c4b8d9dcd

+ 27 - 0
src/components/Pagination/index.vue

@@ -15,6 +15,7 @@
 
 <script>
 import { scrollTo } from '@/utils/scroll-to'
+import { Searchs } from '@/helpers'
 export default {
   name: 'Pagination',
   props: {
@@ -51,6 +52,10 @@ export default {
     hidden: {
       type: Boolean,
       default: false
+    },
+    sync: {
+      type: Boolean,
+      default: false
     }
   },
   computed: {
@@ -71,18 +76,40 @@ export default {
       }
     }
   },
+  mounted() {
+    if (this.sync) {
+      const searchs = new Searchs(this.$route.fullPath)
+      const active = searchs.get()
+      if (active && active.page) {
+        for (const key in active.page) {
+          if (active.page.hasOwnProperty(key)) {
+            const item = active.page[key]
+            this.$emit('update:' + key, item)
+          }
+        }
+      }
+    }
+  },
   methods: {
+    syncStore() {
+      if (this.sync) {
+        const searchs = new Searchs(this.$route.fullPath)
+        searchs.update(this._props, undefined, 'page')
+      }
+    },
     handleSizeChange (val) {
       this.$emit('pagination', { page: this.currentPage, limit: val })
       if (this.autoScroll) {
         scrollTo(0, 800)
       }
+      this.syncStore()
     },
     handleCurrentChange (val) {
       this.$emit('pagination', { page: val, limit: this.pageSize })
       if (this.autoScroll) {
         scrollTo(0, 800)
       }
+      this.syncStore()
     }
   }
 }

+ 33 - 0
src/components/save-form/README.md

@@ -0,0 +1,33 @@
+#### save-form
+
+背景:为保存当页搜索数据,并且不使用keepAlive
+
+###### 使用方法:
+
+``` html
+<saveform :model="form">
+  <el-form-item prop="input">
+    <el-input v-model="form.input">
+  </el-form-item>
+</saveform>
+```
+
+使用时改动较小,仅须将`el-form`修改为`saveform`即可,组件会在验证通过后自动保存当前表单数据,在reset时自动清空数据
+
+数据保存使用的是`sessionStorage`故关闭标签后数据会自动清空
+
+###### 翻页保存:
+``` html
+<pagination
+  :total.sync="rules.total"
+  :page.sync="rules.page"
+  :limit.sync="rules.limit"
+  :page-sizes="rules.page_size"
+  :sync="true"
+  @pagination="getList"
+/>
+```
+请按照以上示例使用翻页,添加了`sync`字段使用此字段才会保存翻页数据
+
+###### 其他:
+请在`@/router/notKeepAliveList`中添加当页路由,保证当前页面不会keepAlive

+ 47 - 0
src/components/save-form/index.vue

@@ -0,0 +1,47 @@
+<template>
+  <el-form
+    v-bind="{...$attrs, ...$props}"
+    v-on="$listeners"
+    ref="form"
+  >
+    <slot/>
+  </el-form>
+</template>
+<script>
+import { Searchs } from '@/helpers'
+export default {
+  name: 'save-form',
+  props: ['model'],
+  data() {
+    return {
+      searchs: null
+    }
+  },
+  mounted() {
+    const searchs = new Searchs(this.$route.fullPath)
+    this.searchs = searchs
+    const active = searchs.get()
+    for (const key in active.form) {
+      if (active.form.hasOwnProperty(key)) {
+        const item = active.form[key]
+        this.model[key] = item
+      }
+    }
+  },
+  methods: {
+    validate(FC) {
+      this.$refs.form.validate(valid => {
+        FC(valid)
+        if (valid) {
+          this.searchs.update(this.model, undefined, 'form')
+        }
+      })
+    },
+    resetFields() {
+      this.searchs.update(this.model, undefined, 'form')
+      this.searchs.update({}, undefined, 'page')
+      this.$refs.form.resetFields()
+    }
+  },
+}
+</script>

+ 5 - 0
src/constant/index.js

@@ -134,3 +134,8 @@ export const kitGroupPurchaseType = {
   FREE: '赠送',
   GROUP: '团购'
 }
+
+export const songUseType = {
+  PERSON: '个人',
+  COMMON: '公用',
+}

+ 77 - 0
src/helpers/index.js

@@ -0,0 +1,77 @@
+/* eslint-disable no-empty */
+export class Searchs {
+  saveKey = 'searchs'
+
+  initSearch = {
+    form: {},
+    page: {},
+  }
+
+  searchs = {}
+
+  constructor(key) {
+    this.key = key
+    this.searchs = this.parse()
+  }
+
+  save() {
+    sessionStorage.setItem(this.saveKey, JSON.stringify(this.searchs))
+  }
+
+  parse() {
+    let json = {}
+    try {
+      const val = sessionStorage.getItem(this.saveKey)
+      json = JSON.parse(val) || {}
+    } catch (error) {}
+    return json
+  }
+
+  get(key) {
+    const k = (key || this.key)
+    if (!this.searchs[k]) {
+      this.searchs[k] = {...initSearch}
+    }
+    return this.searchs[k]
+  }
+
+  remove(type) {
+    if (this.searchs && this.searchs[this.key]) {
+      type ? this.searchs[this.key][type] : this.searchs[this.key]
+      this.save()
+    }
+    return this.searchs
+  }
+
+  getSearchs() {
+    return this.searchs
+  }
+
+  removeByKey(key) {
+    delete this.searchs[key]
+    this.save()
+    return this.searchs
+  }
+
+  removeAll() {
+    this.searchs = {}
+    sessionStorage.setItem(this.saveKey, JSON.stringify(this.searchs))
+    return this.searchs
+  }
+
+  update(data, key, type) {
+    const k = (key || this.key)
+    if (type) {
+      this.searchs[k][type] = data
+    } else {
+      this.searchs[k] = data
+    }
+    this.save()
+    return this.searchs
+  }
+}
+
+const initSearch = {
+  form: {},
+  page: {},
+}

+ 6 - 1
src/layout/components/AppMain.vue

@@ -3,20 +3,25 @@
     <!--   -->
     <transition name="fade-transform"
                 mode="out-in">
-      <keep-alive exclude="modal-chargesForm">
+      <keep-alive v-if="needKeep">
         <router-view :key="key" />
       </keep-alive>
+      <router-view v-else :key="key" />
     </transition>
   </section>
 </template>
 
 <script>
+import notKeepAliveList from '@/router/notKeepAliveList'
 export default {
   name: 'AppMain',
   computed: {
     key () {
       return this.$route.path
     },
+    needKeep() {
+      return !notKeepAliveList.includes(this.$route.fullPath)
+    },
     cachedViews () {
       return this.$store.state.tagsView.cachedViews
     },

+ 18 - 3
src/layout/components/TagsView.vue

@@ -29,6 +29,7 @@
 <script>
 import ScrollPane from '@/components/ScrollPane'
 import { generateTitle } from '@/utils/i18n'
+import { Searchs } from '@/helpers'
 
 export default {
   name: 'TagsView',
@@ -66,7 +67,7 @@ export default {
 
   },
   methods: {
-    // generateTitle by vue-i18n  
+    // generateTitle by vue-i18n
     generateTitle,
     generateRoute () {
       if (this.$route.path && this.$route.path != '/') {
@@ -78,13 +79,25 @@ export default {
     isActive (route) {
       return route.path === this.$route.path
     },
-    addViewTags () {
+    syncTagViewAndSaveForm() {
+      console.log(this.$store.state.tagsView.visitedViews)
+      const keys = this.$store.state.tagsView.visitedViews.map(item => item.fullPath)
+      const searchs = new Searchs()
+      const sks = Object.keys(searchs.getSearchs())
+      for (const item of sks) {
+        if (!keys.includes(item)) {
+          searchs.removeByKey(item)
+        }
+      }
+    },
+    async addViewTags () {
       const route = this.generateRoute()
       if (!route) {
         return false
       }
       // console.log(this.$route)
-      this.$store.dispatch('addVisitedViews', route)
+      await this.$store.dispatch('addVisitedViews', route)
+      this.syncTagViewAndSaveForm()
     },
     moveToCurrentTag () {
       const tags = this.$refs['tag']
@@ -98,6 +111,8 @@ export default {
       })
     },
     closeSelectedTag (view) {
+      const searchs = new Searchs()
+      searchs.remove(this.$route.fullPath)
       this.$store.dispatch('delVisitedViews', view).then((views) => {
         if (this.isActive(view)) {
           const latestView = views.slice(-1)[0]

+ 10 - 0
src/router/index.js

@@ -74,6 +74,15 @@ export const constantRoutes = [
         noCache: '1',
         title: '侧边栏'
       }
+    }, {
+      name: '提交表单',
+      path: 'save-form',
+      component: () => import('@/views/save-form-test'),
+      hidden: true,
+      meta: {
+        noCache: '1',
+        title: '提交表单'
+      }
     }]
   },
   {
@@ -137,6 +146,7 @@ export const asyncRoutes = {
   main: () => import('@/views/main/index'),
   // 内容管理
   contentManager: () => import('@/views/contentManager/index'),
+  accompaniment: () => import('@/views/accompaniment'),
   contentOperation: () => import('@/views/contentManager/contentOperation'),
   // 系统日志
   journal: () => import('@/views/workBenchManager/journal/index'),

+ 5 - 0
src/router/notKeepAliveList.js

@@ -0,0 +1,5 @@
+export default [
+  '/setSilder/save-form',
+  '/contentManager/accompaniment',
+  // '/operateManager/HumanResources'
+]

+ 3 - 1
src/store/index.js

@@ -7,6 +7,7 @@ import user from './modules/user'
 import permission from './modules/permission'
 import buildTeam from './modules/buildTeam'
 import tagsView from './modules/tagsView'
+import selects from './modules/selects'
 
 Vue.use(Vuex)
 
@@ -17,7 +18,8 @@ const store = new Vuex.Store({
     user,
     permission,
     buildTeam,
-    tagsView
+    tagsView,
+    selects
   },
   getters
 })

+ 47 - 0
src/store/modules/selects.js

@@ -0,0 +1,47 @@
+/* eslint-disable no-empty */
+import { branchQueryPage } from '@/api/specialSetting'
+import { getSubject } from '@/api/buildTeam'
+
+/**
+ *
+ * 为避免重复请求全局参数,将需要使用的全局参数放到vuex中缓存
+ *
+ * 使用:
+ *
+ * 按照需要直接使用 this.$store.dispatch('action', force: Bool 是否强制刷新)
+ *
+ * 直接从this.$store.state.name 中获取数据
+ */
+
+export default {
+  state: {
+    branchs: [],
+    subjects: [],
+  },
+  mutations: {
+    commit_branchs: (state, branchs) => {
+      state.branchs = branchs
+    },
+    commit_subjects: (state, subjects) => {
+      state.subjects = subjects
+    },
+  },
+  actions: {
+    async setBranchs({ commit, state }, force) {
+      if (!state.branchs.length || force === true) {
+        try {
+          const res = await branchQueryPage({rows: 9999})
+          commit('commit_branchs', res.data.rows)
+        } catch (error) {}
+      }
+    },
+    async setSubject({ commit, state }, force) {
+      if (!state.subjects.length || force === true) {
+        try {
+          const res = await getSubject({rows: 9999})
+          commit('commit_subjects', res.data)
+        } catch (error) {}
+      }
+    }
+  }
+}

+ 7 - 3
src/utils/vueFilter.js

@@ -1,7 +1,7 @@
 import Vue from 'vue'
 import dayjs from 'dayjs'
 import numeral from 'numeral'
-import { feeProject, feeType, saleType, payStatus, payOrderType, paymentPatternType, payUserType, userPaymentType, courseType, auditType, auditPaymentType, orderServerType, orderAuditType } from '../constant'
+import { feeProject, feeType, saleType, payStatus, payOrderType, paymentPatternType, payUserType, userPaymentType, courseType, auditType, auditPaymentType, orderServerType, orderAuditType, songUseType } from '../constant'
 
 // 合并数组
 Vue.filter('joinArray', (value, type) => {
@@ -693,7 +693,7 @@ Vue.filter('auditPaymentType', value => {
   return auditPaymentType[value]
 })
 
-// 销售收入和服务收入 
+// 销售收入和服务收入
 Vue.filter('orderServer', value => {
   return orderServerType[value]
 })
@@ -702,4 +702,8 @@ Vue.filter('orderServer', value => {
 Vue.filter('orderAuditType', value => {
   orderAuditType[''] = '审核通过'
   return orderAuditType[value]
-})
+})
+
+Vue.filter('songUseTypeFormat', value => {
+  return songUseType[value]
+})

+ 28 - 17
src/views/HumanResources/index.vue

@@ -9,15 +9,16 @@
            v-permission="'employeeInfo/insert'"
            @click="openTypes('create')">添加</div>
       <!-- 搜索标题 -->
-      <el-form :inline="true"
-               class="searchForm"
-               v-model.trim="searchForm">
-        <el-form-item>
+      <saveform :inline="true"
+                ref="searchForm"
+                class="searchForm"
+               :model.sync="searchForm">
+        <el-form-item prop="userNameOrIdOrMobile">
           <el-input placeholder="姓名手机号"
                     v-model.trim="searchForm.userNameOrIdOrMobile"
                     clearable></el-input>
         </el-form-item>
-        <el-form-item>
+        <el-form-item prop="sourceFrom">
           <el-select v-model.trim="searchForm.sourceFrom"
                      clearable
                      filterable
@@ -28,7 +29,7 @@
                        value="转介绍"></el-option>
           </el-select>
         </el-form-item>
-        <el-form-item>
+        <el-form-item prop="status">
           <el-select v-model.trim="searchForm.status"
                      clearable
                      filterable
@@ -47,7 +48,7 @@
                        value="DIMISSION"></el-option>
           </el-select>
         </el-form-item>
-        <el-form-item>
+        <el-form-item prop="position">
           <el-select v-model.trim="searchForm.position"
                      clearable
                      filterable
@@ -60,7 +61,7 @@
                        value="TEACHING"></el-option>
           </el-select>
         </el-form-item>
-        <el-form-item>
+        <el-form-item prop="organId">
           <el-select v-model.trim="searchForm.organId"
                      placeholder='请选择分部'
                      clearable
@@ -72,7 +73,7 @@
             </el-option>
           </el-select>
         </el-form-item>
-        <el-form-item prop="subjectIds"
+        <el-form-item prop="subjectId"
                       :label-width="formLabelWidth">
           <el-select v-model.trim="searchForm.subjectId"
                      clearable
@@ -95,7 +96,7 @@
             end-placeholder="更新结束日期">
           </el-date-picker>
         </el-form-item>
-        <el-form-item>
+        <el-form-item prop="operator">
           <el-input placeholder="归属管理HR"
                     v-model.trim="searchForm.operator"
                     clearable></el-input>
@@ -107,7 +108,7 @@
                      type="primary"
                      style="background-color: #409EFF;border-color: #409EFF;">重置</el-button>
         </el-form-item>
-      </el-form>
+      </saveform>
       <div class="tableWrap">
         <el-table :data="tableList"
                   :header-cell-style="{background:'#EDEEF0',color:'#444'}">
@@ -290,10 +291,11 @@
             </template>
           </el-table-column>
         </el-table>
-        <pagination :total="pageInfo.total"
+        <pagination :total.sync="pageInfo.total"
                     :page.sync="pageInfo.page"
                     :limit.sync="pageInfo.limit"
                     :page-sizes="pageInfo.page_size"
+                    sync
                     @pagination="getList" />
       </div>
     </div>
@@ -304,6 +306,7 @@
                @close="onFormClose('ruleForm')"
                width="1050px">
       <hrform :detail.sync="rowDetail"
+              v-if="typeStatus"
               :organList="organList"
               :subjectList="subjectList"
               :close="onFormClose"
@@ -336,6 +339,7 @@
 </template>
 <script>
 import pagination from "@/components/Pagination/index";
+import saveform from '@/components/save-form'
 import dayjs from 'dayjs'
 import hrform from './form'
 // import store from '@/store'
@@ -367,7 +371,7 @@ const initSearch = {
   dates: []
 }
 export default {
-  components: { pagination, hrform },
+  components: { pagination, hrform, saveform },
   name: "helpCategory",
   data () {
     return {
@@ -495,15 +499,22 @@ export default {
       })
     },
     search () {
+      this.$refs.searchForm.validate(valid => {
+        this.pageInfo = {
+          ...this.pageInfo,
+          page: 1
+        }
+        this.getList()
+      })
+    },
+    onReSet () {
       this.pageInfo = {
         ...this.pageInfo,
         page: 1
       }
+      this.$refs.searchForm.resetFields()
       this.getList()
-    },
-    onReSet () {
-      this.searchForm = { ...initSearch }
-      this.getList()
+
     },
     onTypeSubmit (formName) {
       // 添加数据

+ 38 - 0
src/views/accompaniment/api.js

@@ -0,0 +1,38 @@
+import request from '@/utils/request2'
+
+export const QueryPage = params => {
+  return request({
+    url: '/api-web/sysExamSong/queryPage',
+    data: {},
+    params,
+    requestType: 'form'
+  })
+}
+
+export const Add = data => {
+  return request({
+    url: '/api-web/sysExamSong/add',
+    method: 'post',
+    data,
+    params: {},
+  })
+}
+
+export const Update = data => {
+  return request({
+    url: '/api-web/sysExamSong/update',
+    method: 'post',
+    data,
+    params: {},
+  })
+}
+
+export const Del = id => {
+  return request({
+    url: '/api-web/sysExamSong/del/' + id,
+    method: 'post',
+    data: {},
+    params: {},
+    requestType: 'form'
+  })
+}

+ 238 - 0
src/views/accompaniment/index.vue

@@ -0,0 +1,238 @@
+<template>
+    <div class='m-container'>
+    <h2>
+      <div class="squrt"></div>教学伴奏
+    </h2>
+    <div class="m-core">
+      <el-button @click="open('COMMON')" type="primary" v-permission="'sysExamSong/add'">添加公用伴奏</el-button>
+      <el-button @click="open('PERSON')" type="primary" v-permission="'sysExamSong/add'">添加个人伴奏</el-button>
+      <saveform ref="searchForm" :model.sync="searchForm" inline style="margin-top: 20px">
+        <el-form-item prop="search">
+          <el-input v-model="searchForm.search" clearable placeholder="伴奏编号/伴奏名称"/>
+        </el-form-item>
+        <el-form-item prop="type">
+          <el-select v-model="searchForm.type" clearable placeholder="请选择类型">
+            <el-option
+              v-for="(item, key) in songUseType"
+              :key="key"
+              :label="item"
+              :value="key"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item prop="subjectId">
+          <el-select v-model="searchForm.subjectId" clearable placeholder="请选择声部">
+            <el-option v-for="item in selects.subjects" :value="item.id" :label="item.name" :key="item.id"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-button @click="submit" type="primary">提交</el-button>
+        <el-button @click="reset" type="danger">重置</el-button>
+      </saveform>
+      <el-table
+        style="width: 100%"
+        :header-cell-style="{background:'#EDEEF0',color:'#444'}"
+        :data="tableList"
+      >
+        <el-table-column
+          align="center"
+          prop="id"
+          label="伴奏编号"
+        />
+        <el-table-column
+          align="center"
+          prop="name"
+          label="伴奏名称"
+          width="180px"
+        />
+        <el-table-column
+          align="center"
+          prop="type"
+          label="类型"
+        >
+          <template slot-scope="scope">
+            {{scope.row.type | songUseTypeFormat}}
+          </template>
+        </el-table-column>
+        <el-table-column
+          align="center"
+          prop="subjectNames"
+          label="声部"
+          width="180px"
+        >
+          <template slot-scope="scope">
+            <el-tooltip class="item" effect="dark" :content="scope.row.subjectNames">
+              <div class="remark">{{scope.row.subjectNames}}</div>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+        <el-table-column
+          align="center"
+          prop="speed"
+          label="速度"
+        />
+        <el-table-column
+          align="center"
+          prop="createUserName"
+          label="上传人"
+          width="180px"
+        />
+        <el-table-column
+          align="center"
+          prop="createTime"
+          label="上传时间"
+          width="180px"
+        />
+        <el-table-column
+          align="center"
+          width="180px"
+          label="操作"
+          fixed="right"
+        >
+          <template slot-scope="scope">
+            <el-button type="text"
+              @click="player(scope.row)"
+              :disabled="!scope.row.url"
+            >播放</el-button>
+            <el-button type="text"
+              @click="edit(scope.row)"
+              v-permission="'sysExamSong/update'"
+            >修改</el-button>
+            <el-button type="text"
+              @click="remove(scope.row)"
+              v-permission="'sysExamSong/del'"
+            >删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination
+        sync
+        :total.sync="rules.total"
+        :page.sync="rules.page"
+        :limit.sync="rules.limit"
+        :page-sizes="rules.page_size"
+        @pagination="FetchList"
+      />
+    </div>
+    <el-dialog width="400px" :visible.sync="audioVisible" title="播放伴奏">
+      <audio v-if="audioVisible" style="display: block; margin: auto" controls :src="activeUrl"/>
+    </el-dialog>
+    <el-dialog
+      :title="title"
+      :visible.sync="visible"
+      width="500px"
+    >
+      <submit-form
+        :detail="detail"
+        :type="type"
+        @submited="FetchList"
+        @close="visible = false"
+      />
+    </el-dialog>
+  </div>
+</template>
+<script>
+import saveform from '@/components/save-form'
+import pagination from '@/components/Pagination/index'
+import { songUseType } from '@/constant'
+import { QueryPage, Del } from './api'
+import form from './modals/form'
+export default {
+  name: 'accompaniment',
+  components: {
+    saveform,
+    pagination,
+    'submit-form': form
+  },
+  data() {
+    return {
+      type: '',
+      activeUrl: '',
+      songUseType,
+      audioVisible: false,
+      tableList: [],
+      searchForm: {
+        search: '',
+        type: '',
+        subjectId: ''
+      },
+      rules: {
+        // 分页规则
+        limit: 10, // 限制显示条数
+        page: 1, // 当前页
+        total: 0, // 总条数
+        page_size: [10, 20, 40, 50] // 选择限制显示条数
+      },
+      detail: null,
+      visible: false,
+    }
+  },
+  computed: {
+    selects() {
+      return this.$store.state.selects
+    },
+    title() {
+      const t1 = this.detail ? '修改' : '添加'
+      const t2 = this.type === 'COMMON' ? '公用' : '个人'
+      return t1 + t2 + '伴奏'
+    },
+  },
+  mounted() {
+    this.$store.dispatch('setSubject')
+    this.FetchList()
+  },
+  methods: {
+    async FetchList() {
+      try {
+        const res = await QueryPage({
+          ...this.searchForm,
+          page: this.rules.page,
+          limit: this.rules.limit,
+        })
+        this.tableList = res.data.rows
+        this.$set(this.rules, 'total', res.data.total)
+      } catch (error) {}
+    },
+    submit() {
+      this.$set(this.rules, 'page', 1)
+      this.$refs.searchForm.validate(valid => {
+        if (valid) {
+          this.FetchList()
+        }
+      })
+    },
+    reset() {
+      this.$refs.searchForm.resetFields()
+      this.FetchList()
+    },
+    player(row) {
+      this.activeUrl = row.url
+      this.audioVisible = true
+    },
+    edit(row) {
+      this.detail = row
+      this.visible = true
+    },
+    open(type) {
+      this.type = type
+      this.visible = true
+    },
+    async remove(row) {
+      try {
+        await this.$confirm('是否确认删除此伴奏?', '提示', {
+          type: 'warning',
+        })
+        await Del(row.id)
+        this.$message.success('删除成功')
+        this.FetchList()
+      } catch (error) {}
+    },
+  }
+}
+</script>
+<style lang="less" scoped>
+  .remark{
+    display: inline;
+    overflow: hidden;
+    white-space: pre;
+  }
+</style>

+ 138 - 0
src/views/accompaniment/modals/form.vue

@@ -0,0 +1,138 @@
+<template>
+  <div>
+    <el-form ref="form" :model="form" label-width="100px">
+      <el-form-item
+        prop="name"
+        label="伴奏名称"
+        :rules="[{required: true, message: '请输入伴奏名称'}]"
+      >
+        <el-input placeholder="请输入伴奏名称" v-model="form.name"/>
+      </el-form-item>
+      <el-form-item
+        prop="subjectIds"
+        label="伴奏声部"
+        :rules="[{required: true, message: '请选择伴奏声部'}]"
+      >
+        <el-select style="width: 100%!important;" collapse-tags multiple v-model="form.subjectIds" placeholder="请选择伴奏声部">
+          <el-option v-for="item in selects.subjects" :value="String(item.id)" :label="item.name" :key="item.id"></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item
+        prop="speed"
+        label="伴奏速度"
+        :rules="[{required: true, message: '请输入伴奏速度'}]"
+      >
+        <el-input type="number" placeholder="请输入伴奏速度" v-model="form.speed"/>
+      </el-form-item>
+      <el-form-item
+        label="伴奏文件"
+        prop="url"
+        :rules="[{required: true, message: '请选择伴奏文件'}]"
+      >
+        <el-upload
+          action="/api-web/uploadFile"
+          :headers="headers"
+          :on-success="success"
+          :on-remove="remove"
+          :limit="1"
+          :file-list="filelist"
+          accept=".mp3, .aac">
+          <el-button size="small" type="primary" plain>点击上传</el-button>
+          <div slot="tip" class="el-upload__tip">仅支持上传 mp3/aac 格式音频文件</div>
+        </el-upload>
+      </el-form-item>
+      <div class="btns">
+        <el-button type="primary" @click="submit">提交</el-button>
+        <el-button @click="$listeners.close">取消</el-button>
+      </div>
+    </el-form>
+  </div>
+</template>
+<script>
+import { getToken } from "@/utils/auth"
+import { Add, Update } from '../api'
+export default {
+  props: ['detail', 'type'],
+  data() {
+    return {
+      headers: {
+        Authorization: getToken()
+      },
+      filelist: [],
+      form: {
+        name: '',
+        subjectIds: [],
+        speed: '',
+        url: ''
+      }
+    }
+  },
+  computed: {
+    selects() {
+      return this.$store.state.selects
+    }
+  },
+  mounted() {
+    this.$store.dispatch('setSubject')
+    if (this.detail) {
+      for (const key in this.form) {
+        if (this.form.hasOwnProperty(key)) {
+          if (key === 'subjectIds') {
+            this.$set(this.form, 'subjectIds', (this.detail[key] || '').split(','))
+          } else {
+            this.$set(this.form, key, this.detail[key])
+          }
+        }
+      }
+      this.filelist = [{
+        name: this.detail.url,
+        url: this.detail.url,
+      }]
+    }
+  },
+  methods: {
+    remove() {
+      this.filelist = []
+      this.$set(this.form, 'url', '')
+    },
+    success(res) {
+      if (res.code == 200) {
+        this.filelist = [{
+          name: res.data.url,
+          url: res.data.url,
+        }]
+        this.$set(this.form, 'url', res.data.url)
+      } else {
+        this.$message.error(res.msg || '上传失败')
+      }
+    },
+    async submit() {
+      this.$refs.form.validate(async (valid) => {
+        if (valid) {
+          if (!this.detail) {
+            await Add({
+              ...this.form,
+              type: this.type,
+            })
+            this.$message.success('提交成功')
+          } else {
+            await Update({
+              ...this.form,
+              type: this.type,
+              id: this.detail.id
+            })
+            this.$message.success('修改成功')
+          }
+          this.$listeners.close()
+          this.$listeners.submited()
+        }
+      })
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+  .btns{
+    text-align: right;
+  }
+</style>

+ 0 - 0
src/views/save-form-test/api.js


+ 140 - 0
src/views/save-form-test/index.vue

@@ -0,0 +1,140 @@
+<template>
+  <div class='m-container'>
+    <h2>
+      <div class="squrt"></div>保存表单演示
+    </h2>
+    <div class="m-core">
+      <saveform ref="form" :model.sync="form" inline>
+        <el-form-item
+          label="内容"
+          prop="input"
+          :rules="[{required: true}]"
+        >
+          <el-input v-model="form.input" placeholder="请输入内容"/>
+        </el-form-item>
+        <el-form-item
+          label="内容2"
+          prop="input2"
+          :rules="[{required: true}]"
+        >
+          <el-input v-model="form.input2" placeholder="请输入内容2"/>
+        </el-form-item>
+        <el-form-item label="活动区域" prop="region">
+          <el-select v-model="form.region" placeholder="请选择活动区域">
+            <el-option label="区域一" value="shanghai"></el-option>
+            <el-option label="区域二" value="beijing"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="活动时间" required>
+          <el-col :span="11">
+            <el-form-item prop="date1">
+              <el-date-picker type="date" placeholder="选择日期" v-model="form.date1" style="width: 100%;"></el-date-picker>
+            </el-form-item>
+          </el-col>
+          <el-col class="line" :span="2">-</el-col>
+          <el-col :span="11">
+            <el-form-item prop="date2">
+              <el-time-picker placeholder="选择时间" v-model="form.date2" style="width: 100%;"></el-time-picker>
+            </el-form-item>
+          </el-col>
+        </el-form-item>
+        <el-form-item label="即时配送" prop="delivery">
+          <el-switch v-model="form.delivery"></el-switch>
+        </el-form-item>
+        <el-form-item label="活动性质" prop="type">
+          <el-checkbox-group v-model="form.type">
+            <el-checkbox label="美食/餐厅线上活动" value="1" name="type"></el-checkbox>
+            <el-checkbox label="地推活动" value="2" name="type"></el-checkbox>
+            <el-checkbox label="线下主题活动" value="3" name="type"></el-checkbox>
+            <el-checkbox label="单纯品牌曝光" value="4" name="type"></el-checkbox>
+          </el-checkbox-group>
+        </el-form-item>
+        <el-form-item label="特殊资源" prop="resource">
+          <el-radio-group v-model="form.resource">
+            <el-radio label="线上品牌商赞助"></el-radio>
+            <el-radio label="线下场地免费"></el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="活动形式" prop="desc">
+          <el-input type="textarea" v-model="form.desc"></el-input>
+        </el-form-item>
+        <el-form-item label="活动时间范围" prop="times">
+          <el-date-picker
+            v-model="form.times"
+            type="daterange"
+            range-separator="至"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期">
+          </el-date-picker>
+        </el-form-item>
+        <el-button @click="submit">提交</el-button>
+        <el-button @click="reset">重置</el-button>
+        <el-button @click="testvisible = true">打开弹窗</el-button>
+      </saveform>
+    </div>
+    <pagination :total="rules.total"
+                :page.sync="rules.page"
+                :limit.sync="rules.limit"
+                :page-sizes="rules.page_size"
+                :sync="true"
+                @pagination="getList" />
+    <el-dialog :visible.sync="testvisible" destroy-on-close>
+      <test v-if="testvisible"/>
+      <template #footer>
+        <el-button @click="testvisible = false">关闭</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+<script>
+import saveform from '@/components/save-form'
+import pagination from "@/components/Pagination/index";
+import test from './modals/test'
+export default {
+  components: {
+    saveform,
+    test,
+    pagination
+  },
+  data() {
+    return {
+      testvisible: false,
+      rules: {
+        // 分页规则
+        limit: 10, // 限制显示条数
+        page: 1, // 当前页
+        total: 30, // 总条数
+        page_size: [10, 20, 40, 50] // 选择限制显示条数
+      },
+      form: {
+        input: '',
+        input2: '',
+        region: '',
+        date1: '',
+        date2: '',
+        delivery: '',
+        type: [],
+        resource: '',
+        desc: '',
+        times: []
+      }
+    }
+  },
+  mounted() {
+
+  },
+  methods: {
+    getList() {
+      return Promise.resolve()
+    },
+    submit() {
+      this.$refs.form.validate(valid => {
+        console.log(valid)
+      })
+    },
+    reset() {
+      this.$refs.form.resetFields()
+    }
+  }
+}
+</script>

+ 19 - 0
src/views/save-form-test/modals/test.vue

@@ -0,0 +1,19 @@
+<template>
+  <div>弹窗状态测试</div>
+</template>
+<script>
+export default {
+  created() {
+    console.log('created')
+  },
+  mounted() {
+    console.log('mounted')
+  },
+  updated() {
+    console.log('updated')
+  },
+  destroyed() {
+    console.log('destroyed')
+  }
+}
+</script>

+ 1 - 0
src/vue-shim.d.ts

@@ -0,0 +1 @@
+/// <reference path="../node_modules/vue/types/index.d.ts" />

+ 3 - 0
tsconfig.json

@@ -5,6 +5,9 @@
     "target": "ES6",
     "lib": ["DOM", "ESNext"],
     "outDir": "dist",
+    "moduleResolution": "node",
+    "isolatedModules": true,
+    "module": "esnext",
     "paths": {
       "@/*": ["./src/*"]
     }