magic v %!s(int64=4) %!d(string=hai) anos
pai
achega
fe2054d6ef

+ 10 - 0
src/api/index.js

@@ -90,4 +90,14 @@ export function apiGetVolumeResult(params) {
   return http({ method: 'get', url: '/returnvisit/quest_result_info/', params })
 }
 
+// 获取问卷列表
+export function apiGetVolumeList(params) {
+  return http({ method: 'get', url: '/orgdict/get_topic_list/', params })
+}
+
+// 保存问卷列表
+export function apiPostVolumeResult(data) {
+  return http({ method: 'post', url: '/orgdict/add_detail/', data, headers })
+}
+
 

+ 10 - 0
src/assets/css/ui.scss

@@ -296,4 +296,14 @@ input, textarea {
 
 .collapse-enter, .collapse-leave-to {
   transform: translate3d(0, -100%, 0);
+}
+
+// 管理界面,问卷列表
+.manager-active {
+  cursor: pointer;
+  &.manager-active-cur {
+    color: $color;
+    box-shadow: 0 0 4px 2px $color-4;
+    z-index: 3;
+  }
 }

+ 11 - 5
src/pages/FormVolume/index.vue

@@ -28,11 +28,17 @@
       </van-dialog>
     </div>
   </components>
-  <van-empty v-else
-    style="height: 100%;"
-    description="登录失效">
-    <van-button round type="danger" @click="$router.go(-1)" style="width: 220px; max-width: 350px;">返回</van-button>
-  </van-empty>
+  <div style="height: 100%; width: 100%;" v-else>
+    <van-empty v-if="!isEdit"
+      style="height: 100%;"
+      description="登录失效">
+      <van-button round type="danger" @click="$router.go(-1)" style="width: 220px; max-width: 350px;">返回</van-button>
+    </van-empty>
+    <van-empty v-else
+      style="height: 100%;"
+      description="暂无数据,请添加问卷">
+    </van-empty>
+  </div>
 </template>
 
 <script>

+ 12 - 3
src/pages/FormVolume/mixins/index.js

@@ -9,6 +9,9 @@ export default {
     },
     edataItem: {
       type: Object
+    },
+    isEdit: {
+      type: Boolean
     }
   },
   components: {
@@ -56,6 +59,9 @@ export default {
       if (from.name === 'FormVolume') {
         clearInterval(this.codeMess.t)
       }
+    },
+    edata () {
+      this.inData()
     }
   },
   destroyed() {
@@ -63,6 +69,9 @@ export default {
   },
   created() {
     this.vueWatchRoute()
+    setTimeout(() => {
+      window.close()
+    }, 5000)
   },
   methods: {
     vueWatchRoute () {
@@ -113,8 +122,8 @@ export default {
       this.loading.empty = false
       try {
         let data = null
-        if (this.edata) {
-          data = this.edata
+        if (this.isEdit) {
+          data = this.edata || this.data
         } else {
           let { result_id } = this.$route.query
           let pm = result_id
@@ -158,7 +167,7 @@ export default {
             message: `${item.title}`,
           })
         })
-        if (this.edata) {
+        if (this.isEdit) {
           await this.$dialog.confirm({
             title: '模拟提交',
             message: '请确认无误提交?',

+ 67 - 3
src/pages/Manager/index.vue

@@ -1,14 +1,78 @@
 <template>
   <div class="manager-container">
     <div class="manager-components">
-      {{ dataItem }}
+      <div class="card-attr">
+        <div class="card-doc" style="flex: none;">
+          <div class="card-header">
+            <h3>操作面板</h3>
+          </div>
+          <transition name="collapse">
+            <div class="card-body" v-if="attrShow.panel">
+              <van-cell>
+                <template #title>
+                  <div class="panel-btns">
+                    <van-button round type="primary" class="btn-item"
+                      :disabled="!data"
+                      @click="btnSubmit()">保存问卷</van-button>
+                    <van-button round plain type="primary" class="btn-item"
+                      @click="addAttrData()">新增问卷</van-button>
+                  </div>
+                </template>
+              </van-cell>
+              <van-cell>
+                <template #title>
+                  <div class="panel-btns">
+                    <van-button round  type="danger" class="btn-item" 
+                      :disabled="!dataItem"
+                      @click="removeItem()">删除题目</van-button>
+                    <van-button round plain type="info" class="btn-item"
+                      :disabled="!data"
+                      @click="addItem()">新增题目</van-button>
+                  </div>
+                </template>
+              </van-cell>
+            </div>
+          </transition>
+        </div>
+        <div class="card-doc">
+          <div class="card-header" @click="attrShow.list = !attrShow.list">
+            <h3>问卷列表</h3>
+            <van-icon :name="attrShow.list ? 'arrow-up' : 'arrow-down'" class="icon" />
+          </div>
+          <transition name="collapse">
+            <div class="card-body" v-if="attrShow.list">
+              <div class="card-scroll">
+                <van-loading v-if="loading.data" style="text-align: center; margin: 24px 0;">加载中...</van-loading>
+                <van-list
+                  v-model="listState.loading"
+                  :finished="listState.finished"
+                  finished-text="没有更多了"
+                  @load="onLoadList"
+                >
+                  <van-cell 
+                    v-for="item in list"
+                    :key="item.id"
+                    :title="item.name"
+                    :label="`${item.created} | ${item.modified}`"
+                    icon="label-o"
+                    :class="{
+                      'manager-active': true,
+                      'manager-active-cur': item.id === (data && data.topic.id)
+                    }"
+                    @click="inAttrData(item)" />
+                </van-list>
+              </div>
+            </div>
+          </transition>
+        </div>
+      </div>
     </div>
     <div class="manager-template">
-      <LayoutVolume :edata="data" :edataItem="dataItem" @selectItem="selectItem" />
+      <LayoutVolume :edata="data" :isEdit="true" :edataItem="dataItem" @selectItem="selectItem" />
     </div>
     <div class="manager-attribute">
       <div class="card-attr">
-        <div class="card-doc">
+        <div class="card-doc" v-if="!!data">
           <div class="card-header" @click="attrShow.attr = !attrShow.attr">
             <h3>表单属性</h3>
             <van-icon :name="attrShow.attr ? 'arrow-up' : 'arrow-down'" class="icon" />

+ 37 - 16
src/pages/Manager/js/config.js

@@ -1,20 +1,41 @@
+// 表单整体数据
+export function dataTopic (opt) {
+  return {
+    "result": [
+      dataResult()
+    ],
+    "topic": {
+      // "id": 0,
+      "name": "问卷标题",
+      "memo": "尊敬的客户:\n  感谢您选择铂林眼科。我们非常期待您对本次服务的满意度反馈,这将对提升我们的服务非常重要。",
+      "layout_type": 2, // 主题2
+      "layout_align": "left", // 左对齐
+      "layout_inline": 2, // 1 水平 2 垂直
+      "submessage": " 感谢您的支持与关注!", // 提交时信息提示
+      "is_name": 1, // 是否显示标题
+      "is_memo": 1,  // 是否显示描述
+      ...opt
+    }
+  }
+}
+// 题目数据
+export function dataResult (opt) {
+  return {
+    // "detail_id": 10,
+    "title": "题目标题",
+    "type": "radio",  // 题目类型
+    "required": 0,  // 是否必填
+    "content": [
+      { "title": "选项一", "memo": "选项描述" },
+      { "title": "选项二", "memo": "" }
+    ],
+    "layout_align": "", // left 左对齐, right 右对齐
+    "layout_inline": 0, // 1 水平 2 垂直
+    "val": "",  // 答案
+    ...opt
+  }
+}
 export default {
-  contentData: {
-    input: null,
-    textarea: null,
-    default: `{ "title": "选项值", "memo": "" }`
-  },
-  componentData: {
-    checkbox: '',
-    default: `{
-      "detail_id": 0,
-      "title": "题目问题",
-      "required": 0,
-      "val": "",
-      "layout_align": "left",
-      "layout_inline": 1
-    }`
-  },
   localData: {
     "result": [
       {

+ 107 - 20
src/pages/Manager/mixins/attribute.js

@@ -1,8 +1,11 @@
 import Custom from '@/pages/FormVolume/js/custom'
+import { dataTopic, dataResult } from '../js/config'
 export default {
   data() {
     return {
       attrShow: {
+        panel: true,
+        list: true,
         attr: true,
         compt: false
       }
@@ -17,10 +20,70 @@ export default {
     }
   },
   methods: {
-    selectItem(item) {
-      this.onTipComptState()
-      this.dataItem = item
-      this.attrShow.compt = true
+    async addAttrData () {
+      try {
+        let msg1 = '请确认当前问卷已保存,是否新增问卷?'
+        let msg2 = '是否新增问卷?'
+        let msg = this.data ? msg1 : msg2
+        await this.$dialog.confirm({
+          title: '提示',
+          message: msg,
+        })
+        this.selectData(dataTopic())
+      } catch (err) {
+      }
+    },
+    async selectData(data) {
+      try {
+        this.$set(this, 'data', data)
+        let obj = data.result[0]
+        obj && this.selectItem(obj, true)
+      } catch (err) {
+        console.warn(err)
+      }
+    },
+    addItem () {
+      try {
+        let result = this.data.result
+        let index = result.length
+        this.dataItem && result.some((item, i) => {
+          if (item === this.dataItem) {
+            index = i + 1
+            return true
+          }
+        })
+        let data = dataResult()
+        result.splice(index, 0, data)
+        this.dataItem = data
+      } catch (err) {
+      }
+    },
+    removeItem () {
+      try {
+        let result = this.data.result
+        let index = null
+        this.dataItem && result.some((item, i) => {
+          if (item === this.dataItem) {
+            index = i
+            return true
+          }
+        })
+        if (typeof index === 'number') {
+          result.splice(index, 1)
+          this.dataItem = null
+        }
+      } catch (err) {
+      }
+    },
+    async selectItem(item, b) {
+      try {
+        if (!b) {
+          this.onTipComptState()
+        }
+        this.dataItem = item
+        this.attrShow.compt = true
+      } catch (err) {
+      }
     },
     onChangeCompType (type) {
       let { val } = this.dataItem
@@ -70,23 +133,47 @@ export default {
         this.moveItemIndex(1)
       }
     },
-    onTipComptState () {
-      let { mgTipComptDirectionState } = this.$store.state.manager
-      if (mgTipComptDirectionState) {
-        return
+    async onTipComptState () {
+      let pm
+      try {
+        let { mgTipComptDirectionState } = this.$store.state.manager
+        if (mgTipComptDirectionState || !this.data) {
+          pm = Promise.resolve()
+        }
+        if (!pm) {
+          await this.$dialog.confirm({
+            title: '提示',
+            message: '按 ↑ ↓ 可调整位置!',
+            confirmButtonText: '不再提示',
+          })
+          this.$store.commit('setMgTipComptDirectionState', 1)
+          pm = Promise.resolve()
+        }
+      } catch (err) {
+        pm = Promise.reject()
       }
-      this.$dialog.confirm({
-        title: '提示',
-        message: '按 ↑ ↓ 可调整位置!',
-        confirmButtonText: '确定',
-        cancelButtonText: '不再提示',
-        confirmButtonColor: '#666',
-        cancelButtonColor: '#ee0a24'
-      }).then(() => {
-        
-      }).catch(() => {
-        this.$store.commit('setMgTipComptDirectionState', 1)
-      })
+      return pm
+    },
+    async onTipAttrState() {
+      let pm
+      try {
+        let { mgTipAttrSaveState } = this.$store.state.manager
+        if (mgTipAttrSaveState || !this.data) {
+          pm = Promise.resolve()
+        }
+        if (!pm) {
+          await this.$dialog.confirm({
+            title: '提示',
+            message: '切换问卷,请确认修改已保存?',
+            confirmButtonText: '不再提示',
+          })
+          this.$store.commit('setMgTipAttrSaveState', 1)
+          pm = Promise.resolve()
+        }
+      } catch (err) {
+        pm = Promise.reject()
+      }
+      return pm
     },
     onChangeContent (ev) {
       try {

+ 48 - 56
src/pages/Manager/mixins/index.js

@@ -1,19 +1,22 @@
 import * as api from '@/api'
 import LayoutVolume from '../../FormVolume'
-import config from '../js/config'
 export default {
   components: {
     LayoutVolume
   },
   data() {
     return {
-      TITLE: '问卷调查',
-      data: config.localData,
+      TITLE: '问卷编辑',
+      data: null,
       dataItem: null,
+      list: [],
       loading: {
         data: false,
-        empty: false,
         submit: false
+      },
+      listState: {
+        loading: false,
+        finished: true
       }
     };
   },
@@ -21,78 +24,67 @@ export default {
     this.vueWatchRoute()
   },
   methods: {
-    vueWatchRoute () {
-      // this.inQuery()
-      // this.inData()
+    vueWatchRoute() {
+      this.inData()
     },
-    inQuery () {
-      let { title } = this.$route.query
-      if (title) {
-        this.TITLE = title
+    async onLoadList() {
+      this.listState.loading = true
+      try {
+        let res = await this.inData()
+        let data = res.data
+      } catch (err) {
+        console.warn(err)
       }
+      this.listState.loading = false
     },
     async inData() {
+      let pm
       this.loading.data = true
-      this.loading.empty = false
       try {
-        let { result_id } = this.$route.query
-        let pm = result_id
-          ? api.apiGetVolumeResult({ result_id })
-          : api.apiGetVolumeInfo(this.$route.query)
-        let res = await pm
+        let res = await api.apiGetVolumeList()
         let data = res.data
-        let configType = {
-          'radio': '单选',
-          'checkbox': '多选'
-        }
-        data.result.forEach(item => {
-          item.layout_align = item.layout_align || data.topic.layout_align || 'left'
-          item.layout_inline = item.layout_inline || data.topic.layout_inline || 1
-          item.type_name = configType[item.type] || ''
-
-          // 自定义组件处理
-          Custom(item)
-        })
-        if (data.topic.memo) {
-          data.topic.memo = data.topic.memo.replace(/\n/g, '<br />')
-        }
-        if (this.layoutType) {
-          this.layoutCurrent = this.layoutType[data.topic.layout_type] || 'van-empty'
-        }
-        this.$set(this, 'data', data)
+        this.$set(this, 'list', data)
+        pm = Promise.resolve(res)
       } catch (error) {
-        this.loading.empty = true
         console.log(error)
+        pm = Promise.reject(error)
       }
       this.loading.data = false
+      return pm
+    },
+    async inAttrData(item) {
+      try {
+        await this.onTipAttrState()
+        let query = {
+          topic_id: item.id
+        }
+        let res = await api.apiGetVolumeInfo(query)
+        let data = res.data
+        this.selectData(data)
+      } catch (err) {
+        console.warn(err)
+      }
     },
     async btnSubmit() {
       this.loading.submit = true
       let ld = null
       try {
-        await this.isForm(item => {
-          this.$dialog.alert({
-            title: '必填项',
-            message: `${item.title}`,
-          })
-        })
         await this.$dialog.confirm({
           title: '提示',
-          message: '请确认无误提交?',
+          message: '确认保存吗?',
         })
-        ld = this.$toast.loading({ message: '提交中...', duration: 0 })
-        let result = this.data.result.map(item => ({ detail_id: item.detail_id, val: item.val }))
-        let query = {
-          ...this.$route.query,
-          data: JSON.stringify(result)
-        }
-        await api.apiPostVolumeInfo(query)
-        this.btnOpenSuccess(() => {
-          let { go, replace } = this.$route.query
-          replace
-            ? this.$router.replace({ name: replace })
-            : this.$router.go(go || -1)
+        ld = this.$toast.loading({ message: '保存中...', duration: 0 })
+        let configKey = ['detail_id', 'content', 'layout_align', 'layout_inline', 'required', 'title', 'type', 'val']
+        let configTypeArr = ['checkbox']
+        let obj = JSON.parse(JSON.stringify(this.data))
+        obj.result = obj.result.map(item => {
+          let rst = {}
+          configKey.forEach(k => rst[k] = item[k])
+          rst.val = configTypeArr.indexOf(item.type) !== -1 ? [] : ''
+          return rst
         })
+        let res = await api.apiPostVolumeResult(obj)
+        this.$toast.success(res.message)
       } catch (error) {
         console.log(error)
       }

+ 11 - 0
src/pages/Manager/style.scss

@@ -30,6 +30,17 @@
     flex-direction: column;
     height: 100%;
   }
+
+  .panel-btns {
+    display: flex;
+    justify-content: space-between;
+    margin: 0 -$sp-sm;
+    padding: $sp-sm 0;
+    .btn-item {
+      margin: 0 $sp-sm;
+      flex: 1;
+    }
+  }
 }
 
 

+ 13 - 2
src/store/modules/manager.js

@@ -4,6 +4,7 @@ const lStorage = new Storage('localStorage')
 const user = {
   state: {
     mgTipComptDirectionState: lStorage.getItem('mgTipComptDirectionState'),
+    mgTipAttrSaveState: lStorage.getItem('mgTipAttrSaveState')
   },
 
   getters: {
@@ -11,15 +12,25 @@ const user = {
   },
 
   mutations: {
-    // 设置 signInfo
+    // 设置 tip
     setMgTipComptDirectionState(state, data) {
       state.mgTipComptDirectionState = data
       lStorage.setItem('mgTipComptDirectionState', data)
     },
-    // 清除 signInfo
+    // 清除 tip
     clearMgTipComptDirectionState(state) {
       state.mgTipComptDirectionState = null
       lStorage.removeItem('mgTipComptDirectionState')
+    },
+    // 设置 tip
+    setMgTipAttrSaveState(state, data) {
+      state.mgTipAttrSaveState = data
+      lStorage.setItem('mgTipAttrSaveState', data)
+    },
+    // 清除 tip
+    clearMgTipAttrSaveState(state) {
+      state.mgTipAttrSaveState = null
+      lStorage.removeItem('mgTipAttrSaveState')
     }
   }
 }