浏览代码

认证通1.6版本界面调整修改

喻文俊 3 年之前
父节点
当前提交
8020777c65
共有 34 个文件被更改,包括 2585 次插入387 次删除
  1. 2 0
      .env.development
  2. 2 0
      .env.production
  3. 17 2
      .env.staging
  4. 2 3
      package.json
  5. 0 31
      src/components/MarkdownEditor/default-options.js
  6. 0 118
      src/components/MarkdownEditor/index.vue
  7. 2 2
      src/router/index.js
  8. 73 30
      src/router/module/audit.js
  9. 15 15
      src/router/module/club.js
  10. 0 38
      src/router/module/doctor.js
  11. 13 7
      src/router/module/material.js
  12. 64 0
      src/router/module/personnel.js
  13. 4 0
      src/views/admin/audit/club/review.vue
  14. 13 0
      src/views/admin/audit/device/detail.vue
  15. 4 4
      src/views/admin/audit/device/index.vue
  16. 13 0
      src/views/admin/audit/device/list.vue
  17. 8 0
      src/views/admin/audit/material/index.vue
  18. 263 0
      src/views/admin/audit/material/package/index.vue
  19. 154 0
      src/views/admin/audit/personnel/index.vue
  20. 0 0
      src/views/admin/audit/personnel/operate/index.vue
  21. 0 0
      src/views/admin/audit/personnel/operate/review.vue
  22. 185 0
      src/views/admin/audit/personnel/training/index.vue
  23. 197 0
      src/views/admin/audit/personnel/training/review.vue
  24. 38 0
      src/views/components/CustomItems/index.vue
  25. 101 0
      src/views/components/DoctorCustomItems/index.vue
  26. 196 0
      src/views/normal/club/device-cate.vue
  27. 67 41
      src/views/normal/club/edit.vue
  28. 205 0
      src/views/normal/club/user/index-temp.vue
  29. 20 82
      src/views/normal/club/user/index.vue
  30. 415 0
      src/views/normal/material/package/index.vue
  31. 278 0
      src/views/normal/personnel/operate/edit.vue
  32. 3 3
      src/views/normal/personnel/operate/index.vue
  33. 33 11
      src/views/normal/personnel/training/edit.vue
  34. 198 0
      src/views/normal/personnel/training/index.vue

+ 2 - 0
.env.development

@@ -1,3 +1,5 @@
+NODE_ENV = development
+
 # just a flag
 ENV = 'development'
 

+ 2 - 0
.env.production

@@ -1,3 +1,5 @@
+NODE_ENV = production
+
 # just a flag
 ENV = 'production'
 

+ 17 - 2
.env.staging

@@ -3,6 +3,21 @@ NODE_ENV = production
 # just a flag
 ENV = 'staging'
 
-# base api
-VUE_APP_BASE_API = '/stage-api'
+# 正式地址 API接口
+VUE_APP_BASE_API = 'https://zplma-b.caimei365.com'
+
+# 文件上传 API接口地址
+VUE_APP_UPLOAD_API='https://zplma-b.caimei365.com'
+
+#二维码生成链接location
+VUE_APP_BASE_SERVER = 'https://www-b.caimei365.com'
+
+# 消息接口地址 WebSocket
+VUE_APP_SOCKET_SERVER = 'wss://zplma-b.caimei365.com/websocket?sessionSource=zplm_admin'
+
+# 支付
+VUE_APP_PAY_LOCAL = https://core-b.caimei365.com
+
+# 网站地址
+VUE_APP_LOCAL = 'https://zplm-b.caimei365.com'
 

+ 2 - 3
package.json

@@ -7,7 +7,7 @@
     "dev": "vue-cli-service serve",
     "lint": "eslint --ext .js,.vue src",
     "build:prod": "vue-cli-service build",
-    "build:dev": "vue-cli-service build --mode development",
+    "build:stage": "vue-cli-service build --mode staging",
     "preview": "node build/index.js --preview",
     "new": "plop",
     "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml",
@@ -18,7 +18,7 @@
     "axios": "^0.21.1",
     "clipboard": "2.0.4",
     "codemirror": "5.45.0",
-    "core-js": "3.6.5",
+    "core-js": "^3.21.1",
     "driver.js": "0.9.5",
     "dropzone": "5.5.1",
     "echarts": "4.2.1",
@@ -36,7 +36,6 @@
     "screenfull": "4.2.0",
     "script-loader": "0.7.2",
     "sortablejs": "1.8.4",
-    "tui-editor": "1.3.3",
     "vue": "2.6.10",
     "vue-count-to": "1.0.13",
     "vue-router": "3.0.2",

+ 0 - 31
src/components/MarkdownEditor/default-options.js

@@ -1,31 +0,0 @@
-// doc: https://nhnent.github.io/tui.editor/api/latest/ToastUIEditor.html#ToastUIEditor
-export default {
-  minHeight: '200px',
-  previewStyle: 'vertical',
-  useCommandShortcut: true,
-  useDefaultHTMLSanitizer: true,
-  usageStatistics: false,
-  hideModeSwitch: false,
-  toolbarItems: [
-    'heading',
-    'bold',
-    'italic',
-    'strike',
-    'divider',
-    'hr',
-    'quote',
-    'divider',
-    'ul',
-    'ol',
-    'task',
-    'indent',
-    'outdent',
-    'divider',
-    'table',
-    'image',
-    'link',
-    'divider',
-    'code',
-    'codeblock'
-  ]
-}

+ 0 - 118
src/components/MarkdownEditor/index.vue

@@ -1,118 +0,0 @@
-<template>
-  <div :id="id" />
-</template>
-
-<script>
-// deps for editor
-import 'codemirror/lib/codemirror.css' // codemirror
-import 'tui-editor/dist/tui-editor.css' // editor ui
-import 'tui-editor/dist/tui-editor-contents.css' // editor content
-
-import Editor from 'tui-editor'
-import defaultOptions from './default-options'
-
-export default {
-  name: 'MarkdownEditor',
-  props: {
-    value: {
-      type: String,
-      default: ''
-    },
-    id: {
-      type: String,
-      required: false,
-      default() {
-        return 'markdown-editor-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '')
-      }
-    },
-    options: {
-      type: Object,
-      default() {
-        return defaultOptions
-      }
-    },
-    mode: {
-      type: String,
-      default: 'markdown'
-    },
-    height: {
-      type: String,
-      required: false,
-      default: '300px'
-    },
-    language: {
-      type: String,
-      required: false,
-      default: 'en_US' // https://github.com/nhnent/tui.editor/tree/master/src/js/langs
-    }
-  },
-  data() {
-    return {
-      editor: null
-    }
-  },
-  computed: {
-    editorOptions() {
-      const options = Object.assign({}, defaultOptions, this.options)
-      options.initialEditType = this.mode
-      options.height = this.height
-      options.language = this.language
-      return options
-    }
-  },
-  watch: {
-    value(newValue, preValue) {
-      if (newValue !== preValue && newValue !== this.editor.getValue()) {
-        this.editor.setValue(newValue)
-      }
-    },
-    language(val) {
-      this.destroyEditor()
-      this.initEditor()
-    },
-    height(newValue) {
-      this.editor.height(newValue)
-    },
-    mode(newValue) {
-      this.editor.changeMode(newValue)
-    }
-  },
-  mounted() {
-    this.initEditor()
-  },
-  destroyed() {
-    this.destroyEditor()
-  },
-  methods: {
-    initEditor() {
-      this.editor = new Editor({
-        el: document.getElementById(this.id),
-        ...this.editorOptions
-      })
-      if (this.value) {
-        this.editor.setValue(this.value)
-      }
-      this.editor.on('change', () => {
-        this.$emit('input', this.editor.getValue())
-      })
-    },
-    destroyEditor() {
-      if (!this.editor) return
-      this.editor.off('change')
-      this.editor.remove()
-    },
-    setValue(value) {
-      this.editor.setValue(value)
-    },
-    getValue() {
-      return this.editor.getValue()
-    },
-    setHtml(value) {
-      this.editor.setHtml(value)
-    },
-    getHtml() {
-      return this.editor.getHtml()
-    }
-  }
-}
-</script>

+ 2 - 2
src/router/index.js

@@ -7,7 +7,7 @@ Vue.use(Router)
 // 引入路由
 import supplierRoutes from './module/supplier'
 import clubRoutes from './module/club'
-import doctorRoutes from './module/doctor'
+import personnelRoutes from './module/personnel'
 import feedbackRoutes from './module/feedback'
 import helperRoutes from './module/helper'
 import materialRoutes from './module/material'
@@ -24,7 +24,7 @@ export const asyncRoutes = [
   ...AuditRoutes,
   ...MemberRoutes,
   ...clubRoutes,
-  ...doctorRoutes,
+  ...personnelRoutes,
   ...materialRoutes,
   ...feedbackRoutes,
   ...settingRoutes,

+ 73 - 30
src/router/module/audit.js

@@ -7,59 +7,73 @@ const clubAuditRoutes = [
     path: 'club',
     component: () => import('@/views/admin/audit/club'),
     name: 'AuditClub',
-    meta: { title: '机构认证', roles: ['admin'], noCache: true, id: 'audit1' }
+    meta: { title: '机构认证', noCache: true }
   },
   {
-    path: 'club-list',
+    path: 'club/club-list',
     hidden: true,
     component: () => import('@/views/admin/audit/club/list'),
     name: 'AuditClubList',
-    meta: { title: '机构认证审核', roles: ['admin'], noCache: true, id: 'audit2' }
+    meta: { title: '机构认证审核', noCache: true }
   },
   {
-    path: 'club-detail',
+    path: 'club/club-detail',
     hidden: true,
     component: () => import('@/views/admin/audit/club/review'),
     name: 'AuditClubDetail',
-    meta: { title: '机构认证审核详情', roles: ['admin'], noCache: true, id: 'audit3' }
+    meta: { title: '机构认证审核详情', noCache: true }
   },
   {
-    path: 'club-device-list',
+    path: 'club/device/list',
     hidden: true,
     component: () => import('@/views/admin/audit/club/device'),
     name: 'AuditClubDeviceList',
-    meta: { title: '设备审核列表', roles: ['admin'], noCache: true, id: 'audit4' }
+    meta: { title: '设备审核列表', noCache: true }
   },
   {
-    path: 'club-device-detail',
+    path: 'club/device-detail',
     hidden: true,
     component: () => import('@/views/admin/audit/club/device/review'),
     name: 'AuditClubDeviceDetail',
-    meta: { title: '设备审核详情', roles: ['admin'], noCache: true, id: 'audit5' }
+    meta: { title: '设备审核详情', noCache: true }
   }
 ]
 
 // 医师资料审核
-const doctorAuditRoutes = [
+const personnelAuditRoutes = [
   {
-    path: 'doctor',
-    component: () => import('@/views/admin/audit/doctor'),
-    name: 'AuditDoctor',
-    meta: { title: '医师认证', roles: ['admin'], noCache: true, id: 'audit6' }
+    path: 'personnel',
+    component: () => import('@/views/admin/audit/personnel'),
+    name: 'AuditPersonnel',
+    meta: { title: '人员认证', noCache: true }
   },
   {
-    path: 'doctor-list',
+    path: 'personnel/operate/doctor-list',
     hidden: true,
-    component: () => import('@/views/admin/audit/doctor/list'),
-    name: 'AuditDoctorList',
-    meta: { title: '医师审核列表', roles: ['admin'], noCache: true, id: 'audit7' }
+    component: () => import('@/views/admin/audit/personnel/operate'),
+    name: 'AuditOperateDoctorList',
+    meta: { title: '操作医师审核列表', noCache: true }
   },
   {
-    path: 'doctor-detail',
+    path: 'personnel/operate/doctor-detail',
     hidden: true,
-    component: () => import('@/views/admin/audit/doctor/review'),
-    name: 'AuditDoctorDetail',
-    meta: { title: '医师审核详情', roles: ['admin'], noCache: true, id: 'audit8' }
+    component: () => import('@/views/admin/audit/personnel/operate/review'),
+    name: 'AuditOperateDoctorDetail',
+    meta: { title: '操作医师审核详情', noCache: true }
+  },
+  {
+    path: 'personnel/training/doctor-list',
+    hidden: true,
+    component: () => import('@/views/admin/audit/personnel/training'),
+    name: 'AuditTrainingDoctorList',
+    meta: { title: '培训医师审核列表', noCache: true }
+  },
+  {
+    path: 'personnel/training/doctor-detail',
+    hidden: true,
+    component: () => import('@/views/admin/audit/personnel/training/review'),
+    name: 'AuditTrainingDoctorDetail',
+    meta: { title: '培训医师审核详情', noCache: true }
   }
 ]
 
@@ -69,49 +83,78 @@ const materialAuditRoutes = [
     path: 'material',
     component: () => import('@/views/admin/audit/material'),
     name: 'AuditMaterial',
-    meta: { title: '资料库', roles: ['admin'], noCache: true, id: 'audit9' }
+    meta: { title: '资料库', noCache: true }
   },
   {
     path: 'material/article-list',
     component: () => import('@/views/admin/audit/material/article'),
     name: 'AuditMaterialArticleList',
     hidden: true,
-    meta: { title: '文章审核列表', roles: ['admin'], noCache: true, id: 'audit10' }
+    meta: { title: '文章审核列表', noCache: true }
   },
   {
     path: 'material/article-detail',
     component: () => import('@/views/admin/audit/material/article/review'),
     name: 'AuditMaterialArticleDetail',
     hidden: true,
-    meta: { title: '审核文章', roles: ['admin'], noCache: true, id: 'audit11' }
+    meta: { title: '审核文章', noCache: true }
   },
   {
     path: 'material/image-list',
     component: () => import('@/views/admin/audit/material/image'),
     name: 'AuditMaterialImageList',
     hidden: true,
-    meta: { title: '图片审核列表', roles: ['admin'], noCache: true, id: 'audit12' }
+    meta: { title: '图片审核列表', noCache: true }
   },
   {
     path: 'material/image-detail',
     component: () => import('@/views/admin/audit/material/image/review'),
     name: 'AuditMaterialImageDetail',
     hidden: true,
-    meta: { title: '审核图片', roles: ['admin'], noCache: true, id: 'audit13' }
+    meta: { title: '审核图片', noCache: true }
   },
   {
     path: 'material/video-list',
     component: () => import('@/views/admin/audit/material/video'),
     name: 'AuditMaterialVideoList',
     hidden: true,
-    meta: { title: '视频审核列表', roles: ['admin'], noCache: true, id: 'audit14' }
+    meta: { title: '视频审核列表', noCache: true }
   },
   {
     path: 'material/file-list',
     component: () => import('@/views/admin/audit/material/file'),
     name: 'AuditMaterialFileList',
     hidden: true,
-    meta: { title: '文件审核列表', roles: ['admin'], noCache: true, id: 'audit15' }
+    meta: { title: '文件审核列表', noCache: true }
+  },
+  {
+    path: 'material/package-list',
+    component: () => import('@/views/admin/audit/material/package'),
+    name: 'AuditMaterialPackageList',
+    hidden: true,
+    meta: { title: '资料包审核列表', noCache: true }
+  }
+]
+
+// 设备管理审核
+const deviceAuditRoutes = [
+  {
+    path: 'device/club-list',
+    component: () => import('@/views/admin/audit/device'),
+    name: 'AuditDevice',
+    meta: { title: '设备管理', noCache: true }
+  },
+  {
+    path: 'device/device-list',
+    component: () => import('@/views/admin/audit/device/list'),
+    name: 'AuditDeviceCateList',
+    meta: { title: '设备管理审核', noCache: true }
+  },
+  {
+    path: 'device/device-detail',
+    component: () => import('@/views/admin/audit/device/detail'),
+    name: 'AuditDeviceCateDetail',
+    meta: { title: '设备管理审核详情', noCache: true }
   }
 ]
 
@@ -123,7 +166,7 @@ const AuditRoutes = [
     alwaysShow: true,
     name: 'Audit',
     meta: { title: '审核管理', icon: 'el-icon-s-check', roles: ['admin'], noCache: true },
-    children: [...clubAuditRoutes, ...doctorAuditRoutes, ...materialAuditRoutes]
+    children: [...clubAuditRoutes, ...personnelAuditRoutes, ...materialAuditRoutes, ...deviceAuditRoutes]
   }
 ]
 

+ 15 - 15
src/router/module/club.js

@@ -8,35 +8,28 @@ const clubRoutes = [
     alwaysShow: true,
     redirect: '/club/list',
     name: 'Club',
-    meta: { title: '机构认证', icon: 'icon-jigourenzheng iconfont', roles: ['normal'], noCache: true, proxy: true },
+    meta: { title: '机构认证', icon: 'icon-jigourenzheng iconfont', noCache: true },
     children: [
       // 机构
       {
         path: 'list',
         component: () => import('@/views/normal/club'),
         name: 'ClubList',
-        meta: {
-          title: '机构列表',
-          icon: 'icon-jigouliebiao iconfont',
-          roles: ['normal'],
-          affix: true,
-          noCache: true,
-          proxy: true
-        }
+        meta: { title: '机构列表', icon: 'icon-jigouliebiao iconfont', affix: true, noCache: true }
       },
       {
         path: 'club-add',
         component: () => import('@/views/normal/club/edit'),
         name: 'ClubAdd',
         hidden: true,
-        meta: { title: '添加机构', roles: ['normal'], noCache: true, proxy: true }
+        meta: { title: '添加机构', noCache: true }
       },
       {
         path: 'club-edit',
         component: () => import('@/views/normal/club/edit'),
         name: 'ClubEdit',
         hidden: true,
-        meta: { title: '修改机构', roles: ['normal'], noCache: true, proxy: true }
+        meta: { title: '修改机构', noCache: true }
       },
       // 用户
       {
@@ -44,7 +37,7 @@ const clubRoutes = [
         component: () => import('@/views/normal/club/user'),
         name: 'ClubUserList',
         hidden: true,
-        meta: { title: '登录用户', roles: ['normal'], noCache: true, proxy: true }
+        meta: { title: '登录用户', noCache: true }
       },
       // 设备
       {
@@ -52,21 +45,28 @@ const clubRoutes = [
         path: 'device-list',
         component: () => import('@/views/normal/club/device'),
         name: 'ClubDeviceList',
-        meta: { title: '设备列表', roles: ['normal'], noCache: true, id: 24 }
+        meta: { title: '设备列表', noCache: true, id: 24 }
       },
       {
         hidden: true,
         path: 'device-add',
         component: () => import('@/views/normal/club/device/edit'),
         name: 'ClubDeviceAdd',
-        meta: { title: '添加设备', roles: ['normal'], noCache: true, proxy: true, id: 25 }
+        meta: { title: '添加设备', noCache: true, id: 25 }
       },
       {
         hidden: true,
         path: 'device-edit',
         component: () => import('@/views/normal/club/device/edit'),
         name: 'ClubDeviceEdit',
-        meta: { title: '修改设备', roles: ['normal'], noCache: true, proxy: true, id: 26 }
+        meta: { title: '修改设备', noCache: true, id: 26 }
+      },
+      {
+        hidden: true,
+        path: 'device-cate',
+        component: () => import('@/views/normal/club/device-cate'),
+        name: 'ClubDeviceCate',
+        meta: { title: '设备管理', noCache: true, id: 26 }
       }
     ]
   }

+ 0 - 38
src/router/module/doctor.js

@@ -1,38 +0,0 @@
-// 医师认证路由
-
-import Layout from '@/layout'
-// 授权管理页面
-const doctorRoutes = [
-  {
-    path: '/doctor',
-    component: Layout,
-    alwaysShow: true,
-    redirect: '/doctor/list',
-    name: 'Doctor',
-    meta: { title: '医师认证', icon: 'icon-yishirenzheng iconfont', roles: ['normal'], noCache: true, proxy: true },
-    children: [
-      {
-        path: 'list',
-        component: () => import('@/views/normal/doctor'),
-        name: 'DoctorList',
-        meta: { title: '医师列表', icon: 'icon-yishiliebiao iconfont', roles: ['normal'], noCache: true, proxy: true }
-      },
-      {
-        hidden: true,
-        path: 'add',
-        component: () => import('@/views/normal/doctor/edit'),
-        name: 'DoctorAdd',
-        meta: { title: '添加医师', roles: ['normal'], noCache: true, proxy: true }
-      },
-      {
-        hidden: true,
-        path: 'edit',
-        component: () => import('@/views/normal/doctor/edit'),
-        name: 'DoctorEdit',
-        meta: { title: '修改医师资料', roles: ['normal'], noCache: true, proxy: true }
-      }
-    ]
-  }
-]
-
-export default doctorRoutes

+ 13 - 7
src/router/module/material.js

@@ -9,45 +9,51 @@ const materialRoutes = [
     alwaysShow: true,
     redirect: '/material/article-list',
     name: 'Material',
-    meta: { title: '资料库', icon: 'icon-ziliaoku iconfont', roles: ['normal'], noCache: true, proxy: true },
+    meta: { title: '资料库', icon: 'icon-ziliaoku iconfont', noCache: true },
     children: [
       {
         path: 'article-list',
         component: () => import('@/views/normal/material/article'),
         name: 'MaterialArticleList',
-        meta: { title: '文章列表', icon: 'icon-wenjianliebiao iconfont', roles: ['normal'], noCache: true, proxy: true }
+        meta: { title: '文章列表', icon: 'icon-wenjianliebiao iconfont', noCache: true }
       },
       {
         path: 'article-edit',
         component: () => import('@/views/normal/material/article/edit'),
         hidden: true,
         name: 'MaterialArticleEdit',
-        meta: { title: '编辑文章', icon: 'el-icon-menu', roles: ['normal'], noCache: true, proxy: true }
+        meta: { title: '编辑文章', icon: 'el-icon-menu', noCache: true }
       },
       {
         path: 'image-list',
         component: () => import('@/views/normal/material/image'),
         name: 'MaterialImageList',
-        meta: { title: '图片列表', icon: 'el-icon-picture-outline', roles: ['normal'], noCache: true, proxy: true }
+        meta: { title: '图片列表', icon: 'el-icon-picture-outline', noCache: true }
       },
       {
         path: 'image-edit',
         component: () => import('@/views/normal/material/image/edit'),
         hidden: true,
         name: 'MaterialImageEdit',
-        meta: { title: '编辑图片', icon: 'el-icon-menu', roles: ['normal'], noCache: true, proxy: true }
+        meta: { title: '编辑图片', icon: 'el-icon-menu', noCache: true }
       },
       {
         path: 'video-list',
         component: () => import('@/views/normal/material/video'),
         name: 'MaterialVideoList',
-        meta: { title: '视频列表', icon: 'el-icon-video-camera', roles: ['normal'], noCache: true, proxy: true }
+        meta: { title: '视频列表', icon: 'el-icon-video-camera', noCache: true }
       },
       {
         path: 'file-list',
         component: () => import('@/views/normal/material/file'),
         name: 'MaterialFileList',
-        meta: { title: '文件列表', icon: 'el-icon-receiving', roles: ['normal'], noCache: true, proxy: true }
+        meta: { title: '文件列表', icon: 'el-icon-receiving', noCache: true }
+      },
+      {
+        path: 'package-list',
+        component: () => import('@/views/normal/material/package'),
+        name: 'MaterialPackageList',
+        meta: { title: '资料包列表', icon: 'el-icon-receiving', noCache: true }
       }
     ]
   }

+ 64 - 0
src/router/module/personnel.js

@@ -0,0 +1,64 @@
+// 医师认证路由
+
+import Layout from '@/layout'
+
+// 操作医师
+const operate = [
+  {
+    path: 'operate/list',
+    component: () => import('@/views/normal/personnel/operate'),
+    name: 'OperateDoctorList',
+    meta: { title: '操作医师', noCache: true }
+  },
+  {
+    hidden: true,
+    path: 'operate/add',
+    component: () => import('@/views/normal/personnel/operate/edit'),
+    name: 'OperateDoctorAdd',
+    meta: { title: '添加操作医师', noCache: true }
+  },
+  {
+    hidden: true,
+    path: 'operate/edit',
+    component: () => import('@/views/normal/personnel/operate/edit'),
+    name: 'OperateDoctorEdit',
+    meta: { title: '修改操作医师资料', noCache: true }
+  }
+]
+
+// 培训医师
+const training = [
+  {
+    path: 'training/list',
+    component: () => import('@/views/normal/personnel/training'),
+    name: 'TrainingDoctorList',
+    meta: { title: '培训医师', noCache: true }
+  },
+  {
+    hidden: true,
+    path: 'training/add',
+    component: () => import('@/views/normal/personnel/training/edit'),
+    name: 'TrainingDoctorAdd',
+    meta: { title: '添加培训医师', noCache: true }
+  },
+  {
+    hidden: true,
+    path: 'training/edit',
+    component: () => import('@/views/normal/personnel/training/edit'),
+    name: 'TrainingDoctorEdit',
+    meta: { title: '修改培训医师资料', noCache: true }
+  }
+]
+
+const personnelRoutes = [
+  {
+    path: '/personnel',
+    component: Layout,
+    alwaysShow: true,
+    name: 'Personnel',
+    meta: { title: '人员认证', icon: 'el-icon-s-check', noCache: true },
+    children: [...operate, ...training]
+  }
+]
+
+export default personnelRoutes

+ 4 - 0
src/views/admin/audit/club/review.vue

@@ -32,6 +32,10 @@
         />
       </el-form-item>
 
+      <el-form-item label="是否需要自定义属性">
+        <span>是</span>
+      </el-form-item>
+
       <!-- 审核表单 -->
       <el-form-item label="审核状态:">
         <el-radio-group v-model="formData.auditStatus">

+ 13 - 0
src/views/admin/audit/device/detail.vue

@@ -0,0 +1,13 @@
+<template>
+  <div />
+</template>
+
+<script>
+export default {
+
+}
+</script>
+
+<style scoped>
+
+</style>

+ 4 - 4
src/views/admin/audit/doctor/index.vue → src/views/admin/audit/device/index.vue

@@ -64,13 +64,13 @@
       </el-table-column>
       <el-table-column label="操作" width="240px" align="center">
         <template slot-scope="{ row }">
-          <el-badge :hidden="row.lowerAuditStatus === 1" :value="row.doctorWaitNum" :max="99">
+          <el-badge :hidden="row.lowerAuditStatus === 1" :value="row.waitAuditNum" :max="99">
             <el-button
               type="primary"
               icon="el-icon-s-check"
               size="mini"
-              @click="$_navigationTo(`doctor-list?authUserId=${row.authUserId}`)"
-            >医师认证审核</el-button>
+              @click="$_navigationTo(`/audit/device/device-list?authUserId=${row.authUserId}`)"
+            >设备分类审核</el-button>
           </el-badge>
         </template>
       </el-table-column>
@@ -95,7 +95,7 @@ export default {
   data() {
     return {
       listQuery: {
-        listType: 4,
+        listType: 2,
         brandId: '', // 品牌id
         linkMan: '', // 联系人
         mobile: '', // 手机号

+ 13 - 0
src/views/admin/audit/device/list.vue

@@ -0,0 +1,13 @@
+<template>
+  <div />
+</template>
+
+<script>
+export default {
+
+}
+</script>
+
+<style scoped>
+
+</style>

+ 8 - 0
src/views/admin/audit/material/index.vue

@@ -96,6 +96,14 @@
               @click="$_navigationTo(`material/file-list?authUserId=${row.authUserId}`)"
             >文件</el-button>
           </el-badge>
+          <el-badge :hidden="row.fileWaitNum === 0" :value="row.fileWaitNum" :max="99">
+            <el-button
+              type="primary"
+              icon="el-icon-document-copy"
+              size="mini"
+              @click="$_navigationTo(`material/package-list?authUserId=${row.authUserId}`)"
+            >资料包</el-button>
+          </el-badge>
         </template>
       </el-table-column>
     </el-table>

+ 263 - 0
src/views/admin/audit/material/package/index.vue

@@ -0,0 +1,263 @@
+<template>
+  <div class="app-container review-box">
+    <!-- 搜索区域 -->
+    <div class="filter-container">
+      <div class="filter-control">
+        <span>文件名称:</span>
+        <el-input
+          v-model="listQuery.fileTitle"
+          placeholder="文件名称"
+          @keyup.enter.native="getList"
+        />
+      </div>
+      <div class="filter-control">
+        <span>审核状态:</span>
+        <el-select
+          v-model="listQuery.auditStatus"
+          placeholder="审核状态"
+          clearable
+          @change="getList"
+        >
+          <el-option label="全部" value="" />
+          <el-option label="待审核" :value="2" />
+          <el-option label="审核通过" :value="1" />
+          <el-option label="审核未通过" :value="0" />
+        </el-select>
+      </div>
+      <div class="filter-control">
+        <el-button type="primary" @click="getList">查询</el-button>
+      </div>
+    </div>
+    <!-- 搜索区域END -->
+    <!-- 表格区域 -->
+    <el-table
+      v-loading="listLoading"
+      :data="list"
+      style="width: 100%"
+      border
+      fit
+      highlight-current-row
+      header-row-class-name="tableHeader"
+      cell-class-name="table-cell"
+    >
+      <el-table-column label="序号" :index="indexMethod" type="index" width="80" align="center" />
+      <el-table-column label="文件名称" prop="fileTitle" align="center" />
+      <el-table-column label="审核状态" width="150px" align="center">
+        <template slot-scope="{ row }">
+          <el-tag v-if="row.auditStatus === 2" size="small" type="warning">待审核</el-tag>
+          <el-tag v-if="row.auditStatus === 1" size="small" type="success">审核通过</el-tag>
+          <!-- 未通过原因展示 -->
+          <template v-if="row.auditStatus === 0">
+            <!-- <span class="status danger">审核未通过&nbsp;</span> -->
+            <el-popover
+              placement="top-start"
+              title="审核说明"
+              width="400"
+              trigger="hover"
+              :content="row.invalidReason"
+            >
+              <el-tag slot="reference" size="small" type="danger" class="reason">
+                <span>审核未通过</span>
+                <span class="el-icon-question status danger " />
+              </el-tag>
+            </el-popover>
+            <!-- 未通过原因展示END -->
+          </template>
+        </template>
+      </el-table-column>
+      <el-table-column label="审核时间" width="250px" align="center">
+        <template slot-scope="{row}">
+          <span v-if="row.auditStatus!==2">{{ row.auditTime | formatTime }}</span>
+          <span v-else>—</span>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="审核人" align="center" width="200px">
+        <template slot-scope="{row}">
+          <span v-if="row.auditStatus!==2">{{ row.auditBy }}</span>
+          <span v-else>—</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" width="150px" align="center">
+        <template slot-scope="{row}">
+          <el-button
+            v-if="row.auditStatus===2"
+            type="primary"
+            size="mini"
+            style="margin-right:5px"
+            icon="el-icon-s-check"
+            @click="handleShowDialog(row)"
+          >审核</el-button>
+          <span v-else class="status success el-icon-check">&nbsp;已审核</span>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 表格区域END -->
+    <!-- 页码 -->
+    <pagination v-show="total > 0" :total="total" :page.sync="listQuery.pageNum" :limit.sync="listQuery.pageSize" @pagination="getList(listQuery)" />
+    <!-- 视频预览对话框 -->
+    <el-dialog
+      title="视频审核"
+      :visible.sync="dialogVisible"
+      width="40%"
+      @closed="dialogColosed"
+    >
+      <el-form ref="formRef" :model="dialogData" label-width="65px" :rules="dialogFormRules">
+        <el-form-item label="标题:">
+          {{ current.fileTitle }}
+        </el-form-item>
+        <el-form-item label="文件:">
+          <img src="@/assets/img/pdf_cover.png" alt="文件封面" width="60" height="60">
+          <span class="preview" @click.prevent="handlePreviewFile">预览</span>
+        </el-form-item>
+        <el-form-item label="审核:">
+          <el-radio-group v-model="dialogData.auditStatus">
+            <el-radio :label="1">通过</el-radio>
+            <el-radio :label="0">不通过</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item v-if="dialogData.auditStatus === 0" label="原因:" prop="invalidReason">
+          <el-input v-model="dialogData.invalidReason" type="textarea" placeholder="请说明原因" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="dialogVisible = false">取 消</el-button>
+        <el-button type="primary" @click="handleAuditStatus">提 交</el-button>
+      </div>
+    </el-dialog>
+    <!-- 视频预览对话框END -->
+  </div>
+</template>
+
+<script>
+import Pagination from '@/components/Pagination'
+import openWindow from '@/utils/open-window'
+import { formatDate } from '@/utils'
+import { getFileList } from '@/api/doc'
+import { auditfile } from '@/api/docReview'
+import { mapGetters } from 'vuex'
+export default {
+  components: { Pagination },
+  filters: {
+    formatTime(time) {
+      if (!time) {
+        return ''
+      }
+      return formatDate(time, 'yyyy-MM-DD HH:mm:ss')
+    }
+  },
+  data() {
+    return {
+      dialogVisible: false, // 文件审核dialog是否显示
+      listLoading: false, // 列表加载
+      total: 0, // 列表数据条数
+      listQuery: {
+        authUserId: '', // 机构id
+        auditStatus: '', // 审核状态
+        fileTitle: '', // 文件标题
+        listType: 2, // 列表类型
+        pageNum: 1, // 页码
+        pageSize: 10, // 分页大小
+        status: '' // 文章状态:0已下线,1已上线,2待上线
+      },
+      list: [],
+      // 当前审核选中的文件数据,需要传给预览窗口的数据
+      current: {
+        currentfileUrl: '',
+        fileTitle: ''
+      },
+      // 审核提交数据
+      dialogData: {
+        fileId: '', // 文章id
+        auditStatus: 1, // 审核状态
+        invalidReason: '', // 不同过原因
+        auditBy: '' // 审核人
+      },
+      dialogFormRules: {
+        invalidReason: { required: true, message: '不通过原因不能为空', tigger: 'blur' }
+      }
+    }
+  },
+  computed: {
+    ...mapGetters(['authUserId'])
+  },
+  created() {
+    this.listQuery.authUserId = this.$route.query.authUserId
+    this.getList()
+  },
+  methods: {
+    // 获取列表数据
+    getList() {
+      this.listLoading = true
+      getFileList(this.listQuery)
+        .then(res => {
+          if (res.code !== 0) return
+          this.list = res.data.list
+          this.total = res.data.total
+          console.log(res)
+        })
+        .finally(() => {
+          this.listLoading = false
+        })
+    },
+    // 显示审核dialog对话框
+    handleShowDialog(row) {
+      console.log('------')
+      console.log(row)
+      this.current.currentfileUrl = row.filePreviewUrl
+      this.current.fileTitle = row.fileTitle
+      this.dialogData.fileId = row.fileId
+      this.dialogVisible = true
+    },
+    // dialog关闭后的回调
+    dialogColosed() {
+      this.currentfileUrl = ''
+      this.current.currentfileUrl = ''
+      this.current.fileTitle = ''
+      this.dialogData.auditStatus = 1
+      this.dialogData.fileId = ''
+    },
+    // 审核操作
+    handleAuditStatus() {
+      this.$refs.formRef.validate(valid => {
+        if (valid) {
+          this.dialogData.auditBy = this.authUserId
+          auditfile(this.dialogData)
+            .then(res => {
+              console.log(res)
+              if (res.code !== 0) return
+              this.$message({
+                message: res.data,
+                type: 'success',
+                duration: 1000
+              })
+              this.dialogVisible = false
+              this.getList()
+            })
+        }
+      })
+    },
+    // 小窗口浏览pdf文件
+    handlePreviewFile() {
+      openWindow(this.current.currentfileUrl, this.current.fileTitle, this.current.fileTitle, 1200, 600)
+    },
+    indexMethod(index) {
+      return index + this.listQuery.pageSize * (this.listQuery.pageNum - 1) + 1
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.el-table .cell {
+  overflow: visible;
+}
+.el-badge{
+  margin: 0 6px;
+}
+.preview{
+  cursor: pointer;
+  margin-left: 5px;
+  color: #0eaae7;
+}
+</style>

+ 154 - 0
src/views/admin/audit/personnel/index.vue

@@ -0,0 +1,154 @@
+<template>
+  <div class="app-container">
+    <!-- 搜索区域 -->
+    <div class="filter-container">
+      <div class="filter-control">
+        <span>供应商名称:</span>
+        <el-input v-model="listQuery.shopName" placeholder="供应商名称" @keyup.enter.native="getList" />
+      </div>
+      <div class="filter-control">
+        <span>供应商类型:</span>
+        <el-select v-model="listQuery.shopType" placeholder="供应商类型" clearable @change="getList">
+          <el-option label="所有类型" value="" />
+          <el-option label="代理商" :value="2" />
+          <el-option label="品牌方" :value="1" />
+        </el-select>
+      </div>
+      <div class="filter-control">
+        <span>手机号:</span>
+        <el-input v-model="listQuery.mobile" placeholder="手机号" @keyup.enter.native="getList" />
+      </div>
+      <div class="filter-control">
+        <span>审核状态:</span>
+        <el-select v-model="listQuery.lowerAuditStatus" placeholder="审核状态" clearable @change="getList">
+          <el-option label="全部" value="" />
+          <el-option label="已完成审核" :value="1" />
+          <el-option label="未完成审核" :value="0" />
+        </el-select>
+      </div>
+      <div class="filter-control">
+        <span>联系人:</span>
+        <el-input v-model="listQuery.linkMan" placeholder="联系人" @keyup.enter.native="getList" />
+      </div>
+      <div class="filter-control">
+        <el-button type="primary" @click="getList">查询</el-button>
+      </div>
+    </div>
+    <!-- 搜索区域END -->
+    <!-- 表格区域 -->
+    <el-table
+      v-loading="listLoading"
+      :data="list"
+      style="width: 100%"
+      border
+      fit
+      highlight-current-row
+      header-row-class-name="tableHeader"
+      cell-class-name="table-cell"
+    >
+      <el-table-column :index="indexMethod" label="序号" type="index" width="80" align="center" />
+      <el-table-column prop="name" label="供应商名称" align="center" />
+      <el-table-column label="供应商类型" width="150px" align="center">
+        <template slot-scope="{ row }">
+          <span v-if="row.shopType === 1">品牌方</span>
+          <span v-if="row.shopType === 2">代理商</span>
+        </template>
+      </el-table-column>
+      <el-table-column prop="mobile" label="手机号" width="200px" align="center" />
+      <el-table-column prop="linkMan" label="联系人" width="200px" align="center" />
+      <el-table-column label="审核状态" width="220px" align="center">
+        <template slot-scope="{ row }">
+          <el-tag v-if="row.lowerAuditStatus === 0" size="small" type="danger">未完成审核</el-tag>
+          <el-tag v-if="row.lowerAuditStatus === 1" size="small" type="success">已完成审核</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" width="320px" align="center">
+        <template slot-scope="{ row }">
+          <el-badge :hidden="row.lowerAuditStatus === 1" :value="row.doctorWaitNum" :max="99">
+            <el-button
+              type="primary"
+              icon="el-icon-s-check"
+              size="mini"
+              @click="$_navigationTo(`personnel/operate/doctor-list?authUserId=${row.authUserId}`)"
+            >操作医师审核</el-button>
+          </el-badge>
+          <el-badge :hidden="row.lowerAuditStatus === 1" :value="row.doctorWaitNum" :max="99">
+            <el-button
+              type="primary"
+              icon="el-icon-s-check"
+              size="mini"
+              @click="$_navigationTo(`personnel/training/doctor-list?authUserId=${row.authUserId}`)"
+            >培训医师审核</el-button>
+          </el-badge>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 表格区域END -->
+    <!-- 页码 -->
+    <pagination
+      v-show="total > 0"
+      :total="total"
+      :page.sync="listQuery.pageNum"
+      :limit.sync="listQuery.pageSize"
+      @pagination="getList(listQuery)"
+    />
+  </div>
+</template>
+
+<script>
+import { fetchSupplierList } from '@/api/supplier'
+import Pagination from '@/components/Pagination'
+export default {
+  components: { Pagination },
+  data() {
+    return {
+      listQuery: {
+        listType: 4,
+        brandId: '', // 品牌id
+        linkMan: '', // 联系人
+        mobile: '', // 手机号
+        pageNum: 0, // 页码
+        pageSize: 10, // 分页大小
+        shopName: '', // 供应商名称
+        shopType: '', // 供应商类型
+        lowerAuditStatus: ''
+      },
+      listLoading: false,
+      list: [],
+      total: 0
+    }
+  },
+  created() {
+    this.getList()
+  },
+  methods: {
+    // 过滤列表
+    handleFilter() {},
+    // 获取供应商列表
+    getList() {
+      this.listLoading = true
+      fetchSupplierList(this.listQuery)
+        .then(res => {
+          if (res.code !== 0) return
+          this.list = res.data.list
+          this.total = res.data.total
+        })
+        .finally(() => {
+          this.listLoading = false
+        })
+    },
+    indexMethod(index) {
+      return index + this.listQuery.pageSize * (this.listQuery.pageNum - 1) + 1
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.el-table .cell {
+  overflow: visible;
+}
+.el-badge {
+  margin: 0 6px;
+}
+</style>

+ 0 - 0
src/views/admin/audit/doctor/list.vue → src/views/admin/audit/personnel/operate/index.vue


+ 0 - 0
src/views/admin/audit/doctor/review.vue → src/views/admin/audit/personnel/operate/review.vue


+ 185 - 0
src/views/admin/audit/personnel/training/index.vue

@@ -0,0 +1,185 @@
+<template>
+  <div class="app-container">
+    <div class="filter-container">
+      <div class="filter-control">
+        <span>医师姓名:</span>
+        <el-input v-model="listQuery.doctorName" placeholder="医师姓名" @keyup.enter.native="handleFilter" />
+      </div>
+      <div class="filter-control">
+        <span>从业资格证编号:</span>
+        <el-input v-model="listQuery.certificateNo" placeholder="从业资格证编号" @keyup.enter.native="handleFilter" />
+      </div>
+      <div class="filter-control">
+        <span>审核状态:</span>
+        <el-select v-model="listQuery.auditStatus" placeholder="审核状态" clearable @change="getList">
+          <el-option label="全部" value="" />
+          <el-option label="待审核" :value="2" />
+          <el-option label="审核通过" :value="1" />
+          <el-option label="审核未通过" :value="0" />
+        </el-select>
+      </div>
+      <div class="filter-control">
+        <el-button type="primary" @click="getList">查询</el-button>
+      </div>
+    </div>
+    <!-- 表格区域 -->
+    <el-table
+      :key="tableKey"
+      v-loading="listLoading"
+      :data="list"
+      border
+      fit
+      highlight-current-row
+      style="width: 100%;"
+      header-row-class-name="tableHeader"
+    >
+      <el-table-column label="序号" :index="indexMethod" type="index" align="center" width="80" />
+      <el-table-column label="医生姓名" align="center" prop="doctorName" />
+      <el-table-column label="从业资格证编号" align="center" prop="certificateNo" />
+
+      <el-table-column label="审核状态" width="220px" align="center">
+        <template slot-scope="{ row }">
+          <el-tag v-if="row.auditStatus === 2" size="small" type="warning">待审核</el-tag>
+          <el-tag v-if="row.auditStatus === 1" size="small" type="success">审核通过</el-tag>
+          <!-- 未通过原因展示 -->
+          <template v-if="row.auditStatus === 0">
+            <!-- <span class="status danger">审核未通过&nbsp;</span> -->
+            <el-popover placement="top-start" title="审核说明" width="400" trigger="hover" :content="row.invalidReason">
+              <el-tag slot="reference" size="small" type="danger" class="reason">
+                <span>审核未通过</span>
+                <span class="el-icon-question status danger " />
+              </el-tag>
+            </el-popover>
+            <!-- 未通过原因展示END -->
+          </template>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="审核时间" width="250px" align="center">
+        <template slot-scope="{ row }">
+          <span v-if="row.auditStatus !== 2">{{ row.auditTime | formatTime }}</span>
+          <span v-else>—</span>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="审核人" align="center">
+        <template slot-scope="{ row }">
+          <span v-if="row.auditStatus !== 2">{{ row.auditBy }}</span>
+          <span v-else>—</span>
+        </template>
+      </el-table-column>
+
+      <!-- <el-table-column v-if="false" label="创建人" width="180px" align="center" prop="createBy" /> -->
+      <el-table-column label="操作" align="center" width="240px" class-name="small-padding fixed-width">
+        <template slot-scope="{ row }">
+          <el-button
+            v-if="row.auditStatus !== 1"
+            type="primary"
+            size="mini"
+            @click="$_navigationTo(`doctor-detail?id=${row.doctorId}`)"
+          >
+            审核
+          </el-button>
+          <span v-else class="status success el-icon-check">&nbsp;已审核</span>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 页码 -->
+    <pagination
+      v-show="total > 0"
+      :total="total"
+      :page.sync="listQuery.pageNum"
+      :limit.sync="listQuery.pageSize"
+      @pagination="getList"
+    />
+  </div>
+</template>
+
+<script>
+import Pagination from '@/components/Pagination' // secondary package based on el-pagination
+import { mapGetters } from 'vuex'
+import { formatDate } from '@/utils'
+import { fetchDoctorList, removeDoctor, doctorStatusChange } from '@/api/doctor'
+
+export default {
+  components: { Pagination },
+  filters: {
+    formatTime(time) {
+      if (!time) {
+        return ''
+      }
+      return formatDate(time, 'yyyy-MM-DD HH:mm:ss')
+    }
+  },
+  data() {
+    return {
+      listLoading: false,
+      tableKey: 0,
+      total: 0,
+      listQuery: {
+        authUserId: '',
+        listType: 2,
+        doctorName: '',
+        auditStatus: '',
+        certificateNo: '',
+        status: '',
+        pageNum: 1,
+        pageSize: 10
+      },
+      list: []
+    }
+  },
+  computed: {
+    ...mapGetters(['authUserId', 'proxyInfo'])
+  },
+  created() {
+    this.getList()
+  },
+  methods: {
+    // 获取列表
+    getList() {
+      this.listQuery.authUserId = this.$route.query.authUserId
+
+      console.log(this.listQuery.authUserId)
+      fetchDoctorList(this.listQuery).then(res => {
+        console.log(res)
+        this.list = res.data.list
+        this.total = res.data.total
+      })
+    },
+    // 修改状态
+    handleChangeStatus(row) {
+      const status = row.status === 1 ? 0 : 1
+
+      doctorStatusChange({ doctorId: row.doctorId, status }).then(res => {
+        this.$message.success('状态修改成功')
+        this.getList()
+      })
+    },
+    // 删除
+    async handleRemoveDoctor(row) {
+      const text = await this.$confirm('确认删除该医师信息吗?删除后不可恢复', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).catch(() => {
+        this.$message.info('已取消操作')
+      })
+      if (text !== 'confirm') return
+
+      removeDoctor({ doctorId: row.doctorId }).then(res => {
+        this.$message.success('删除成功')
+        this.getList()
+      })
+    },
+    // 表格索引
+    indexMethod(index) {
+      return index + this.listQuery.pageSize * (this.listQuery.pageNum - 1) + 1
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 197 - 0
src/views/admin/audit/personnel/training/review.vue

@@ -0,0 +1,197 @@
+<template>
+  <div class="doctor-edit">
+    <el-form ref="formRef" class="doctor-edit-form" label-width="140px" :model="formData" :rules="formRules">
+      <el-form-item label="医师姓名:">
+        <span>{{ doctorInfo.doctorName }}</span>
+      </el-form-item>
+      <el-form-item label="从业资格证编号:">
+        <span>{{ doctorInfo.certificateNo }}</span>
+      </el-form-item>
+      <el-form-item label="所在机构:" prop="clubName">
+        <span>{{ doctorInfo.clubName }}</span>
+      </el-form-item>
+      <el-form-item label="医师照片:" prop="doctorImage">
+        <el-image
+          style="width: 140px; height: 140px"
+          :src="doctorInfo.doctorImage"
+          :preview-src-list="doctorImgList"
+        />
+      </el-form-item>
+      <el-form-item label="轮播图:" prop="banner">
+        <el-image
+          v-for="(image,index) in doctorInfo.bannerList"
+          :key="index"
+          style="width: 140px; height: 140px"
+          :src="image"
+          :preview-src-list="bannerImgList"
+        />
+      </el-form-item>
+
+      <el-form-item label="具备操作资格设备:" prop="equipment">
+        <div class="device-section">
+          <div v-for="(equipment, index) in doctorInfo.equipmentList" :key="index" class="section">
+            <el-form label-width="90px">
+              <el-form-item label="设备名称:">
+                <span>{{ equipment.equipmentName }}</span>
+              </el-form-item>
+              <el-form-item label="所属品牌:">
+                <span>{{ equipment.brand }}</span>
+              </el-form-item>
+              <el-form-item label="设备图片:">
+                <el-image
+                  style="width: 140px; height: 140px"
+                  :src="equipment.image"
+                  :preview-src-list="equipmentImgList"
+                />
+              </el-form-item>
+            </el-form>
+          </div>
+        </div>
+      </el-form-item>
+
+      <!-- 审核表单 -->
+      <el-form-item label="审核状态:">
+        <el-radio-group v-model="formData.auditStatus">
+          <el-radio :label="1">通过</el-radio>
+          <el-radio :label="0">不通过</el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item v-if="formData.auditStatus!==1" prop="invalidReason" label="原因:">
+        <el-input v-model="formData.invalidReason" type="textarea" placeholder="请说明原因" />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="info" @click="$_back">返回</el-button>
+        <el-button type="primary" @click="submit">提交</el-button>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+import { doctorFormData, doctorAudit } from '@/api/doctor'
+
+export default {
+  data() {
+    return {
+      isRequest: true,
+      doctorId: '',
+      point: {},
+      disabled: false,
+      doctorInfo: {
+        doctorName: '',
+        certificateNo: '',
+        clubName: '',
+        bannerList: '',
+        doctorImage: '',
+        equipmentList: ''
+      },
+      formData: {
+        auditBy: '', // 审核人id
+        doctorId: '', // 商品id
+        auditStatus: 1, // 商品审核状态
+        invalidReason: '' // 审核信息
+      },
+      formRules: {
+        invalidReason: { required: true, message: '不通过原因不能为空', tigger: 'blur' }
+      },
+      doctorImgList: [],
+      bannerImgList: [],
+      equipmentImgList: []
+    }
+  },
+  computed: {
+    ...mapGetters(['authUserId'])
+  },
+  created() {
+    this.doctorId = this.formData.doctorId = parseInt(this.$route.query.id)
+    this.initFormData()
+  },
+  methods: {
+    // 表单数据回显
+    initFormData() {
+      doctorFormData({ doctorId: this.doctorId }).then(res => {
+        this.setFormData(res.data)
+      })
+    },
+    // 设置表单数据
+    setFormData(data) {
+      this.doctorInfo.doctorName = data.doctorName
+      this.doctorInfo.certificateNo = data.certificateNo
+      this.doctorInfo.clubName = data.clubName
+      this.doctorInfo.bannerList = data.bannerList
+      this.doctorInfo.doctorImage = data.doctorImage
+      this.doctorInfo.equipmentList = data.equipmentList
+
+      this.doctorImgList = [data.doctorImage]
+      this.bannerImgList = data.bannerList
+      this.equipmentImgList = data.equipmentList.map(item => item.image)
+    },
+
+    // 提交审核信息
+    submit() {
+      this.$refs.formRef.validate(valid => {
+        if (valid) {
+          this.isLoading = true
+          // 指定审核人
+          this.formData.auditBy = this.authUserId
+          // 提交审核信息
+          doctorAudit(this.formData).then(res => {
+            if (res.code !== 0) return
+            this.$message.success(res.data)
+            this.$store.dispatch('tagsView/delView', this.$route)
+            this.$router.back()
+          }).finally(() => {
+            this.isLoading = false
+          })
+        }
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.doctor-edit{
+  margin-bottom: 80px;
+}
+
+.doctor-edit-form {
+  width: 650px;
+  margin: 0 auto;
+  margin-top: 80px;
+}
+.submit-btn {
+  text-align: center;
+  .el-button {
+    width: 140px;
+  }
+}
+
+.device-section {
+  .section {
+    position: relative;
+    border: 1px solid #eee;
+    padding: 25px 25px 0 16px;
+    margin-bottom: 16px;
+    .remove {
+      position: absolute;
+      right: 6px;
+      top: 6px;
+      font-size: 14px;
+      cursor: pointer;
+      background: #f56c6c;
+      color: #fff;
+      padding: 2px;
+      border-radius: 50%;
+      transition: all 0.2s;
+      &:hover {
+        background: red;
+      }
+    }
+  }
+  .el-form-item {
+    padding-bottom: 25px;
+  }
+}
+</style>

+ 38 - 0
src/views/components/CustomItems/index.vue

@@ -0,0 +1,38 @@
+<template>
+  <div class="custom-items">
+    <el-form ref="form" :model="formData" :rules="rules" label-width="85px">
+      <el-form-item label="认证设备" prop="name">
+        <el-input v-model="formData.name" />
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'CustomItems',
+  data() {
+    return {
+      formData: {
+        name: ''
+      },
+      rules: {
+        name: [{ required: true, message: '认证设备不能为空', tiggers: ['blur'] }]
+      }
+    }
+  },
+  watch: {
+    formData: {
+      handler: function() {
+        this.$emit('change', this.formData)
+      },
+      deep: true
+    }
+  },
+  methods: {
+    validate(callback) {
+      return this.$refs.form.validate(callback)
+    }
+  }
+}
+</script>

+ 101 - 0
src/views/components/DoctorCustomItems/index.vue

@@ -0,0 +1,101 @@
+<template>
+  <div class="doctor-custom-items">
+    <div v-for="formData in formDataList" :key="formData.uuid" class="custom-section">
+      <i v-if="formDataList.length > 1" class="el-icon-close remove" title="移除该项" @click="removeOne(formData)" />
+      <el-form :ref="'form' + formData.uuid" :model="formData" :rules="rules" label-width="90px">
+        <el-form-item label="字段名称" prop="name">
+          <el-input v-model="formData.name" placeholder="字段名称" />
+        </el-form-item>
+        <el-form-item label="字段内容" prop="content">
+          <el-input v-model="formData.content" type="textarea" placeholder="字段内容" rows="5" />
+        </el-form-item>
+      </el-form>
+    </div>
+    <el-button type="primary" size="mini" round @click="insertOne">添加</el-button>
+  </div>
+
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      uuid: 1,
+      formDataList: [{
+        uuid: 1,
+        name: '',
+        content: ''
+      }],
+      rules: {
+        name: [{ required: true, message: '字段名称不能为空', tiggers: ['blur'] }],
+        content: [{ required: true, message: '字段内容不能为空', tiggers: ['blur'] }]
+      }
+    }
+  },
+  watch: {
+    formDataList: {
+      handler: function(nVal) {
+        this.$emit('change', this.formDataList)
+      },
+      deep: true
+    }
+  },
+  methods: {
+    // 插入一条
+    insertOne() {
+      this.uuid++
+      this.formDataList.push({
+        uuid: this.uuid,
+        name: '',
+        content: ''
+      })
+    },
+    // 删除一条
+    removeOne(formData) {
+      const uuid = formData.uuid
+      const index = this.formDataList.findIndex(item => item.uuid === uuid)
+      this.formDataList.splice(index, 1)
+    },
+    // 验证表单数据
+    valideAllForm() {
+      const validateList = []
+      this.formDataList.forEach(formData => {
+        const refName = 'form' + formData.uuid
+        validateList.push(this.$refs[refName][0].validate())
+      })
+      return Promise.all(validateList)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.doctor-custom-items{
+  .custom-section{
+    position: relative;
+    border: 1px solid #eee;
+    padding: 25px 25px 0 16px;
+    margin-bottom: 16px;
+
+    .el-form-item{
+      padding-bottom: 25px;
+    }
+
+    .remove {
+      position: absolute;
+      right: 6px;
+      top: 6px;
+      font-size: 14px;
+      cursor: pointer;
+      background: #f56c6c;
+      color: #fff;
+      padding: 2px;
+      border-radius: 50%;
+      transition: all 0.2s;
+      &:hover {
+        background: red;
+      }
+    }
+  }
+}
+</style>

+ 196 - 0
src/views/normal/club/device-cate.vue

@@ -0,0 +1,196 @@
+<template>
+  <div class="app-container">
+    <div class="filter-container">
+      <div class="filter-control">
+        <span>设备管理:</span>
+        <el-input v-model="listQuery.productName" placeholder="设备管理" @keyup.enter.native="handleFilter" />
+      </div>
+      <div class="filter-control">
+        <span>审核状态:</span>
+        <el-select v-model="listQuery.auditStatus" placeholder="审核状态" clearable @change="getList">
+          <el-option label="全部" value="" />
+          <el-option label="待审核" :value="2" />
+          <el-option label="审核通过" :value="1" />
+          <el-option label="审核未通过" :value="0" />
+        </el-select>
+      </div>
+      <div class="filter-control">
+        <span>上线状态:</span>
+        <el-select v-model="listQuery.status" placeholder="上线状态" clearable @change="getList">
+          <el-option label="全部" value="" />
+          <el-option label="已上线" :value="1" />
+          <el-option label="待上线" :value="2" />
+          <el-option label="未上线" :value="0" />
+        </el-select>
+      </div>
+      <div class="filter-control">
+        <permission-button type="primary" @click="handleFilter">查询</permission-button>
+        <permission-button
+          type="primary"
+          @click="handleAdd"
+        >添加</permission-button>
+      </div>
+    </div>
+    <!-- 表格区域 -->
+    <el-table
+      :key="tableKey"
+      v-loading="listLoading"
+      :data="list"
+      border
+      fit
+      highlight-current-row
+      style="width: 100%;"
+      header-row-class-name="tableHeader"
+    >
+      <el-table-column label="序号" :index="indexMethod" type="index" align="center" width="80" />
+      <el-table-column label="设备管理" align="center" prop="productName" />
+
+      <el-table-column label="审核状态" width="220px" align="center">
+        <template slot-scope="{ row }">
+          <el-tag v-if="row.auditStatus === 2" size="small" type="warning">待审核</el-tag>
+          <el-tag v-if="row.auditStatus === 1" size="small" type="success">审核通过</el-tag>
+          <!-- 未通过原因展示 -->
+          <template v-if="row.auditStatus === 0">
+            <!-- <span class="status danger">审核未通过&nbsp;</span> -->
+            <el-popover placement="top-start" title="审核说明" width="400" trigger="hover" :content="row.invalidReason">
+              <el-tag slot="reference" size="small" type="danger" class="reason">
+                <span>审核未通过</span>
+                <span class="el-icon-question status danger " />
+              </el-tag>
+            </el-popover>
+            <!-- 未通过原因展示END -->
+          </template>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="上线状态" width="140px" align="center">
+        <template slot-scope="{ row }">
+          <!-- 只有审核通过了才能操作上下线 auditStatus :审核状态 -->
+          <template v-if="row.auditStatus === 1">
+            <template v-if="row.status === 0">
+              <span style="margin-right:10px;" class="status danger">已下线</span>
+              <permission-button type="primary" size="mini" @click="handleChangeStatus(row)">上线</permission-button>
+            </template>
+            <template v-else>
+              <span style="margin-right:10px;" class="status success ">已上线</span>
+              <permission-button type="info" size="mini" @click="handleChangeStatus(row)">下线</permission-button>
+            </template>
+          </template>
+          <template v-else>
+            <!-- <el-tag type="warning">待上线</el-tag> -->
+            <span style="margin-right:10px;" class="status warning">待上线</span>
+          </template>
+        </template>
+      </el-table-column>
+      <el-table-column label="创建时间" class-name="status-col" width="300px" align="center">
+        <template slot-scope="{ row }">
+          <span>{{ row.createTime | formatTime }}</span>
+        </template>
+      </el-table-column>
+      <!-- <el-table-column v-if="false" label="创建人" width="180px" align="center" prop="createBy" /> -->
+      <el-table-column label="操作" align="center" width="240px" class-name="small-padding fixed-width">
+        <template slot-scope="{ row }">
+          <permission-button
+            type="default"
+            size="mini"
+            @click="handleEdit"
+          >
+            编辑
+          </permission-button>
+          <permission-button type="danger" size="mini" @click="handleRemoveProduct(row)">
+            删除
+          </permission-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 页码 -->
+    <pagination
+      v-show="total > 0"
+      :total="total"
+      :page.sync="listQuery.pageNum"
+      :limit.sync="listQuery.pageSize"
+      @pagination="getList"
+    />
+  </div>
+</template>
+
+<script>
+import PermissionButton from '@/views/components/PermissionButton'
+import Pagination from '@/components/Pagination' // secondary package based on el-pagination
+import { formatDate } from '@/utils'
+import { mapGetters } from 'vuex'
+export default {
+  name: 'ComplexTable',
+  components: { Pagination, PermissionButton },
+  filters: {
+    formatTime(time) {
+      if (!time) {
+        return ''
+      }
+      return formatDate(time, 'yyyy-MM-DD HH:mm:ss')
+    }
+  },
+
+  data() {
+    return {
+      authParty: '',
+      tableKey: 0,
+      list: null,
+      total: 0,
+      listLoading: true,
+      listQuery: {
+        status: '',
+        auditStatus: '',
+        authId: '',
+        productName: '',
+        snCode: '',
+        pageNum: 1,
+        pageSize: 10
+      },
+      showQRcode: false,
+      productInfo: {},
+      // 审核未通过
+      auditFailedList: [],
+      auditNoticeFlag: true
+    }
+  },
+  computed: {
+    ...mapGetters(['userIdentity'])
+  },
+  created() {
+    this.getList()
+  },
+
+  methods: {
+    // 获取列表信息
+    getList() {
+      this.listLoading = false
+    },
+    // 过滤列表
+    handleFilter() {
+
+    },
+    // 改变启用状态
+    handleChangeStatus(item) {
+
+    },
+    // 添加分类
+    handleAdd() {
+
+    },
+    // 添加分类
+    handleEdit() {
+
+    },
+    // 表格索引
+    indexMethod(index) {
+      return index + this.listQuery.pageSize * (this.listQuery.pageNum - 1) + 1
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 67 - 41
src/views/normal/club/edit.vue

@@ -48,6 +48,15 @@
           @remove="handleBannerRemove"
         />
       </el-form-item>
+      <el-form-item label="自定义属性:">
+        <el-radio-group v-model="formData.customize" size="mini">
+          <el-radio :label="0" border>否</el-radio>
+          <el-radio :label="1" border>是</el-radio>
+        </el-radio-group>
+        <div v-show="formData.customize" class="attributes">
+          <custom-items ref="customizeForm" @change="custromizeFormChange" />
+        </div>
+      </el-form-item>
     </el-form>
     <div class="submit-btn">
       <el-button type="primary" @click="submit">保存</el-button>
@@ -73,6 +82,7 @@
 // import Location from '@/components/location'
 import AMapUI from '@/components/AMapUI'
 import UploadImage from '@/components/UploadImage'
+import CustomItems from '@/views/components/CustomItems'
 import { mapGetters } from 'vuex'
 import { saveBrandAuth, getAuthFormData } from '@/api/auth'
 import { getAddress } from '@/api/common'
@@ -82,7 +92,8 @@ export default {
   components: {
     // Location
     [AMapUI.name]: AMapUI,
-    UploadImage
+    UploadImage,
+    CustomItems
   },
   data() {
     var validatePoint = (rule, value, callback) => {
@@ -127,7 +138,9 @@ export default {
         point: '',
         mobile: '',
         logoImage: '',
-        banner: ''
+        banner: '',
+        customize: 0,
+        customValue: ''
       },
       rules: {
         name: [{ required: true, message: '机构名称不能为空', trigger: ['blur', 'change'] }],
@@ -177,6 +190,11 @@ export default {
     this.initFormData()
   },
   methods: {
+    // 自定义属性修改
+    custromizeFormChange(data) {
+      this.customValue = data.name
+    },
+
     // 获取地址
     initAddress() {
       return getAddress({
@@ -215,51 +233,55 @@ export default {
 
     // 表单提交保存
     submit() {
-      this.$refs.submitForm.validate(valide => {
-        if (!valide) return
+      this.$refs.customizeForm.validate(valide => {
+        if (!valide && this.formData.customize) return
 
-        console.log(this.formData)
-        const {
-          name: authParty,
-          address: [provinceId, cityId, townId],
-          mobile,
-          logoImage: logo,
-          point: lngAndLat
-        } = this.formData
+        this.$refs.submitForm.validate(valide => {
+          if (!valide) return
 
-        const authUserId = this.proxyInfo?.authUserId || this.authUserId
+          console.log(this.formData)
+          const {
+            name: authParty,
+            address: [provinceId, cityId, townId],
+            mobile,
+            logoImage: logo,
+            point: lngAndLat
+          } = this.formData
 
-        const data = {
-          authId: parseInt(this.authId),
-          authParty,
-          authUserId,
-          createBy: authUserId,
-          provinceId,
-          cityId,
-          townId,
-          address: this.formData.fullAddress,
-          mobile,
-          logo,
-          lngAndLat
-        }
+          const authUserId = this.proxyInfo?.authUserId || this.authUserId
 
-        data.bannerList = this.bannerList.map(item => (item.response ? item.response.data : item.url))
+          const data = {
+            authId: parseInt(this.authId),
+            authParty,
+            authUserId,
+            createBy: authUserId,
+            provinceId,
+            cityId,
+            townId,
+            address: this.formData.fullAddress,
+            mobile,
+            logo,
+            lngAndLat
+          }
 
-        saveBrandAuth(data)
-          .then(res => {
-            const h = this.$createElement
-            this.$notify.success({
-              title: `修改授权机构`,
-              message: h('i', { style: 'color: #333' }, `已修改授权机构:"${this.formData.name}"`),
-              duration: 3000
+          data.bannerList = this.bannerList.map(item => (item.response ? item.response.data : item.url))
+
+          saveBrandAuth(data)
+            .then(res => {
+              const h = this.$createElement
+              this.$notify.success({
+                title: `修改授权机构`,
+                message: h('i', { style: 'color: #333' }, `已修改授权机构:"${this.formData.name}"`),
+                duration: 3000
+              })
+              this.$refs.submitForm.resetFields()
+              this.$store.dispatch('tagsView/delView', this.$route)
+              this.$router.push('/club/list')
+            })
+            .catch(err => {
+              this.$message.danger(err.msg)
             })
-            this.$refs.submitForm.resetFields()
-            this.$store.dispatch('tagsView/delView', this.$route)
-            this.$router.push('/club/list')
-          })
-          .catch(err => {
-            this.$message.danger(err.msg)
-          })
+        })
       })
     },
     handleChange(e) {
@@ -326,4 +348,8 @@ export default {
     width: 140px;
   }
 }
+
+.attributes{
+  padding-top: 16px;
+}
 </style>

+ 205 - 0
src/views/normal/club/user/index-temp.vue

@@ -0,0 +1,205 @@
+<template>
+  <div class="app-container">
+    <!-- 搜索区域 -->
+    <div class="filter-container">
+      <div class="filter-control">
+        <span>手机号:</span>
+        <el-input
+          v-model="listQuery.mobile"
+          placeholder="手机号"
+          @keyup.enter.native="getList"
+        />
+      </div>
+      <div class="filter-control">
+        <span>状态:</span>
+        <el-select
+          v-model="listQuery.status"
+          placeholder="邀请码状态"
+          clearable
+          @change="getList"
+        >
+          <el-option label="全部" value="" />
+          <el-option label="已绑定" :value="1" />
+          <el-option label="未绑定" :value="0" />
+          <el-option label="已过期" :value="2" />
+        </el-select>
+      </div>
+      <div class="filter-control">
+        <permission-button type="primary" @click="getList">查询</permission-button>
+        <permission-button type="primary" @click="handleMakeGenerate">生成邀请码</permission-button>
+      </div>
+    </div>
+    <!-- 搜索区域END -->
+    <!-- 表格区域 -->
+    <el-table
+      v-loading="listLoading"
+      :data="list"
+      style="width: 100%"
+      border
+      fit
+      highlight-current-row
+      cell-class-name="table-cell"
+      header-row-class-name="tableHeader"
+    >
+      <el-table-column label="序号" :index="indexMethod" type="index" width="80px" align="center" />
+      <el-table-column label="邀请码" prop="invitationCode" width="100px" align="center" />
+      <el-table-column label="状态" width="100px" align="center">
+        <template slot-scope="{ row }">
+          <el-tag v-if="row.status === 0" size="small" type="warning">未绑定</el-tag>
+          <el-tag v-if="row.status === 1" size="small" type="success">已绑定</el-tag>
+          <el-tag v-if="row.status === 2" size="small" type="danger">已过期</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="添加时间" width="200px" align="center">
+        <template slot-scope="{row}">
+          <span>{{ row.addTime | formatTime }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="微信昵称" align="center">
+        <template slot-scope="{row}">
+          <span v-if="row.nickName">{{ row.nickName }}</span>
+          <span v-else>—</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="手机号" width="140" align="center">
+        <template slot-scope="{row}">
+          <span v-if="row.mobile">{{ row.mobile }}</span>
+          <span v-else>—</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="openID" width="280" align="center">
+        <template slot-scope="{row}">
+          <span v-if="row.openId">{{ row.openId }}</span>
+          <span v-else>—</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="绑定时间" width="200px" align="center">
+        <template slot-scope="{row}">
+          <span v-if="row.bindTime">{{ row.bindTime | formatTime }}</span>
+          <span v-else>—</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" width="200px" align="center">
+        <template slot-scope="{row}">
+          <permission-button v-if="row.status === 1" type="danger" size="mini" style="margin-right:5px" @click="handleUnbindGenerate(row)">解绑邀请码</permission-button>
+          <permission-button v-else type="primary" size="mini" style="margin-right:5px" @click="handleUpdateGenerate(row)">更新邀请码</permission-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 表格区域END -->
+    <pagination v-show="total>0" :total="total" :page.sync="listQuery.pageNum" :limit.sync="listQuery.pageSize" @pagination="getList(listQuery)" />
+  </div>
+</template>
+
+<script>
+import PermissionButton from '@/views/components/PermissionButton'
+import Pagination from '@/components/Pagination' // secondary package based on el-pagination
+import { formatDate } from '@/utils'
+import { mapGetters } from 'vuex'
+import { getAuthUserList, makeGenerate, unbindGenerate, updateGenerate } from '@/api/auth'
+export default {
+  components: { Pagination, PermissionButton },
+  filters: {
+    formatTime(time) {
+      if (!time) {
+        return ''
+      }
+      return formatDate(time, 'yyyy-MM-DD HH:mm:ss')
+    }
+  },
+  data() {
+    return {
+      total: 0,
+      authId: '', // 机构id
+      listLoading: false,
+      listQuery: {
+        authId: '', // 机构id
+        mobile: '', // 手机号
+        status: '', // 邀请码状态
+        pageNum: 0, // 页码
+        pageSize: 10 // 分页大小
+      },
+      list: [],
+      srcList: []
+    }
+  },
+  computed: {
+    ...mapGetters(['authUserId'])
+  },
+  created() {
+    this.authId = this.$route.query.id
+    this.getList()
+  },
+  methods: {
+    // 获取列表数据
+    getList() {
+      this.listLoading = true
+      this.listQuery.authId = this.authId
+      getAuthUserList(this.listQuery)
+        .then(res => {
+          if (res.code !== 0) return
+          this.list = res.data.list
+          this.total = res.data.total
+          console.log(res)
+        })
+        .finally(() => {
+          this.listLoading = false
+        })
+    },
+    // 生成邀请码
+    handleMakeGenerate() {
+      makeGenerate({ authId: this.authId })
+        .then(res => {
+          console.log(res)
+          if (res.code !== 0) return
+          this.$message({
+            type: 'success',
+            message: res.data,
+            duration: 500
+          })
+          this.getList()
+        })
+    },
+    // 更新邀请码
+    handleUpdateGenerate(row) {
+      updateGenerate({ clubUserId: row.clubUserId })
+        .then(res => {
+          if (res.code !== 0) return
+          console.log(res)
+          this.$message({
+            type: 'success',
+            message: res.data,
+            duration: 500
+          })
+          this.getList()
+        })
+    },
+    // 解绑邀请码
+    handleUnbindGenerate(row) {
+      unbindGenerate({ clubUserId: row.clubUserId })
+        .then(res => {
+          if (res.code !== 0) return
+          console.log(res)
+          this.$message({
+            type: 'success',
+            message: res.data,
+            duration: 500
+          })
+          this.getList()
+        })
+    },
+    indexMethod(index) {
+      return index + this.listQuery.pageSize * (this.listQuery.pageNum - 1) + 1
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.el-table .cell {
+  overflow: visible;
+}
+.el-badge{
+  margin: 0 6px;
+}
+</style>

+ 20 - 82
src/views/normal/club/user/index.vue

@@ -3,30 +3,16 @@
     <!-- 搜索区域 -->
     <div class="filter-container">
       <div class="filter-control">
-        <span>手机号:</span>
-        <el-input
-          v-model="listQuery.mobile"
-          placeholder="手机号"
-          @keyup.enter.native="getList"
-        />
+        <span>姓名:</span>
+        <el-input v-model="listQuery.mobile" placeholder="姓名" @keyup.enter.native="getList" />
       </div>
       <div class="filter-control">
-        <span>状态:</span>
-        <el-select
-          v-model="listQuery.status"
-          placeholder="邀请码状态"
-          clearable
-          @change="getList"
-        >
-          <el-option label="全部" value="" />
-          <el-option label="已绑定" :value="1" />
-          <el-option label="未绑定" :value="0" />
-          <el-option label="已过期" :value="2" />
-        </el-select>
+        <span>手机号:</span>
+        <el-input v-model="listQuery.mobile" placeholder="手机号" @keyup.enter.native="getList" />
       </div>
       <div class="filter-control">
         <permission-button type="primary" @click="getList">查询</permission-button>
-        <permission-button type="primary" @click="handleMakeGenerate">生成邀请码</permission-button>
+        <permission-button type="primary" @click="handleCreate">添加用户</permission-button>
       </div>
     </div>
     <!-- 搜索区域END -->
@@ -42,12 +28,11 @@
       header-row-class-name="tableHeader"
     >
       <el-table-column label="序号" :index="indexMethod" type="index" width="80px" align="center" />
-      <el-table-column label="邀请码" prop="invitationCode" width="100px" align="center" />
-      <el-table-column label="状态" width="100px" align="center">
-        <template slot-scope="{ row }">
-          <el-tag v-if="row.status === 0" size="small" type="warning">未绑定</el-tag>
-          <el-tag v-if="row.status === 1" size="small" type="success">已绑定</el-tag>
-          <el-tag v-if="row.status === 2" size="small" type="danger">已过期</el-tag>
+      <el-table-column label="姓名" prop="invitationCode" align="center" />
+      <el-table-column label="手机号" width="140" align="center">
+        <template slot-scope="{row}">
+          <span v-if="row.mobile">{{ row.mobile }}</span>
+          <span v-else>—</span>
         </template>
       </el-table-column>
       <el-table-column label="添加时间" width="200px" align="center">
@@ -61,28 +46,16 @@
           <span v-else>—</span>
         </template>
       </el-table-column>
-      <el-table-column label="手机号" width="140" align="center">
-        <template slot-scope="{row}">
-          <span v-if="row.mobile">{{ row.mobile }}</span>
-          <span v-else>—</span>
-        </template>
-      </el-table-column>
       <el-table-column label="openID" width="280" align="center">
         <template slot-scope="{row}">
           <span v-if="row.openId">{{ row.openId }}</span>
           <span v-else>—</span>
         </template>
       </el-table-column>
-      <el-table-column label="绑定时间" width="200px" align="center">
-        <template slot-scope="{row}">
-          <span v-if="row.bindTime">{{ row.bindTime | formatTime }}</span>
-          <span v-else>—</span>
-        </template>
-      </el-table-column>
       <el-table-column label="操作" width="200px" align="center">
         <template slot-scope="{row}">
-          <permission-button v-if="row.status === 1" type="danger" size="mini" style="margin-right:5px" @click="handleUnbindGenerate(row)">解绑邀请码</permission-button>
-          <permission-button v-else type="primary" size="mini" style="margin-right:5px" @click="handleUpdateGenerate(row)">更新邀请码</permission-button>
+          <permission-button type="danger" size="mini" style="margin-right:5px" @click="handleEdit(row)">编辑</permission-button>
+          <permission-button type="primary" size="mini" style="margin-right:5px" @click="handleRemove(row)">删除</permission-button>
         </template>
       </el-table-column>
     </el-table>
@@ -96,7 +69,7 @@ import PermissionButton from '@/views/components/PermissionButton'
 import Pagination from '@/components/Pagination' // secondary package based on el-pagination
 import { formatDate } from '@/utils'
 import { mapGetters } from 'vuex'
-import { getAuthUserList, makeGenerate, unbindGenerate, updateGenerate } from '@/api/auth'
+import { getAuthUserList } from '@/api/auth'
 export default {
   components: { Pagination, PermissionButton },
   filters: {
@@ -146,48 +119,13 @@ export default {
           this.listLoading = false
         })
     },
-    // 生成邀请码
-    handleMakeGenerate() {
-      makeGenerate({ authId: this.authId })
-        .then(res => {
-          console.log(res)
-          if (res.code !== 0) return
-          this.$message({
-            type: 'success',
-            message: res.data,
-            duration: 500
-          })
-          this.getList()
-        })
-    },
-    // 更新邀请码
-    handleUpdateGenerate(row) {
-      updateGenerate({ clubUserId: row.clubUserId })
-        .then(res => {
-          if (res.code !== 0) return
-          console.log(res)
-          this.$message({
-            type: 'success',
-            message: res.data,
-            duration: 500
-          })
-          this.getList()
-        })
-    },
-    // 解绑邀请码
-    handleUnbindGenerate(row) {
-      unbindGenerate({ clubUserId: row.clubUserId })
-        .then(res => {
-          if (res.code !== 0) return
-          console.log(res)
-          this.$message({
-            type: 'success',
-            message: res.data,
-            duration: 500
-          })
-          this.getList()
-        })
-    },
+    // 创建用户
+    handleCreate() {},
+    // 修改用户
+    handleEdit() {},
+    // 删除用户
+    handleRemove() {},
+
     indexMethod(index) {
       return index + this.listQuery.pageSize * (this.listQuery.pageNum - 1) + 1
     }

+ 415 - 0
src/views/normal/material/package/index.vue

@@ -0,0 +1,415 @@
+<template>
+  <div class="app-container">
+    <!-- 搜索区域 -->
+    <div class="filter-container">
+      <div class="filter-control">
+        <span>资料包标题:</span>
+        <el-input v-model="listQuery.fileTitle" placeholder="资料包标题" @keyup.enter.native="getList" />
+      </div>
+      <div class="filter-control">
+        <span>审核状态:</span>
+        <el-select v-model="listQuery.auditStatus" placeholder="审核状态" clearable @change="getList">
+          <el-option label="全部" value="" />
+          <el-option label="待审核" :value="2" />
+          <el-option label="审核通过" :value="1" />
+          <el-option label="审核未通过" :value="0" />
+        </el-select>
+      </div>
+      <div class="filter-control">
+        <span>上线状态:</span>
+        <el-select v-model="listQuery.status" placeholder="上线状态" clearable @change="getList">
+          <el-option label="全部" value="" />
+          <el-option label="已上线" :value="1" />
+          <el-option label="待上线" :value="2" />
+          <el-option label="未上线" :value="0" />
+        </el-select>
+      </div>
+      <div class="filter-control">
+        <permission-button type="primary" @click="getList">查询</permission-button>
+        <permission-button type="primary" @click="handleShowDialog('add')">添加资料包</permission-button>
+      </div>
+    </div>
+    <!-- 搜索区域END -->
+    <!-- 表格区域 -->
+    <el-table
+      v-loading="listLoading"
+      :data="list"
+      style="width: 100%"
+      border
+      fit
+      highlight-current-row
+      cell-class-name="table-cell"
+      header-row-class-name="tableHeader"
+    >
+      <el-table-column label="序号" :index="indexMethod" type="index" width="80" align="center" />
+      <el-table-column label="资料包标题" prop="fileTitle" align="center" />
+      <el-table-column label="审核状态" width="180px" align="center">
+        <template slot-scope="{ row }">
+          <el-tag v-if="row.auditStatus === 2" size="small" type="warning">待审核</el-tag>
+          <el-tag v-if="row.auditStatus === 1" size="small" type="success">审核通过</el-tag>
+          <!-- 未通过原因展示 -->
+          <template v-if="row.auditStatus === 0">
+            <!-- <span class="status danger">审核未通过&nbsp;</span> -->
+            <el-popover placement="top-start" title="审核说明" width="400" trigger="hover" :content="row.invalidReason">
+              <el-tag slot="reference" size="small" type="danger" class="reason">
+                <span>审核未通过</span>
+                <span class="el-icon-question status danger " />
+              </el-tag>
+            </el-popover>
+            <!-- 未通过原因展示END -->
+          </template>
+        </template>
+      </el-table-column>
+      <el-table-column label="上线状态" width="180px" align="center">
+        <template slot-scope="{ row }">
+          <!-- 只有审核通过了才能操作上下线 auditStatus :审核状态 -->
+          <template v-if="row.auditStatus === 1">
+            <template v-if="row.status === 0">
+              <span style="margin-right:10px;" class="status danger">已下线</span>
+              <permission-button type="primary" size="mini" @click="handleChangeStatus(row)">上线</permission-button>
+            </template>
+            <template v-else>
+              <span style="margin-right:10px;" class="status success ">已上线</span>
+              <permission-button type="info" size="mini" plain @click="handleChangeStatus(row)">下线</permission-button>
+            </template>
+          </template>
+          <template v-else>
+            <!-- <el-tag type="warning">待上线</el-tag> -->
+            <span style="margin-right:10px;" class="status warning">待上线</span>
+          </template>
+        </template>
+      </el-table-column>
+      <el-table-column label="创建时间" width="240px" align="center">
+        <template slot-scope="{ row }">
+          {{ row.createTime | formatTime }}
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" width="280px" align="center">
+        <template slot-scope="{ row }">
+          <permission-button
+            type="primary"
+            size="mini"
+            style="margin-right:5px"
+            @click="handleShowDialog('edit', row)"
+          >编辑</permission-button>
+          <permission-button
+            type="danger"
+            size="mini"
+            style="margin-right:5px"
+            @click="handleRemoveFile(row)"
+          >删除</permission-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 表格区域END -->
+    <pagination
+      v-show="total > 0"
+      :total="total"
+      :page.sync="listQuery.pageNum"
+      :limit.sync="listQuery.pageSize"
+      @pagination="getList(listQuery)"
+    />
+    <el-dialog
+      :title="dialogTitle"
+      :visible.sync="dialogVisible"
+      width="40%"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      @close="beforeDialogClose"
+    >
+      <el-form ref="formRef" :model="dialogData" label-width="110px" :rules="dialogFormRules">
+        <el-form-item label="标题:" prop="fileTitle">
+          <el-input v-model="dialogData.fileTitle" maxlength="50" show-word-limit />
+        </el-form-item>
+        <el-form-item label="上传文件:" prop="fileName" style="margin-bottom:0">
+          <upload-file
+            ref="fileUpload"
+            accept=".pdf"
+            mode="document"
+            :file-list="fileList"
+            :auto-upload="false"
+            :before-upload="beforeUpload"
+            @success="handleUploadFileSuccess"
+            @remove="handleRemoveUploadFile"
+            @change="handleChooseFileChange"
+          />
+          <el-input v-show="false" v-model="dialogData.fileName" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <permission-button @click="dialogVisible = false">取 消</permission-button>
+        <permission-button type="primary" :loading="submitLoading" @click="handleSave">提 交</permission-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import PermissionButton from '@/views/components/PermissionButton'
+import Pagination from '@/components/Pagination' // secondary package based on el-pagination
+import UploadFile from '@/components/UploadFile'
+import { formatDate } from '@/utils'
+import { mapGetters } from 'vuex'
+import { changeFileStatus, getFileList, removeFile, saveFile } from '@/api/doc'
+import openWindow from '@/utils/open-window'
+export default {
+  components: { Pagination, UploadFile, PermissionButton },
+  filters: {
+    formatTime(time) {
+      if (!time) {
+        return ''
+      }
+      return formatDate(time, 'yyyy-MM-DD HH:mm:ss')
+    }
+  },
+  data() {
+    return {
+      listLoading: false, // 列表加载
+      submitLoading: false, // 按钮加载
+      total: 0, // 列表条数
+      dialogVisible: false, // 是否显示dialog
+      dialogTitle: '添加文件', // dialog标题
+      listQuery: {
+        authUserId: '', // 供应商用户id
+        listType: 1, // 列表类型:1文件列表,2文件审核列表
+        fileTitle: '', // 文件标题
+        auditStatus: '', // 审核状态:0审核未通过,1审核通过,2待审核
+        status: '', // 文件状态:0已下线,1已上线,2待上线
+        pageNum: 1, // 页码
+        pageSize: 10 // 分页大小
+      },
+      fileList: [],
+      // dialog表单中的数据
+      dialogData: {
+        fileId: '',
+        fileTitle: '',
+        fileName: ''
+      },
+      // dialog表单数据校验规则
+      dialogFormRules: {
+        fileTitle: [{ required: true, message: '标题不能为空', trigger: 'blur' }],
+        fileName: [{ required: true, message: '文件不能为空', trigger: 'change' }]
+      },
+      currentFileData: {
+        filePreviewUrl: '',
+        fileDownloadUrl: ''
+      },
+      list: []
+    }
+  },
+  computed: {
+    ...mapGetters(['authUserId', 'userIdentity'])
+  },
+  created() {
+    this.getList()
+  },
+  methods: {
+    // 小窗口浏览pdf文件
+    handlePreviewFile(row) {
+      openWindow(row.filePreviewUrl, row.fileTitle, row.fileTitle, 1200, 600)
+    },
+
+    // 获取列表数据
+    getList() {
+      this.listLoading = true
+      this.listQuery.authUserId = this.authUserId
+      getFileList(this.listQuery)
+        .then(res => {
+          if (res.code !== 0) return
+          this.list = res.data.list
+          this.total = res.data.total
+          console.log(res)
+        })
+        .finally(() => {
+          this.listLoading = false
+        })
+    },
+
+    // 点击保存按钮
+    handleSave() {
+      const status = this.fileList.length > 0 && this.fileList[0].status
+      if (status === 'success') {
+        this.save()
+      }
+      if (status === 'ready') {
+        this.submitLoading = true
+        this.$refs.fileUpload.$refs.fileUpload.submit()
+      }
+    },
+
+    // 调用保存数据接口
+    save() {
+      this.$refs.formRef.validate(valide => {
+        if (!valide) return
+        // 请求参数
+        const params = {
+          ...this.dialogData,
+          ...this.currentFileData,
+          authUserId: this.authUserId
+        }
+
+        saveFile(params)
+          .then(res => {
+            this.dialogVisible = false
+            this.getList()
+            this.$message({
+              type: 'success',
+              message: res.data,
+              duration: 500
+            })
+          })
+          .finally(() => {
+            this.submitLoading = false
+          })
+      })
+    },
+
+    // 删除文件
+    async handleRemoveFile(row) {
+      const text = await this.$confirm('文件删除后不可恢复,确认删除该文件吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).catch(() => {
+        this.$message.info('已取消操作')
+      })
+      if (text !== 'confirm') return
+      removeFile({ fileId: row.fileId }).then(res => {
+        if (res.code !== 0) return
+        this.$message.success(res.data)
+        this.getList()
+      })
+    },
+
+    // 状态改变
+    handleChangeStatus(item) {
+      this.listLoading = true
+      const params = {
+        fileId: item.fileId,
+        status: item.status === 1 ? 0 : 1
+      }
+      changeFileStatus(params)
+        .then(res => {
+          this.$message({
+            message: res.data,
+            duration: 500,
+            type: 'success'
+          })
+          this.getList()
+        })
+        .finally(() => {
+          this.listLoading = false
+        })
+    },
+
+    // 显示dialog对话框
+    handleShowDialog(type, row) {
+      this.dialogTitle = '添加文件'
+      if (type === 'edit') {
+        this.dialogTitle = '修改文件'
+        this.setDialogData(row)
+      }
+      this.dialogVisible = true
+    },
+
+    // 设置表单初始值
+    setDialogData(row) {
+      if (typeof row !== 'object') return
+      setTimeout(() => {
+        for (const key in this.dialogData) {
+          if (Object.hasOwnProperty.call(this.dialogData, key)) {
+            this.dialogData[key] = row[key]
+          }
+        }
+
+        this.resetCurrentFileData(row)
+
+        if (row.filePreviewUrl && row.fileDownloadUrl && row.fileId) {
+          this.fileList = [{ name: row.fileName }]
+        }
+      }, 100)
+    },
+
+    // 对话框关闭前的回调
+    beforeDialogClose() {
+      this.$refs.formRef.resetFields()
+      this.$refs.fileUpload.$refs.fileUpload.abort()
+      this.fileList = []
+      this.dialogData.fileId = ''
+      this.submitLoading = false
+      this.resetCurrentFileData()
+    },
+
+    // 上传文件成功
+    handleUploadFileSuccess({ response, file, fileList }) {
+      console.log(response)
+      this.fileList = fileList
+      this.dialogData.fileName = response.fileName
+      this.currentFileData.filePreviewUrl = response.previewUrl
+      this.currentFileData.fileDownloadUrl = response.downloadUrl
+      this.save()
+    },
+
+    // 移除文件
+    handleRemoveUploadFile({ response, file, fileList }) {
+      this.fileList = []
+      this.dialogData.fileName = ''
+      this.resetCurrentFileData()
+    },
+
+    // 文件选择
+    handleChooseFileChange({ file, fileList }) {
+      this.fileList = fileList
+      this.dialogData.fileName = file.name
+      this.resetCurrentFileData()
+    },
+
+    // 重置选中文件数据
+    resetCurrentFileData(row = {}) {
+      this.currentFileData.filePreviewUrl = row.filePreviewUrl || ''
+      this.currentFileData.fileDownloadUrl = row.fileDownloadUrl || ''
+    },
+
+    // 文件上传之前
+    beforeUpload(file, fileList) {
+      let maxSize = 100
+      // 文件过大
+      if (file.size > maxSize * 1024 * 1024) {
+        this.$message.warning('文件大小超过100MB,请重新选择!')
+        return Promise.reject()
+      }
+      maxSize = 30
+      // 直接上传
+      if (file.size <= maxSize * 1024 * 1024) return true
+      // 用户确认上传
+      return this.$confirm(`您上传的文件大于${maxSize}MB,是否继续上传?文件上传过程中请您耐心等待!`, '文件上传提示', {
+        confirmButtonText: '继续',
+        cancelButtonText: '取消',
+        type: 'warning'
+      })
+        .then(() => {
+          return Promise.resolve()
+        })
+        .catch(() => {
+          this.$message.info('已取消上传')
+          this.submitLoading = false
+          return Promise.reject()
+        })
+    },
+
+    // 表格索引
+    indexMethod(index) {
+      return index + this.listQuery.pageSize * (this.listQuery.pageNum - 1) + 1
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.el-table .cell {
+  overflow: visible;
+}
+.el-badge {
+  margin: 0 6px;
+}
+</style>

+ 278 - 0
src/views/normal/personnel/operate/edit.vue

@@ -0,0 +1,278 @@
+<template>
+  <div class="doctor-edit">
+    <el-form ref="submitForm" class="doctor-edit-form" label-width="140px" :model="formData" :rules="rules">
+      <el-form-item label="操作医师姓名:" prop="doctorName">
+        <el-input v-model="formData.doctorName" type="text" placeholder="请输入操作医师姓名" :maxlength="30" />
+      </el-form-item>
+      <el-form-item label="从业资格证编号:" prop="certificateNo">
+        <el-input v-model="formData.certificateNo" placeholder="请输入从业资格证编号" />
+      </el-form-item>
+      <el-form-item label="所在机构:" prop="clubName">
+        <el-input v-model="formData.clubName" placeholder="请输入机构名称" />
+      </el-form-item>
+      <el-form-item label="操作医师照片:" prop="doctorImage">
+        <el-input v-show="false" v-model="formData.doctorImage" />
+        <upload-image
+          tip="提示:上传操作医师个人照片,建议尺寸:200px * 200px"
+          :image-list="doctorImageList"
+          :before-upload="beforeDoctorImageUpload"
+          @success="uploadDoctorImageSuccess"
+          @remove="handleDoctorImageRemove"
+        />
+      </el-form-item>
+      <el-form-item label="轮播图:" prop="banner">
+        <el-input v-show="false" v-model="formData.banner" />
+        <upload-image
+          tip="至少上传一张机构门店图,最多上传6张;建议尺寸:542px * 542px"
+          :image-list="bannerList"
+          :limit="6"
+          :before-upload="beforeBannerUpload"
+          @success="uploadBannerSuccess"
+          @remove="handleBannerRemove"
+        />
+      </el-form-item>
+
+      <el-form-item label="操作医师标签">
+        <div class="tag-control">
+          <el-input />
+          <el-button type="primary">添加</el-button>
+        </div>
+        <div class="tag-list">
+          <el-tag closable>医师标签</el-tag>
+        </div>
+      </el-form-item>
+
+      <el-form-item label="自定义属性">
+        <doctor-custom-items ref="customItems" @change="customItemsChange" />
+      </el-form-item>
+
+      <el-form-item label="具备操作资格设备:" prop="equipment">
+        <el-input v-show="false" v-model="formData.equipment" />
+        <device-section v-if="!isRequest" ref="subForm" :list="deviceList" @change="deviceListDataChange" />
+      </el-form-item>
+
+    </el-form>
+    <div class="submit-btn">
+      <el-button type="primary" @click="submit">保存</el-button>
+      <el-button type="warning" @click="$_back()">返回</el-button>
+    </div>
+  </div>
+</template>
+
+<script>
+import { Promise } from 'jszip/lib/external'
+import { mapGetters } from 'vuex'
+import DeviceSection from '@/views/components/DeviceSection/index.vue'
+import DoctorCustomItems from '@/views/components/DoctorCustomItems/index.vue'
+import UploadImage from '@/components/UploadImage'
+import { doctorSave, doctorFormData } from '@/api/doctor'
+
+export default {
+  components: { DeviceSection, UploadImage, DoctorCustomItems },
+  data() {
+    var validateCertificateNo = (rule, value, callback) => {
+      if (value === '') {
+        callback(new Error('从业资格证编号不能为空'))
+      } else {
+        if (/^[a-z|A-Z|0-9]{1,}$/.test(value)) {
+          callback()
+        } else {
+          callback(new Error('从业资格证编号格式不正确'))
+        }
+      }
+    }
+
+    return {
+      isRequest: true,
+      editType: 'add',
+      doctorId: '',
+      point: {},
+      disabled: false,
+      formData: {
+        doctorName: '',
+        certificateNo: '',
+        clubName: '',
+        banner: '',
+        doctorImage: '',
+        equipment: ''
+      },
+      rules: {
+        doctorName: [
+          { required: true, message: '医师姓名不能为空', trigger: ['blur', 'change'] },
+          { maxlength: 30, message: '最大长度为30个字符', trigger: ['blur', 'change'] }
+        ],
+        certificateNo: [{ required: true, validator: validateCertificateNo, trigger: ['blur', 'change'] }],
+        clubName: [{ required: true, message: '所在机构不能为空', trigger: ['blur', 'change'] }],
+        doctorImage: [{ required: true, message: '请上传医师照片', trigger: 'change' }],
+        banner: [{ required: true, message: '请上传至少一张轮播图', trigger: 'change' }],
+        equipment: [{ required: true, message: '具备操作资格设备不能为空', trigger: 'change' }]
+      },
+      // logo图片列表
+      doctorImageList: [],
+      // banner图片列表
+      bannerList: [],
+      // 资格仪器列表
+      deviceList: []
+    }
+  },
+  computed: {
+    ...mapGetters(['token', 'authUserId']),
+    // 图片上传
+    action() {
+      return process.env.VUE_APP_UPLOAD_API + '/upload/image'
+    },
+    headers() {
+      return {
+        'X-Token': this.token
+      }
+    }
+  },
+  created() {
+    this.doctorId = parseInt(this.$route.query.id)
+    this.editType = this.$route.query.type || 'edit'
+    this.initFormData()
+  },
+  methods: {
+
+    // 自定义字段变化
+    customItemsChange(list) {
+      console.log(list)
+    },
+
+    // 具备操作资格设备列表数据变化
+    deviceListDataChange(list) {
+      this.deviceList = list
+      this.formData.equipment = list.length || ''
+    },
+
+    // 表单数据回显
+    initFormData() {
+      if (this.editType !== 'edit') {
+        this.isRequest = false
+        return
+      }
+      doctorFormData({ doctorId: this.doctorId }).then(res => {
+        this.setFormData(res.data)
+      })
+    },
+
+    // 设置表单数据
+    setFormData(data) {
+      this.formData.doctorName = data.doctorName
+      this.formData.certificateNo = data.certificateNo
+      this.formData.clubName = data.clubName
+      this.formData.doctorImage = data.doctorImage
+
+      this.formData.banner = data.bannerList.length
+      this.formData.equipmentList = data.equipmentList.length
+      this.doctorImageList.push({ url: data.doctorImage, name: 'doctor' })
+      this.bannerList = data.bannerList.map(item => ({ url: item, name: '' }))
+      this.deviceList = data.equipmentList
+
+      this.isRequest = false
+    },
+
+    // 提交表单验证
+    submit() {
+      Promise.all([
+        this.$refs.subForm.valideAllForm(),
+        this.$refs.submitForm.validate(),
+        this.$refs.customItems.valideAllForm()
+      ]).then(res => {
+        this.save()
+      })
+    },
+
+    // 保存
+    save() {
+      const authUserId = this.authUserId
+      const data = {
+        authUserId,
+        createBy: authUserId,
+        doctorId: this.doctorId,
+        doctorName: this.formData.doctorName,
+        certificateNo: this.formData.certificateNo,
+        clubName: this.formData.clubName,
+        doctorImage: this.formData.doctorImage
+      }
+
+      console.log(this.formData)
+
+      data.bannerList = this.bannerList.map(item => item.response ? item.response.data : item.url)
+      data.equipmentList = this.deviceList.map(item => ({ equipmentName: item.equipmentName, brand: item.brand, image: item.image }))
+      doctorSave(data).then(res => {
+        this.$message.success('保存成功')
+        this.$store.dispatch('tagsView/delView', this.$route)
+        this.$router.push('/doctor/list')
+      })
+    },
+
+    // 医师照片上传
+    uploadDoctorImageSuccess({ response, file, fileList }) {
+      this.doctorImageList = fileList
+      this.formData.doctorImage = response.data
+    },
+    handleDoctorImageRemove({ file, fileList }) {
+      this.doctorImageList = fileList
+      this.formData.doctorImage = ''
+    },
+    beforeDoctorImageUpload(file) {
+      const flag = file.size / 1024 / 1024 < 1
+      if (!flag) {
+        this.$message.error('上传医师照片大小不能超过 1MB!')
+      }
+      return flag
+    },
+
+    // banner上传
+    uploadBannerSuccess({ response, file, fileList }) {
+      this.bannerList = fileList
+      this.formData.banner = fileList.length || ''
+    },
+    handleBannerRemove({ file, fileList }) {
+      this.bannerList = fileList
+      this.formData.banner = fileList.length || ''
+    },
+    beforeBannerUpload(file) {
+      const flag = file.size / 1024 / 1024 < 1
+      if (!flag) {
+        this.$message.error('上传banner图片大小不能超过 1MB!')
+      }
+      return flag
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.doctor-edit{
+  margin-bottom: 80px;
+}
+
+.doctor-edit-form {
+  width: 650px;
+  margin: 0 auto;
+  margin-top: 80px;
+}
+.submit-btn {
+  text-align: center;
+  .el-button {
+    width: 140px;
+  }
+}
+
+.tag-control{
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  .el-input{
+    flex: 1;
+    margin-right: 16px;
+  }
+}
+
+.tag-list{
+    padding-top: 8px;
+}
+
+</style>

+ 3 - 3
src/views/normal/doctor/index.vue → src/views/normal/personnel/operate/index.vue

@@ -2,8 +2,8 @@
   <div class="app-container">
     <div class="filter-container">
       <div class="filter-control">
-        <span>医师姓名:</span>
-        <el-input v-model="listQuery.doctorName" placeholder="医师姓名" @keyup.enter.native="handleFilter" />
+        <span>操作医师姓名:</span>
+        <el-input v-model="listQuery.doctorName" placeholder="操作医师姓名" @keyup.enter.native="handleFilter" />
       </div>
       <div class="filter-control">
         <span>审核状态:</span>
@@ -40,7 +40,7 @@
       header-row-class-name="tableHeader"
     >
       <el-table-column label="序号" :index="indexMethod" type="index" align="center" width="80" />
-      <el-table-column label="医生姓名" align="center" prop="doctorName" />
+      <el-table-column label="操作医师姓名" align="center" prop="doctorName" />
       <el-table-column label="从业资格证编号" align="center" prop="certificateNo" />
 
       <el-table-column label="审核状态" width="220px" align="center">

+ 33 - 11
src/views/normal/doctor/edit.vue → src/views/normal/personnel/training/edit.vue

@@ -32,9 +32,18 @@
         />
       </el-form-item>
 
-      <el-form-item label="具备操作资格设备:" prop="equipment">
-        <el-input v-show="false" v-model="formData.equipment" />
-        <device-section v-if="!isRequest" ref="subForm" :list="deviceList" @change="deviceListDataChange" />
+      <el-form-item label="操作医师标签">
+        <div class="tag-control">
+          <el-input />
+          <el-button type="primary">添加</el-button>
+        </div>
+        <div class="tag-list">
+          <el-tag closable>医师标签</el-tag>
+        </div>
+      </el-form-item>
+
+      <el-form-item label="自定义属性">
+        <doctor-custom-items ref="customItems" @change="customItemsChange" />
       </el-form-item>
 
     </el-form>
@@ -48,12 +57,12 @@
 <script>
 import { Promise } from 'jszip/lib/external'
 import { mapGetters } from 'vuex'
-import DeviceSection from '@/views/components/DeviceSection/index.vue'
 import UploadImage from '@/components/UploadImage'
+import DoctorCustomItems from '@/views/components/DoctorCustomItems/index.vue'
 import { doctorSave, doctorFormData } from '@/api/doctor'
 
 export default {
-  components: { DeviceSection, UploadImage },
+  components: { DoctorCustomItems, UploadImage },
   data() {
     var validateCertificateNo = (rule, value, callback) => {
       if (value === '') {
@@ -118,10 +127,10 @@ export default {
     this.initFormData()
   },
   methods: {
-    // 具备操作资格设备列表数据变化
-    deviceListDataChange(list) {
-      this.deviceList = list
-      this.formData.equipment = list.length || ''
+
+    // 自定义字段变化
+    customItemsChange(list) {
+      console.log(list)
     },
 
     // 表单数据回显
@@ -146,14 +155,13 @@ export default {
       this.formData.equipmentList = data.equipmentList.length
       this.doctorImageList.push({ url: data.doctorImage, name: 'doctor' })
       this.bannerList = data.bannerList.map(item => ({ url: item, name: '' }))
-      this.deviceList = data.equipmentList
 
       this.isRequest = false
     },
 
     // 提交表单验证
     submit() {
-      Promise.all([this.$refs.subForm.valideAllForm(), this.$refs.submitForm.validate()]).then(res => {
+      Promise.all([this.$refs.customItems.valideAllForm(), this.$refs.submitForm.validate()]).then(res => {
         this.save()
       })
     },
@@ -236,4 +244,18 @@ export default {
   }
 }
 
+.tag-control{
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  .el-input{
+    flex: 1;
+    margin-right: 16px;
+  }
+}
+
+.tag-list{
+    padding-top: 8px;
+}
+
 </style>

+ 198 - 0
src/views/normal/personnel/training/index.vue

@@ -0,0 +1,198 @@
+<template>
+  <div class="app-container">
+    <div class="filter-container">
+      <div class="filter-control">
+        <span>培训医师姓名:</span>
+        <el-input v-model="listQuery.doctorName" placeholder="培训医师姓名" @keyup.enter.native="handleFilter" />
+      </div>
+      <div class="filter-control">
+        <span>审核状态:</span>
+        <el-select v-model="listQuery.auditStatus" placeholder="审核状态" clearable @change="getList">
+          <el-option label="全部" value="" />
+          <el-option label="待审核" :value="2" />
+          <el-option label="审核通过" :value="1" />
+          <el-option label="审核未通过" :value="0" />
+        </el-select>
+      </div>
+      <div class="filter-control">
+        <span>上线状态:</span>
+        <el-select v-model="listQuery.status" placeholder="上线状态" clearable @change="getList">
+          <el-option label="全部" value="" />
+          <el-option label="已上线" :value="1" />
+          <el-option label="待上线" :value="2" />
+          <el-option label="未上线" :value="0" />
+        </el-select>
+      </div>
+      <div class="filter-control">
+        <permission-button type="primary" @click="getList">查询</permission-button>
+        <permission-button type="primary" @click="$_navigationTo('add?type=add')">添加</permission-button>
+      </div>
+    </div>
+    <!-- 表格区域 -->
+    <el-table
+      :key="tableKey"
+      v-loading="listLoading"
+      :data="list"
+      border
+      fit
+      highlight-current-row
+      style="width: 100%;"
+      header-row-class-name="tableHeader"
+    >
+      <el-table-column label="序号" :index="indexMethod" type="index" align="center" width="80" />
+      <el-table-column label="培训医师姓名" align="center" prop="doctorName" />
+      <el-table-column label="从业资格证编号" align="center" prop="certificateNo" />
+
+      <el-table-column label="审核状态" width="220px" align="center">
+        <template slot-scope="{ row }">
+          <el-tag v-if="row.auditStatus === 2" size="small" type="warning">待审核</el-tag>
+          <el-tag v-if="row.auditStatus === 1" size="small" type="success">审核通过</el-tag>
+          <!-- 未通过原因展示 -->
+          <template v-if="row.auditStatus === 0">
+            <!-- <span class="status danger">审核未通过&nbsp;</span> -->
+            <el-popover placement="top-start" title="审核说明" width="400" trigger="hover" :content="row.invalidReason">
+              <el-tag slot="reference" size="small" type="danger" class="reason">
+                <span>审核未通过</span>
+                <span class="el-icon-question status danger " />
+              </el-tag>
+            </el-popover>
+            <!-- 未通过原因展示END -->
+          </template>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="上线状态" width="140px" align="center">
+        <template slot-scope="{ row }">
+          <!-- 只有审核通过了才能操作上下线 auditStatus :审核状态 -->
+          <template v-if="row.auditStatus === 1">
+            <template v-if="row.status === 0">
+              <span style="margin-right:10px;" class="status danger">已下线</span>
+              <permission-button type="primary" size="mini" @click="handleChangeStatus(row)">上线</permission-button>
+            </template>
+            <template v-else>
+              <span style="margin-right:10px;" class="status success ">已上线</span>
+              <permission-button type="info" size="mini" @click="handleChangeStatus(row)">下线</permission-button>
+            </template>
+          </template>
+          <template v-else-if="row.auditStatus === 2">
+            <!-- <el-tag type="warning">待上线</el-tag> -->
+            <span style="margin-right:10px;" class="status warning">待上线</span>
+          </template>
+          <template v-else>
+            <!-- <el-tag type="warning">待上线</el-tag> -->
+            <span style="margin-right:10px;" class="status danger">已下线</span>
+          </template>
+        </template>
+      </el-table-column>
+      <el-table-column label="创建时间" class-name="status-col" width="300px" align="center">
+        <template slot-scope="{ row }">
+          <span>{{ row.createTime | formatTime }}</span>
+        </template>
+      </el-table-column>
+
+      <!-- <el-table-column v-if="false" label="创建人" width="180px" align="center" prop="createBy" /> -->
+      <el-table-column label="操作" align="center" width="240px" class-name="small-padding fixed-width">
+        <template slot-scope="{ row }">
+          <permission-button type="primary" size="mini" @click="$_navigationTo(`edit?type=edit&id=${row.doctorId}`)">
+            编辑
+          </permission-button>
+          <permission-button type="danger" size="mini" @click="handleRemoveDoctor(row)">
+            删除
+          </permission-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 页码 -->
+    <pagination
+      v-show="total > 0"
+      :total="total"
+      :page.sync="listQuery.pageNum"
+      :limit.sync="listQuery.pageSize"
+      @pagination="getList"
+    />
+  </div>
+</template>
+
+<script>
+import PermissionButton from '@/views/components/PermissionButton'
+import Pagination from '@/components/Pagination' // secondary package based on el-pagination
+import { mapGetters } from 'vuex'
+import { formatDate } from '@/utils'
+import { fetchDoctorList, removeDoctor, doctorStatusChange } from '@/api/doctor'
+
+export default {
+  components: { Pagination, PermissionButton },
+  filters: {
+    formatTime(time) {
+      if (!time) {
+        return ''
+      }
+      return formatDate(time, 'yyyy-MM-DD HH:mm:ss')
+    }
+  },
+  data() {
+    return {
+      listLoading: false,
+      tableKey: 0,
+      total: 0,
+      listQuery: {
+        listType: 1,
+        doctorName: '',
+        auditStatus: '',
+        status: '',
+        pageNum: 1,
+        pageSize: 10
+      },
+      list: []
+    }
+  },
+  computed: {
+    ...mapGetters(['authUserId'])
+  },
+  created() {
+    this.getList()
+  },
+  methods: {
+    // 获取列表
+    getList() {
+      this.listQuery.authUserId = this.authUserId
+      fetchDoctorList(this.listQuery).then(res => {
+        console.log(res)
+        this.list = res.data.list
+        this.total = res.data.total
+      })
+    },
+    // 修改状态
+    handleChangeStatus(row) {
+      const status = row.status === 1 ? 0 : 1
+
+      doctorStatusChange({ doctorId: row.doctorId, status }).then(res => {
+        this.$message.success('状态修改成功')
+        this.getList()
+      })
+    },
+    // 删除
+    async handleRemoveDoctor(row) {
+      const text = await this.$confirm('确认删除该医师信息吗?删除后不可恢复', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).catch(() => {
+        this.$message.info('已取消操作')
+      })
+      if (text !== 'confirm') return
+
+      removeDoctor({ doctorId: row.doctorId }).then(res => {
+        this.$message.success('删除成功')
+        this.getList()
+      })
+    },
+    // 表格索引
+    indexMethod(index) {
+      return index + this.listQuery.pageSize * (this.listQuery.pageNum - 1) + 1
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped></style>