Sfoglia il codice sorgente

服务号口令看课/ 防sql注入/上传校验/ 等安全问题

wangxy 1 giorno fa
parent
commit
e8ef08db33
61 ha cambiato i file con 700 aggiunte e 80 eliminazioni
  1. 8 0
      src/api/company/company.js
  2. 9 0
      src/api/course/userCoursePeriod.js
  3. 17 0
      src/api/his/company.js
  4. 5 0
      src/components/Editor/wang.vue
  5. 1 0
      src/components/FileUpload/index.vue
  6. 1 0
      src/components/ImageUpload/index.vue
  7. 3 0
      src/components/Material/index.vue
  8. 3 0
      src/components/Material/single.vue
  9. 22 2
      src/utils/common.js
  10. 3 0
      src/views/chat/chatKeyword/index.vue
  11. 3 0
      src/views/chat/chatRole/index.vue
  12. 1 0
      src/views/company/companyUser/indexAll.vue
  13. 3 0
      src/views/company/components/companyProfit.vue
  14. 4 1
      src/views/components/course/userCourseCatalogDetails.vue
  15. 6 0
      src/views/course/courseFinishTemp/index.vue
  16. 6 0
      src/views/course/courseFinishTempParent/index.vue
  17. 43 5
      src/views/course/coursePlaySourceConfig/index.vue
  18. 7 0
      src/views/course/reward/index.vue
  19. 70 15
      src/views/course/userCoursePeriod/index.vue
  20. 1 39
      src/views/course/userCoursePeriod/redPacket.vue
  21. 3 0
      src/views/course/userVideo/index.vue
  22. 3 0
      src/views/his/adv/index.vue
  23. 5 0
      src/views/his/appVersion/index.vue
  24. 3 0
      src/views/his/article/index.vue
  25. 3 0
      src/views/his/articleCate/index.vue
  26. 1 0
      src/views/his/caseArticle/index.vue
  27. 1 0
      src/views/his/chineseMedicine/index.vue
  28. 109 14
      src/views/his/company/index.vue
  29. 3 0
      src/views/his/department/index.vue
  30. 6 0
      src/views/his/doctor/type1.vue
  31. 6 0
      src/views/his/doctor/type2.vue
  32. 4 0
      src/views/his/doctorArticle/index.vue
  33. 3 0
      src/views/his/doctorArticleCate/index.vue
  34. 1 0
      src/views/his/famousPrescribe/index.vue
  35. 4 0
      src/views/his/homeArticle/index.vue
  36. 3 0
      src/views/his/homeCategory/index.vue
  37. 3 0
      src/views/his/hospital/index.vue
  38. 33 1
      src/views/his/integralGoods/index.vue
  39. 28 0
      src/views/his/integralOrder/index.vue
  40. 1 0
      src/views/his/medicatedFood/index.vue
  41. 1 0
      src/views/his/package/index.vue
  42. 3 0
      src/views/his/packageCate/index.vue
  43. 4 0
      src/views/his/store/index.vue
  44. 1 0
      src/views/his/storeProduct/index.vue
  45. 3 0
      src/views/his/storeProductCategory/index.vue
  46. 3 0
      src/views/his/testTemp/index.vue
  47. 1 0
      src/views/his/vessel/index.vue
  48. 4 0
      src/views/hisStore/adv/index.vue
  49. 32 1
      src/views/hisStore/integralGoods/index.vue
  50. 10 0
      src/views/hisStore/store/index.vue
  51. 1 0
      src/views/hisStore/storeProduct/index.vue
  52. 1 0
      src/views/hisStore/storeProductAudit/index.vue
  53. 3 0
      src/views/qw/friendMaterial/index.vue
  54. 1 0
      src/views/qw/sop/ImageUpload.vue
  55. 4 0
      src/views/qw/sopTemp/addSopTemp.vue
  56. 5 0
      src/views/qw/sopTemp/addSopTempOld.vue
  57. 4 0
      src/views/qw/sopTemp/updateSopTemp.vue
  58. 4 0
      src/views/qw/sopTemp/updateSopTemp2.vue
  59. 5 0
      src/views/qw/sopTemp/updateSopTempOld.vue
  60. 91 1
      src/views/system/config/config.vue
  61. 80 1
      src/views/system/config/config2.vue

+ 8 - 0
src/api/company/company.js

@@ -73,6 +73,14 @@ export function getCompanyList() {
     method: 'get'
   })
 }
+
+/** 服务号点播下拉(口令看课):合并点播源 type=2 与 wx.mp.configs */
+export function listCourseMaAppOptions() {
+  return request({
+    url: '/company/company/courseMaAppOptions',
+    method: 'get'
+  })
+}
 export function recharge(data) {
   return request({
     url: '/company/company/recharge',

+ 9 - 0
src/api/course/userCoursePeriod.js

@@ -61,6 +61,15 @@ export function insertCourse(data) {
   })
 }
 
+// 批量将小节转为注册课
+export function batchToRegisterCourse(ids) {
+  return request({
+    url: '/course/period/batchToRegisterCourse',
+    method: 'post',
+    data: ids
+  })
+}
+
 // 新增会员营期
 export function updateCourseTime(data) {
   return request({

+ 17 - 0
src/api/his/company.js

@@ -127,3 +127,20 @@ export function getGatewayCompanyList(companyId) {
     method: 'get'
   })
 }
+
+/** 服务号点播下拉(口令看课) */
+export function listCourseMaAppOptions() {
+  return request({
+    url: '/his/company/courseMaAppOptions',
+    method: 'get'
+  })
+}
+
+/** 口令看课开关 */
+export function changeWatchPasswordEnabled(data) {
+  return request({
+    url: '/his/company/changeWatchPasswordEnabled',
+    method: 'post',
+    data: data
+  })
+}

+ 5 - 0
src/components/Editor/wang.vue

@@ -6,6 +6,7 @@
 
 <script>
 import E from 'wangeditor'
+import { getToken } from '@/utils/auth'
 export default {
   name: 'editoritem',
   data() {
@@ -31,6 +32,9 @@ export default {
       if (this.editor == null) {
         this.editor = new E(that.$refs.editor1)
         this.editor.config.uploadImgServer = this.uploadUrl;
+        this.editor.config.uploadImgHeaders = {
+          Authorization: 'Bearer ' + getToken()
+        };
         this.editor.config.uploadImgMaxSize = 6 * 1024 * 1024 // 2M
         this.editor.config.uploadImgAccept = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp']
         this.editor.config.uploadFileName = 'fileName'
@@ -61,6 +65,7 @@ export default {
 
             const xhr = new XMLHttpRequest();
             xhr.open('POST', that.uploadUrl);
+            xhr.setRequestHeader('Authorization', 'Bearer ' + getToken());
 
             xhr.onload = function () {
               if (xhr.status >= 200 && xhr.status < 300) {

+ 1 - 0
src/components/FileUpload/index.vue

@@ -2,6 +2,7 @@
   <div class="upload-file">
     <el-upload
       :action="uploadFileUrl"
+      :headers="headers"
       :before-upload="handleBeforeUpload"
       :file-list="fileList"
       :limit="limit"

+ 1 - 0
src/components/ImageUpload/index.vue

@@ -2,6 +2,7 @@
   <div class="component-upload-image">
     <el-upload
       :action="uploadImgUrl"
+      :headers="headers"
       list-type="picture-card"
       :on-success="handleUploadSuccess"
       :before-upload="handleBeforeUpload"

+ 3 - 0
src/components/Material/index.vue

@@ -72,6 +72,7 @@
                 <el-col :span="12" style="text-align: right;">
                   <el-upload
                     :action="uploadUrl"
+                    :headers="uploadHeaders"
                     :file-list="[]"
                     :on-progress="handleProgress"
                     :before-upload="beforeUpload"
@@ -140,6 +141,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import { listMaterial, getMaterial, delMaterial, addMaterial, updateMaterial, exportMaterial } from "@/api/store/material";
 import { getAllMaterialGroup,listMaterialGroup, getMaterialGroup, delMaterialGroup, addMaterialGroup, updateMaterialGroup, exportMaterialGroup } from "@/api/store/materialGroup";
 import { Loading } from 'element-ui';
@@ -181,6 +183,7 @@ export default {
   },
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       finalQuality:1,
       uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
       dialogVisible: false,

+ 3 - 0
src/components/Material/single.vue

@@ -63,6 +63,7 @@
                 <el-col :span="12" style="text-align: right;">
                   <el-upload
                     :action="uploadUrl"
+                    :headers="uploadHeaders"
                     :file-list="[]"
                     :on-progress="handleProgress"
                     :before-upload="beforeUpload"
@@ -131,6 +132,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import { listMaterial, getMaterial, delMaterial, addMaterial, updateMaterial, exportMaterial } from "@/api/store/material";
 import { getAllMaterialGroup,listMaterialGroup, getMaterialGroup, delMaterialGroup, addMaterialGroup, updateMaterialGroup, exportMaterialGroup } from "@/api/store/materialGroup";
 import { Loading } from 'element-ui';
@@ -174,6 +176,7 @@ export default {
   },
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       finalQuality:1,
       myValue: this.value,
       uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",

+ 22 - 2
src/utils/common.js

@@ -2,6 +2,8 @@
  * 通用js方法封装处理
  * Copyright (c) 2019 ruoyi
  */
+import axios from 'axios'
+import { getToken } from '@/utils/auth'
 
 const baseURL = process.env.VUE_APP_BASE_API
 
@@ -112,9 +114,27 @@ export function selectDictLabels(datas, value, separator) {
 	return actions.join('').substring(0, actions.join('').length - 1);
 }
 
-// 通用下载方法
+// 通用下载方法(需登录 Token,禁止用 window.location 匿名拉文件)
 export function download(fileName) {
-	window.location.href = baseURL + "/common/download?fileName=" + encodeURI(fileName) + "&delete=" + false;
+	const name = (fileName || '').toString().split('/').pop() || 'download'
+	axios({
+		method: 'get',
+		url: baseURL + '/common/download',
+		params: { fileName: fileName, delete: false },
+		responseType: 'blob',
+		headers: { Authorization: 'Bearer ' + getToken() }
+	}).then(res => {
+		const blob = new Blob([res.data])
+		const a = document.createElement('a')
+		a.href = URL.createObjectURL(blob)
+		a.download = decodeURIComponent(name)
+		document.body.appendChild(a)
+		a.click()
+		document.body.removeChild(a)
+		URL.revokeObjectURL(a.href)
+	}).catch(() => {
+		console.error('文件下载失败')
+	})
 }
 
 // 字符串格式化(%s )

+ 3 - 0
src/views/chat/chatKeyword/index.vue

@@ -132,6 +132,7 @@
             v-model="form.qwCode"
             class="avatar-uploader"
             :action="uploadUrl"
+                    :headers="uploadHeaders"
             :show-file-list="false"
             :on-success="handleAvatarSuccess"
             :before-upload="beforeAvatarUpload">
@@ -149,6 +150,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import { listChatKeyword, getChatKeyword, delChatKeyword, addChatKeyword, updateChatKeyword, exportChatKeyword } from "@/api/chat/chatKeyword";
 import { getAllRoleList } from "@/api/chat/chatRole";
 import Editor from '@/components/Editor/wang';
@@ -157,6 +159,7 @@ export default {
   name: "ChatKeyword",
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
       baseUrl: process.env.VUE_APP_BASE_API,
       typeOptions:[],

+ 3 - 0
src/views/chat/chatRole/index.vue

@@ -135,6 +135,7 @@
             v-model="form.kfHeadUrl"
             class="avatar-uploader"
             :action="uploadUrl"
+                    :headers="uploadHeaders"
             :show-file-list="false"
             :on-success="handleAvatarSuccess"
             :before-upload="beforeAvatarUpload">
@@ -168,6 +169,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import { listChatRole, getChatRole, delChatRole, addChatRole, updateChatRole, exportChatRole } from "@/api/chat/chatRole";
 import { getUrl } from "@/api/chat/chatUpload";
 import QRCode from 'qrcodejs2'
@@ -176,6 +178,7 @@ export default {
   name: "ChatRole",
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       companyList:[],
       user:null,
       qrOpen:false,

+ 1 - 0
src/views/company/companyUser/indexAll.vue

@@ -1598,6 +1598,7 @@ export default {
         const response = await axios.post(this.uploadUrl, formData, {
           headers: {
             'Content-Type': 'multipart/form-data',
+            'Authorization': 'Bearer ' + getToken(),
           },
           onUploadProgress: progressEvent => {
             const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);

+ 3 - 0
src/views/company/components/companyProfit.vue

@@ -115,6 +115,7 @@
               v-model="form.imgUrl"
               class="avatar-uploader"
               :action="uploadUrl"
+                    :headers="uploadHeaders"
               :show-file-list="false"
               :on-success="handleSuccess"
               :before-upload="beforeUpload">
@@ -135,12 +136,14 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import { audit1,audit2,audit3, getCompanyProfit, delCompanyProfit, addCompanyProfit, updateCompanyProfit, exportCompanyProfit } from "@/api/company/companyProfit";
 
 export default {
   name: "profit",
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       type:null,
       uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
       baseUrl: process.env.VUE_APP_BASE_API,

+ 4 - 1
src/views/components/course/userCourseCatalogDetails.vue

@@ -105,7 +105,8 @@
         </el-form-item>
 
         <el-form-item label="视频缩略图" prop="thumbnail">
-          <el-upload v-model="form.thumbnail" class="avatar-uploader" :action="uploadUrl" :show-file-list="false"
+          <el-upload v-model="form.thumbnail" class="avatar-uploader" :action="uploadUrl"
+                    :headers="uploadHeaders" :show-file-list="false"
                      :on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload">
             <img v-if="form.thumbnail" :src="form.thumbnail" class="avatar" width="300px">
             <i v-else class="el-icon-plus avatar-uploader-icon"></i>
@@ -385,6 +386,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import {
   addUserCourseVideo,
   delUserCourseVideo,
@@ -412,6 +414,7 @@ export default {
   components: { Template, VideoUpload, QuestionBank, CourseWatchComment, CourseProduct, draggable },
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       duration: null,
       packageList: [],
       //课题

+ 6 - 0
src/views/course/courseFinishTemp/index.vue

@@ -264,6 +264,7 @@
                           v-model="item.fileUrl"
                           class="avatar-uploader"
                           :action="uploadUrl"
+                    :headers="uploadHeaders"
                           :show-file-list="false"
                           :on-success="(res, file) => handleAvatarSuccessFile(res, file, item)"
                           :before-upload="beforeAvatarUploadFile">
@@ -282,6 +283,7 @@
                           v-model="item.videoUrl"
                           class="avatar-uploader"
                           :action="uploadUrl"
+                    :headers="uploadHeaders"
                           :show-file-list="false"
                           :on-success="(res, file) => handleAvatarSuccessVideo(res, file, item)"
                           :before-upload="beforeAvatarUploadVideo">
@@ -364,6 +366,7 @@
                           v-model="item.fileUrl"
                           class="avatar-uploader"
                           :action="uploadUrl"
+                    :headers="uploadHeaders"
                           :show-file-list="false"
                           :on-success="(res, file) => handleAvatarSuccessFile(res, file, item)"
                           :before-upload="beforeAvatarUploadFile">
@@ -382,6 +385,7 @@
                           v-model="item.videoUrl"
                           class="avatar-uploader"
                           :action="uploadUrl"
+                    :headers="uploadHeaders"
                           :show-file-list="false"
                           :on-success="(res, file) => handleAvatarSuccessVideo(res, file, item)"
                           :before-upload="beforeAvatarUploadVideo">
@@ -448,6 +452,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import {
   listCourseFinishTemp,
   getCourseFinishTemp,
@@ -466,6 +471,7 @@ export default {
   components: {ImageUpload},
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       //上传语音的遮罩层
       voiceLoading: false,
       uploadUrl: process.env.VUE_APP_BASE_API + "/common/uploadOSS2",

+ 6 - 0
src/views/course/courseFinishTempParent/index.vue

@@ -207,6 +207,7 @@
                           v-model="item.fileUrl"
                           class="avatar-uploader"
                           :action="uploadUrl"
+                    :headers="uploadHeaders"
                           :show-file-list="false"
                           :on-success="(res, file) => handleAvatarSuccessFile(res, file, item)"
                           :before-upload="beforeAvatarUploadFile">
@@ -225,6 +226,7 @@
                           v-model="item.videoUrl"
                           class="avatar-uploader"
                           :action="uploadUrl"
+                    :headers="uploadHeaders"
                           :show-file-list="false"
                           :on-success="(res, file) => handleAvatarSuccessVideo(res, file, item)"
                           :before-upload="beforeAvatarUploadVideo">
@@ -307,6 +309,7 @@
                           v-model="item.fileUrl"
                           class="avatar-uploader"
                           :action="uploadUrl"
+                    :headers="uploadHeaders"
                           :show-file-list="false"
                           :on-success="(res, file) => handleAvatarSuccessFile(res, file, item)"
                           :before-upload="beforeAvatarUploadFile">
@@ -325,6 +328,7 @@
                           v-model="item.videoUrl"
                           class="avatar-uploader"
                           :action="uploadUrl"
+                    :headers="uploadHeaders"
                           :show-file-list="false"
                           :on-success="(res, file) => handleAvatarSuccessVideo(res, file, item)"
                           :before-upload="beforeAvatarUploadVideo">
@@ -391,6 +395,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import {getCompanyList} from "@/api/company/company";
 import { listCourseFinishTempParent, getCourseFinishTempParent, delCourseFinishTempParent, addCourseFinishTempParent, updateCourseFinishTempParent, exportCourseFinishTempParent } from "@/api/course/courseFinishTempParent";
 import {courseList} from '@/api/qw/sop'
@@ -403,6 +408,7 @@ export default {
   components: { ImageUpload },
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       voiceLoading: false,
       uploadUrl: process.env.VUE_APP_BASE_API + "/common/uploadOSS2",
       uploadUrlByVoice: process.env.VUE_APP_BASE_API + "/common/uploadOSSByHOOKVoice",

+ 43 - 5
src/views/course/coursePlaySourceConfig/index.vue

@@ -1,5 +1,12 @@
 <template>
   <div class="app-container">
+<!--    <el-alert-->
+<!--      title="口令看课说明:公司「服务号点播配置」下拉取本页类型为公众号/服务号(type=2)且状态=正常的记录。请先新增服务号点播源,再回公司管理绑定。"-->
+<!--      type="info"-->
+<!--      :closable="false"-->
+<!--      show-icon-->
+<!--      style="margin-bottom: 12px;"-->
+<!--    />-->
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
       <el-form-item label="名称" prop="name">
         <el-input
@@ -39,6 +46,21 @@
           size="small"
           @keyup.enter.native="handleQuery"
         />
+      </el-form-item>
+      <el-form-item label="类型" prop="type">
+        <el-select
+          v-model="queryParams.type"
+          placeholder="请选择类型"
+          clearable
+          size="small"
+        >
+          <el-option
+            v-for="item in typesOptions"
+            :key="item.dictValue"
+            :label="item.dictLabel"
+            :value="Number(item.dictValue)"
+          />
+        </el-select>
       </el-form-item>
             <el-form-item label="状态" prop="status">
         <el-select
@@ -323,8 +345,11 @@
               v-for="item in typesOptions"
               :key="item.dictValue"
               :label="item.dictLabel"
-              :value="item.dictValue"/>
+              :value="Number(item.dictValue)"/>
           </el-select>
+          <div style="color:#909399;font-size:12px;line-height:1.4;margin-top:4px;">
+            口令看课服务号请选「服务号/公众号」(字典值=2),状态选「正常」后才会出现在公司「服务号点播配置」下拉中(显示中文名称)。
+          </div>
         </el-form-item>
         <el-form-item label="原始id" prop="originalId">
           <el-input v-model="form.originalId" placeholder="请输入原始id" />
@@ -487,7 +512,8 @@ export default {
         pageSize: 10,
         name: null,
         appid: null,
-        status: null
+        status: null,
+        type: null
       },
       // 协议配置相关
       agreementDialogVisible: false,
@@ -611,6 +637,11 @@ export default {
     allList().then(e => {
       this.formatterCompanyOptions = e.rows;
     });
+    // 从公司管理「去配置」带 type=2 进入时,默认筛服务号
+    if (this.$route.query && this.$route.query.type != null && this.$route.query.type !== '') {
+      const t = this.$route.query.type
+      this.queryParams.type = isNaN(Number(t)) ? t : Number(t)
+    }
     this.getList();
   },
   methods: {
@@ -872,7 +903,11 @@ export default {
     handleAdd() {
       this.reset()
       this.open = true
-        this.form.isMall = 0;
+      this.form.isMall = 0;
+      // 从公司管理带 type=2 进入时,新增默认服务号类型
+      if (this.$route.query && this.$route.query.type != null && this.$route.query.type !== '') {
+        this.form.type = Number(this.$route.query.type)
+      }
       this.title = "添加小程序配置"
     },
     handleUpdate(row) {
@@ -881,7 +916,7 @@ export default {
       get(id).then(response => {
         this.form = {
           ...response.data,
-          type: response.data.type.toString(),
+          type: Number(response.data.type),
           authTxtUrl: response.data.authTxtUrl || null
         }
         if(!!this.form.setCompanyIds){
@@ -934,6 +969,9 @@ export default {
           }else{
             this.form.setCompanyIds = "";
           }
+          // 保证类型入库为数字:1小程序 2服务号/公众号 3自定义
+          this.form.type = Number(this.form.type)
+          this.form.status = this.form.status == null ? 0 : Number(this.form.status)
           console.log(this.form);
           if (this.form.id != null) {
             update(this.form).then(response => {
@@ -977,7 +1015,7 @@ export default {
         token: 'cbnd7lJvkripVOpyTFAna6NAWCxCrvC',
         aesKey: 'HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E',
         msgDataFormat: 'JSON',
-        type: '1',
+        type: 1,
         status: 0,
         authTxtUrl: null
       }

+ 7 - 0
src/views/course/reward/index.vue

@@ -164,6 +164,7 @@
                 v-model="form.closeChestUrl"
                 class="avatar-uploader"
                 :action="uploadUrl"
+                    :headers="uploadHeaders"
                 :show-file-list="false"
                 :on-success="handleSuccessClose"
                 :before-upload="beforeUpload">
@@ -178,6 +179,7 @@
                 v-model="form.openChestUrl"
                 class="avatar-uploader"
                 :action="uploadUrl"
+                    :headers="uploadHeaders"
                 :show-file-list="false"
                 :on-success="handleSuccessOpen"
                 :before-upload="beforeUpload">
@@ -258,6 +260,7 @@
                   <el-upload
                     class="spin-icon-uploader"
                     :action="uploadUrl"
+                    :headers="uploadHeaders"
                     :show-file-list="false"
                     :before-upload="beforeUpload"
                     accept="image/*"
@@ -366,6 +369,7 @@
                   <el-upload
                     class="spin-icon-uploader"
                     :action="uploadUrl"
+                    :headers="uploadHeaders"
                     :show-file-list="false"
                     :before-upload="beforeUpload"
                     accept="image/*"
@@ -478,6 +482,7 @@
                   <el-upload
                     class="spin-icon-uploader"
                     :action="uploadUrl"
+                    :headers="uploadHeaders"
                     :show-file-list="false"
                     :before-upload="beforeUpload"
                     accept="image/*"
@@ -778,6 +783,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import { listReward, getReward, delReward, addReward, updateReward, exportReward } from "@/api/course/reward";
 import CouponComponent from '@/views/components/his/couponComponent.vue'
 import RewardGoodsComponent from '@/views/components/course/rewardGoodsComponent.vue'
@@ -792,6 +798,7 @@ export default {
   },
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       finalQuality:1,
       uploadUrl: process.env.VUE_APP_BASE_API+"/common/uploadOSS",
       detailVisible: false,

+ 70 - 15
src/views/course/userCoursePeriod/index.vue

@@ -375,7 +375,7 @@
 <!--    </el-dialog>-->
 
     <!-- 添加课程对话框-->
-    <el-dialog :title="course.addMode === 'insert' ? '插入课程' : '添加课程'" :visible.sync="course.addOpen" width="500px" append-to-body>
+    <el-dialog :title="course.addMode === 'insert' ? '插入课程' : '添加课程'" :visible.sync="course.addOpen" width="620px" append-to-body>
       <el-form ref="courseAddForm" :model="course.form" :rules="courseAddRules" label-width="100px">
         <el-form-item label="课程" prop="courseId">
           <el-select filterable  v-model="course.form.courseId" placeholder="请选择课程"  clearable size="small" @change="courseChange(course.form.courseId)" style="width: 100%" :value-key="'dictValue'">
@@ -387,6 +387,13 @@
             />
           </el-select>
         </el-form-item>
+        <el-alert
+          type="info"
+          :closable="false"
+          show-icon
+          style="margin-bottom: 12px;"
+          title="课型说明:默认全部为「正课」(提看课口令)。若某小节是注册课,请勾到下方「注册课小节」。"
+        />
         <el-form-item label="小节" prop="videoIds">
           <el-select filterable  v-model="course.form.videoIds" placeholder="请选择小节" :multiple-limit="getDiff(course.row.periodStartingTime, course.row.periodEndTime) - course.total" multiple clearable size="small" style="width: 100%" :value-key="'dictValue'">
             <el-option
@@ -397,11 +404,11 @@
             />
           </el-select>
         </el-form-item>
-        <el-form-item label="无需注册小节" prop="noRegisterVideoIds">
+        <el-form-item label="注册课小节" prop="registerVideoIds">
           <el-select
             filterable
-            v-model="course.form.noRegisterVideoIds"
-            placeholder="请选择无需注册的小节"
+            v-model="course.form.registerVideoIds"
+            placeholder="勾选注册课小节(不勾=正课)"
             multiple
             clearable
             size="small"
@@ -416,6 +423,10 @@
             />
           </el-select>
           <p v-if="!course.form.videoIds || course.form.videoIds.length === 0" style="color: #909399; font-size: 12px; margin: 5px 0 0;">请先选择小节</p>
+          <div v-else style="margin-top: 8px; font-size: 12px; line-height: 1.6; color: #606266;">
+            <div><span style="color:#E6A23C;font-weight:600;">正课</span>(提看课口令):{{ addCourseMainNames || '无' }}</div>
+            <div><span style="color:#67C23A;font-weight:600;">注册课</span>(提注册链接):{{ addCourseRegisterNames || '无' }}</div>
+          </div>
         </el-form-item>
         <el-form-item v-if="course.addMode === 'insert'" label="营期时间" prop="dayDate">
           <el-date-picker
@@ -572,7 +583,15 @@
                 v-hasPermi="['course:period:updateCourseTime']"
               >修改看课时间</el-button>
             </el-col>
-
+            <el-col :span="1.5">
+              <el-button
+                type="success"
+                size="mini"
+                :disabled="updateCourse.ids.length <= 0"
+                @click="handleBatchToRegisterCourse"
+                v-hasPermi="['course:period:addCourse']"
+              >转注册课</el-button>
+            </el-col>
             <el-col :span="1.5">
               <el-button
                 type="warning"
@@ -589,7 +608,12 @@
             <el-table-column label="课程" align="center" prop="courseName" width="180" />
             <el-table-column label="小节" align="center" prop="videoName" />
             <el-table-column label="开课状态" align="center" prop="status" width="100" :formatter="courseStatusFormatter" />
-            <el-table-column label="是否开启注册链接" align="center" prop="registrationSwitch" width="180" :formatter="registrationSwitchFormatter" />
+            <el-table-column label="课型" align="center" prop="registrationSwitch" width="120">
+              <template slot-scope="scope">
+                <el-tag v-if="scope.row.registrationSwitch === 1" type="success">注册课</el-tag>
+                <el-tag v-else type="warning">正课</el-tag>
+              </template>
+            </el-table-column>
             <el-table-column label="营期时间" align="center" prop="dayDate" width="100"/>
             <el-table-column label="开始时间" align="center" prop="startDateTime" width="100">
 <!--              <template slot-scope="scope">-->
@@ -675,7 +699,7 @@
 </template>
 
 <script>
-import {addPeriod, delPeriod, exportPeriod, getPeriod, pagePeriod, updatePeriod, getDays, addCourse, insertCourse, delPeriodDay, delPeriodDayAndReorder, updateCourseTime, updateCourseDate, updateListCourseData, periodCourseMove, closePeriod, copyPeriod} from "@/api/course/userCoursePeriod";
+import {addPeriod, delPeriod, exportPeriod, getPeriod, pagePeriod, updatePeriod, getDays, addCourse, insertCourse, delPeriodDay, delPeriodDayAndReorder, updateCourseTime, updateCourseDate, updateListCourseData, periodCourseMove, closePeriod, copyPeriod, batchToRegisterCourse} from "@/api/course/userCoursePeriod";
 import {getCompanyList} from "@/api/company/company";
 import { listCamp, addCamp, editCamp, delCamp, copyCamp } from "@/api/course/userCourseCamp";
 import { courseList,videoList } from '@/api/course/courseRedPacketLog'
@@ -758,7 +782,7 @@ export default {
         addMode: 'add',
         form: {
           videoIds: [],
-          noRegisterVideoIds: []
+          registerVideoIds: []
         },
       },
       //修改营期时间参数
@@ -935,6 +959,20 @@ export default {
       return this.videoList.filter(video =>
         this.course.form.videoIds.includes(parseInt(video.dictValue))
       );
+    },
+    addCourseRegisterNames() {
+      const reg = this.course.form.registerVideoIds || [];
+      return this.filteredVideoList
+        .filter(v => reg.includes(parseInt(v.dictValue)))
+        .map(v => v.dictLabel)
+        .join('、');
+    },
+    addCourseMainNames() {
+      const reg = this.course.form.registerVideoIds || [];
+      return this.filteredVideoList
+        .filter(v => !reg.includes(parseInt(v.dictValue)))
+        .map(v => v.dictLabel)
+        .join('、');
     }
   },
   created() {
@@ -1090,6 +1128,23 @@ export default {
     handleSelectionCourseChange(selection) {
       this.updateCourse.ids = selection.map(item => item.id)
     },
+    handleBatchToRegisterCourse() {
+      const ids = this.updateCourse.ids || [];
+      if (!ids.length) {
+        this.$message.warning('请先勾选要转为注册课的小节');
+        return;
+      }
+      this.$confirm('确认将勾选的 ' + ids.length + ' 个小节转为注册课?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        return batchToRegisterCourse(ids);
+      }).then(() => {
+        this.$message.success('已转为注册课');
+        this.getCourseList();
+      }).catch(() => {});
+    },
     /** 新增按钮操作 */
     handleAdd() {
       this.reset();
@@ -1624,7 +1679,7 @@ export default {
         timeRange: ['00:00:00', '23:59:59'],
         joinTime: '23:59:59',
         videoIds: [],
-        noRegisterVideoIds: []
+        registerVideoIds: []
       };
       // 重置表单
       this.$nextTick(() => {
@@ -1672,7 +1727,7 @@ export default {
         timeRange: null,
         joinTime: null,
         videoIds: [],
-        noRegisterVideoIds: []
+        registerVideoIds: []
       };
       // 重置表单
       if (this.$refs.courseAddForm) {
@@ -1708,7 +1763,7 @@ export default {
     },
     courseChange(row){
       this.course.form.videoIds = [];
-      this.course.form.noRegisterVideoIds = [];
+      this.course.form.registerVideoIds = [];
       videoList(row).then(response => {
         this.videoList=response.list
       });
@@ -1909,15 +1964,15 @@ export default {
         2: '进行中',
         3: '已结束'
       };
-      return statusMap[row.periodStatus] || '未知状态';
+      return statusMap[row.periodStatus] || '未知';
     },
     /** 注册开关状态格式化 */
     registrationSwitchFormatter(row) {
       const switchMap = {
-        0: '未开启',
-        1: '已开启'
+        0: '正课',
+        1: '注册课'
       };
-      return switchMap[row.registrationSwitch] || '未知状态';
+      return switchMap[row.registrationSwitch] || '正课';
     },
     /** 开课状态格式化 */
     courseStatusFormatter(row) {

+ 1 - 39
src/views/course/userCoursePeriod/redPacket.vue

@@ -40,20 +40,6 @@
             v-if="scope.row.openRedPacket === 1"
             v-hasPermi="['course:period:changeRedPacketState']"
           >关闭红包</el-button>
-          <el-button
-            size="mini"
-            type="text"
-            @click="handleOpenRegistration(scope.row)"
-            v-if="scope.row.openRegistrationSwitch === 0"
-            v-hasPermi="['course:period:changeRegistrationSwitchState']"
-          >打开注册链接</el-button>
-          <el-button
-            size="mini"
-            type="text"
-            @click="handleCloseRegistration(scope.row)"
-            v-if="scope.row.openRegistrationSwitch === 1"
-            v-hasPermi="['course:period:changeRegistrationSwitchState']"
-          >关闭注册链接</el-button>
         </template>
       </el-table-column>
     </el-table>
@@ -177,7 +163,7 @@ import {
   getPeriodCompanyList,
   batchSaveRedPacket,
   getPeriodRedPacketList,
-  batchSaveRedPacketByCompany, changeRedPacketState, changeRegistrationState
+  batchSaveRedPacketByCompany, changeRedPacketState
 } from '@/api/course/userCoursePeriod'
 import redPacket from "@/views/course/userCoursePeriod/redPacket.vue";
 
@@ -283,30 +269,6 @@ export default {
         this.getCompanyList()
       })
     },
-    //打开注册链接
-    handleOpenRegistration(row){
-      const params = {
-        periodId: this.periodId,
-        companyId: row.companyId,
-        state: 1
-      }
-      changeRegistrationState( params).then(response => {
-        this.msgSuccess(response.msg)
-        this.getCompanyList()
-      })
-    },
-    //关闭注册链接
-    handleCloseRegistration(row){
-      const params = {
-        periodId: this.periodId,
-        companyId: row.companyId,
-        state: 0
-      }
-      changeRegistrationState( params).then(response => {
-        this.msgSuccess(response.msg)
-        this.getCompanyList()
-      })
-    },
     // 获取课程列表
     getCourseList() {
       this.redPacketList = []

+ 3 - 0
src/views/course/userVideo/index.vue

@@ -272,6 +272,7 @@
             v-model="form.thumbnail"
             class="avatar-uploader"
             :action="uploadUrl"
+                    :headers="uploadHeaders"
             :show-file-list="false"
             :on-success="handleAvatarSuccess"
             :before-upload="beforeAvatarUpload">
@@ -379,6 +380,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import VideoUpload from '@/components/VideoUpload/index';
 import { listUserVideo, getUserVideo, delUserVideo, addUserVideo, updateUserVideo, exportUserVideo,auditUserVideo,putOn,pullOff } from "@/api/course/userVideo";
 import {getSignature,uploadHuaWeiVod} from "@/api/common"
@@ -400,6 +402,7 @@ export default {
   },
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       tagsOptions:[],
       tags:[],
 

+ 3 - 0
src/views/his/adv/index.vue

@@ -168,6 +168,7 @@
                       v-model="form.imageUrl"
                       class="avatar-uploader"
                       :action="uploadUrl"
+                    :headers="uploadHeaders"
                       :show-file-list="false"
                       :on-success="handleAvatarSuccess"
                       :before-upload="beforeAvatarUpload">
@@ -228,6 +229,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import { listAdv, getAdv, delAdv, addAdv, updateAdv, exportAdv } from "@/api/his/adv";
 import Editor from '@/components/Editor/wang';
 import { getPromotionalActiveOption } from '@/api/his/promotionalActive'
@@ -236,6 +238,7 @@ export default {
   components: { Editor },
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
       baseUrl: process.env.VUE_APP_BASE_API,
       showOptions: [],

+ 5 - 0
src/views/his/appVersion/index.vue

@@ -123,6 +123,7 @@
             accept=".apk"
             class="upload-demo"
             :action="uploadUrl"
+                    :headers="uploadHeaders"
             :before-upload="beforeUpload"
             :on-success="handleSuccess"
             :on-preview="handlePreview"
@@ -140,6 +141,7 @@
             accept=".zip"
             class="upload-demo"
             :action="uploadUrl"
+                    :headers="uploadHeaders"
             :before-upload="beforeUpload"
             :on-success="handleSuccess"
             :on-preview="handlePreview"
@@ -161,6 +163,7 @@
             accept=".apk"
             class="upload-demo"
             :action="uploadUrl"
+                    :headers="uploadHeaders"
             :before-upload="beforeUploadBaidu"
             :on-success="handleSuccessBaidu"
             :on-preview="handlePreviewBaidu"
@@ -215,12 +218,14 @@
   </div>
 </template>
 <script>
+import { getToken } from '@/utils/auth';
 import { listApp, getApp, delApp, addApp, updateApp, exportApp } from "@/api/his/appVersion";
 
 export default {
   name: "App",
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       updateConfig: { },
       uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
       // 遮罩层

+ 3 - 0
src/views/his/article/index.vue

@@ -184,6 +184,7 @@
                   v-model="form.imageUrl"
                   class="avatar-uploader"
                   :action="uploadUrl"
+                    :headers="uploadHeaders"
                   :show-file-list="false"
                   :on-success="handleAvatarSuccess"
                   :before-upload="beforeAvatarUpload">
@@ -249,6 +250,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import { listArticle, getArticle, delArticle, addArticle, updateArticle, exportArticle } from "@/api/his/article";
 import { getAllArticleCateList} from "@/api/his/articleCate";
 import Editor from '@/components/Editor/wang';
@@ -258,6 +260,7 @@ export default {
   components: { Editor ,ArticleDetails},
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       show:{
               title:"健康知识详情",
               open:false,

+ 3 - 0
src/views/his/articleCate/index.vue

@@ -133,6 +133,7 @@
             v-model="form.imgUrl"
             class="avatar-uploader"
             :action="uploadUrl"
+                    :headers="uploadHeaders"
             :show-file-list="false"
             :on-success="handleAvatarSuccess"
             :before-upload="beforeAvatarUpload">
@@ -163,12 +164,14 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import { listArticleCate, getArticleCate, delArticleCate, addArticleCate, updateArticleCate, exportArticleCate } from "@/api/his/articleCate";
 
 export default {
   name: "articleCate",
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       // 遮罩层
       loading: true,
       uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",

+ 1 - 0
src/views/his/caseArticle/index.vue

@@ -172,6 +172,7 @@
                       v-model="form.imgUrl"
                       class="avatar-uploader"
                       :action="uploadUrl"
+                    :headers="headers"
                       :show-file-list="false"
                       :on-success="handleAvatarSuccess"
                       :before-upload="beforeAvatarUpload">

+ 1 - 0
src/views/his/chineseMedicine/index.vue

@@ -167,6 +167,7 @@
                       v-model="form.imgUrl"
                       class="avatar-uploader"
                       :action="uploadUrl"
+                    :headers="headers"
                       :show-file-list="false"
                       :on-success="handleAvatarSuccess"
                       :before-upload="beforeAvatarUpload">

+ 109 - 14
src/views/his/company/index.vue

@@ -145,6 +145,22 @@
           <el-tag>{{scope.row.watchMiniAppName}}</el-tag>
         </template>
       </el-table-column>
+      <el-table-column label="服务号" align="center" prop="courseMaAppId" width="140">
+        <template slot-scope="scope">
+          <span>{{ formatMaAppName(scope.row.courseMaAppId) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="口令看课" align="center" width="100">
+        <template slot-scope="scope">
+          <el-switch
+            v-model="scope.row.watchPasswordEnabled"
+            :active-value="1"
+            :inactive-value="0"
+            @change="handleWatchPasswordEnabledChange(scope.row)"
+            v-hasPermi="['his:company:edit']"
+          />
+        </template>
+      </el-table-column>
       <el-table-column label="备注" align="center" prop="remark"/>
       <el-table-column label="开始时间" align="center" prop="startTime" width="180"/>
       <el-table-column label="到期时间" align="center" prop="limitTime" width="180"/>
@@ -413,8 +429,9 @@
         <el-form-item label="服务号点播配置" prop="courseMaAppId">
           <el-select
             v-model="form.courseMaAppId"
-            placeholder="请选择服务号"
+            placeholder="请选择服务号(口令正课必选)"
             clearable
+            filterable
             size="small"
           >
             <el-option
@@ -855,13 +872,16 @@ import {
   changeRedPacketState,
   getDivConfig,
   setDiv, exitMiniProgram,
-  getGatewayCompanyList
+  getGatewayCompanyList,
+  listCourseMaAppOptions,
+  changeWatchPasswordEnabled
 } from '@/api/his/company'
 import { getFollowDoctorList } from '@/api/his/doctor'
 import { docList } from '@/api/his/doctor'
 import { cateList } from '@/api/his/packageCate'
 import { listDept } from '@/api/system/dept'
 import { listAll } from '@/api/course/coursePlaySourceConfig'
+import { getConfigByKey } from '@/api/system/config'
 import { companyBatchUnbindSipExt, getSipExtDetail, getUnBindExtnumPage } from '@/api/aiSipCall/aiSipCallUser'
 
 export default {
@@ -997,6 +1017,9 @@ export default {
         ],
         linkName: [
           { required: true, message: '联系人不能为空', trigger: 'blur' }
+        ],
+        courseMaAppId: [
+          { required: false, message: '请选择服务号点播配置(开启口令看课时必选)', trigger: 'change' }
         ]
         // followDoctorIds: [
         //   { required: true, message: "不能为空", trigger: "blur" }
@@ -1080,6 +1103,7 @@ export default {
   },
   created() {
     this.getList()
+    this.getAppList()
     this.getDicts('sys_company_status').then(response => {
       this.statusOptions = response.data
     })
@@ -1197,6 +1221,28 @@ export default {
         this.getList()
       })
     },
+    /** 口令看课开关 */
+    handleWatchPasswordEnabledChange(row) {
+      const state = row.watchPasswordEnabled
+      if (state === 1 && !row.courseMaAppId) {
+        this.$message.error('请先编辑公司并绑定服务号点播配置,再开启口令看课')
+        row.watchPasswordEnabled = 0
+        return
+      }
+      changeWatchPasswordEnabled({
+        companyId: row.companyId,
+        state: state
+      }).then(response => {
+        if (response.code === 200) {
+          this.msgSuccess(state === 1 ? '已开启口令看课:正课复制将生成口令' : '已关闭口令看课:正课复制仍为小程序链接')
+        } else {
+          row.watchPasswordEnabled = state === 1 ? 0 : 1
+          this.$message.error(response.msg || '修改失败')
+        }
+      }).catch(() => {
+        row.watchPasswordEnabled = state === 1 ? 0 : 1
+      })
+    },
     handleRedDeduct(row) {
       this.redDeductForm.companyId = row.companyId
       this.redDeductForm.companyName = row.companyName
@@ -1406,23 +1452,72 @@ export default {
       this.maAppList = []
       this.miniAppList = []
       this.customAppList = []
+      const mapMa = (list) => (list || []).map(v => ({
+        appId: v.appId || v.appid,
+        appName: v.appName || v.name || v.appId || v.appid
+      })).filter(v => !!v.appId && !!v.appName && v.appId !== v.appName)
+      // 服务号下拉:courseMa.config type=2(专用接口 + 配置兜底)
+      const optionsReq = listCourseMaAppOptions().then(response => {
+        const data = Array.isArray(response.data) ? response.data : []
+        this.maAppList = mapMa(data)
+      }).catch(() => {
+        this.maAppList = []
+      })
+      getConfigByKey('courseMa.config').then(response => {
+        let list = []
+        try {
+          if (response.data && response.data.configValue) {
+            list = JSON.parse(response.data.configValue) || []
+          }
+        } catch (e) {
+          list = []
+        }
+        const maFromConfig = mapMa(list.filter(v => Number(v.type) === 2))
+        optionsReq.finally(() => {
+          if (!this.maAppList.length) {
+            this.maAppList = maFromConfig
+            return
+          }
+          const nameMap = {}
+          maFromConfig.forEach(i => { nameMap[i.appId] = i.appName })
+          this.maAppList = this.maAppList.map(i => ({
+            appId: i.appId,
+            appName: nameMap[i.appId] || i.appName || i.appId
+          }))
+          maFromConfig.forEach(i => {
+            if (!this.maAppList.some(x => x.appId === i.appId)) {
+              this.maAppList.push(i)
+            }
+          })
+        })
+      }).catch(() => {})
+      // 小程序/自定义:仍走点播播放源表 listAll(不要改成 courseMa,避免丢已绑定的看课小程序)
       listAll().then(response => {
         const { code, data } = response
-        if (code === 200) {
-          if (data) {
-            this.maAppList = data.filter(v => v.type == 2).map(v => {
-              return { appId: v.appid, appName: v.name }
-            })
-            this.miniAppList = data.filter(v => v.type == 1).map(v => {
-              return { appId: v.appid, appName: v.name }
-            })
-            this.customAppList = data.filter(v => v.type == 3).map(v => {
-              return { appId: v.appid, appName: v.name }
-            })
-          }
+        if (code === 200 && data) {
+          this.miniAppList = data.filter(v => Number(v.type) === 1).map(v => ({
+            appId: v.appid,
+            appName: v.name
+          }))
+          this.customAppList = data.filter(v => Number(v.type) === 3).map(v => ({
+            appId: v.appid,
+            appName: v.name
+          }))
         }
       })
     },
+    goPlaySourceConfig() {
+      this.$router.push({ path: '/system/config', query: { activeName: 'courseMa.config' } }).catch(() => {
+        this.$message.info('请打开【系统管理-参数设置】,切换到「点播播放源配置」标签,新增类型=服务号')
+      })
+    },
+    formatMaAppName(appId) {
+      if (!appId) {
+        return '-'
+      }
+      const hit = (this.maAppList || []).find(item => item.appId === appId)
+      return hit ? hit.appName : appId
+    },
     /** 提交按钮 */
     submitForm() {
       this.$refs['form'].validate(valid => {

+ 3 - 0
src/views/his/department/index.vue

@@ -157,6 +157,7 @@
             v-model="form.iconUrl"
             class="avatar-uploader"
             :action="uploadUrl"
+                    :headers="uploadHeaders"
             :show-file-list="false"
             :on-success="handleAvatarSuccess"
             :before-upload="beforeAvatarUpload">
@@ -197,6 +198,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import { listDepartment, getDepartment, delDepartment, addDepartment, updateDepartment, exportDepartment } from "@/api/his/department";
 import departmentDetails from '../../components/his/departmentDetails.vue';
 export default {
@@ -204,6 +206,7 @@ export default {
    components: { departmentDetails },
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
       baseUrl: process.env.VUE_APP_BASE_API,
    show:{

+ 6 - 0
src/views/his/doctor/type1.vue

@@ -461,6 +461,7 @@
                             v-model="form.idCardFrontUrl"
                             class="avatar-uploader"
                             :action="fruploadUrl"
+                         :headers="uploadHeaders"
                             :show-file-list="false"
                             :on-success="frhandleAvatarSuccess"
                             :before-upload="beforeAvatarUpload">
@@ -475,6 +476,7 @@
                               v-model="form.avatar"
                               class="avatar-uploader"
                               :action="avataruploadUrl"
+                         :headers="uploadHeaders"
                               :show-file-list="false"
                               :on-success="avatarhandleAvatarSuccess"
                               :before-upload="beforeAvatarUpload">
@@ -491,6 +493,7 @@
                          v-model="form.idCardBackUrl"
                          class="avatar-uploader"
                          :action="bauploadUrl"
+                         :headers="uploadHeaders"
                          :show-file-list="false"
                          :on-success="bahandleAvatarSuccess"
                          :before-upload="beforeAvatarUpload">
@@ -505,6 +508,7 @@
                          v-model="form.signUrl"
                          class="avatar-uploader"
                          :action="bauploadUrl"
+                         :headers="uploadHeaders"
                          :show-file-list="false"
                          :on-success="signhandleAvatarSuccess"
                          :before-upload="beforeAvatarUpload">
@@ -756,6 +760,7 @@
   </div>
 </template>
 <script>
+import { getToken } from '@/utils/auth';
 import {editFollow,getAllFollowDoctorList,getUserList, listDoctor, getWxaCodeUnLimit,getDoctor, listdocuser,delDoctor, addDoctor, updateDoctor, exportDoctor ,updateAuditDoctor,getDoctorPrice,editDoctorPrice,editPassWord,editPrice} from "@/api/his/doctor";
 import{listDepartment} from "@/api/his/disease";
 import{listAllHospital} from "@/api/his/hospital";
@@ -772,6 +777,7 @@ export default {
   components: { doctorDetails,doctorinquiryDetails,ImageUpload },
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       doctorName: {name:"1"},
       docuser:[],
       updateFollowOpen:false,

+ 6 - 0
src/views/his/doctor/type2.vue

@@ -372,6 +372,7 @@
                                   v-model="form.idCardFrontUrl"
                                   class="avatar-uploader"
                                   :action="FruploadUrl"
+                         :headers="uploadHeaders"
                                   :show-file-list="false"
                                   :on-success="FrhandleAvatarSuccess"
                                   :before-upload="beforeAvatarUpload">
@@ -386,6 +387,7 @@
                                     v-model="form.avatar"
                                     class="avatar-uploader"
                                     :action="avataruploadUrl"
+                         :headers="uploadHeaders"
                                     :show-file-list="false"
                                     :on-success="avatarhandleAvatarSuccess"
                                     :before-upload="beforeAvatarUpload">
@@ -402,6 +404,7 @@
                                   v-model="form.idCardBackUrl"
                                   class="avatar-uploader"
                                   :action="BauploadUrl"
+                         :headers="uploadHeaders"
                                   :show-file-list="false"
                                   :on-success="BahandleAvatarSuccess"
                                   :before-upload="beforeAvatarUpload">
@@ -416,6 +419,7 @@
                                v-model="form.signUrl"
                                class="avatar-uploader"
                                :action="bauploadUrl"
+                         :headers="uploadHeaders"
                                :show-file-list="false"
                                :on-success="signhandleAvatarSuccess"
                                :before-upload="beforeAvatarUpload">
@@ -532,6 +536,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import {getUserList, listDoctor, getDoctor, delDoctor, addDoctor, updateDoctor, exportDoctor ,updateAuditDoctor,getDoctorPrice,editDoctorPrice,editPassWord,editPrice} from "@/api/his/doctor";
 import{listDepartment} from "@/api/his/disease";
 import{listAllHospital} from "@/api/his/hospital";
@@ -542,6 +547,7 @@ export default {
   components: { pharmacistDetails },
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       FruploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
       BauploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
       avataruploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",

+ 4 - 0
src/views/his/doctorArticle/index.vue

@@ -189,6 +189,7 @@
                       v-model="form.imageUrl"
                       class="avatar-uploader"
                       :action="uploadUrl"
+                    :headers="uploadHeaders"
                       :show-file-list="false"
                       :on-success="handleAvatarSuccess"
                       :before-upload="beforeAvatarUpload">
@@ -206,6 +207,7 @@
                ref="upload"
                class="upload-demo"
                :action="uploadUrl"
+                    :headers="uploadHeaders"
                :on-success="handleSuccess"
                :before-upload="beforeUpload"
                :limit="1"
@@ -252,6 +254,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import {listdocuser, listArticle, getArticle, delArticle, addArticle, updateArticle, exportArticle } from "@/api/his/doctorArticle";
 import articleDetails from '../../components/his/doctorArticleDetails.vue';
 import { getAllArticleCateList} from "@/api/his/doctorArticleCate";
@@ -261,6 +264,7 @@ export default {
   components: { articleDetails,Editor },
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       videoUrl: "",
       videoAccept:"video/*",
       uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",

+ 3 - 0
src/views/his/doctorArticleCate/index.vue

@@ -133,6 +133,7 @@
             v-model="form.imgUrl"
             class="avatar-uploader"
             :action="uploadUrl"
+                    :headers="uploadHeaders"
             :show-file-list="false"
             :on-success="handleAvatarSuccess"
             :before-upload="beforeAvatarUpload">
@@ -163,12 +164,14 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import { listDoctorArticleCate, getDoctorArticleCate, delDoctorArticleCate, addDoctorArticleCate, updateDoctorArticleCate, exportDoctorArticleCate } from "@/api/his/doctorArticleCate";
 
 export default {
   name: "DoctorArticleCate",
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       // 遮罩层
       loading: true,
       uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",

+ 1 - 0
src/views/his/famousPrescribe/index.vue

@@ -182,6 +182,7 @@
                       v-model="form.imgUrl"
                       class="avatar-uploader"
                       :action="uploadUrl"
+                    :headers="headers"
                       :show-file-list="false"
                       :on-success="handleAvatarSuccess"
                       :before-upload="beforeAvatarUpload">

+ 4 - 0
src/views/his/homeArticle/index.vue

@@ -152,6 +152,7 @@
           <el-upload
             class="avatar-uploader"
             :action="uploadUrl"
+                    :headers="uploadHeaders"
             :show-file-list="false"
             :on-success="handleImageUrlSuccess"
             :before-upload="beforeImageUrlUpload">
@@ -171,6 +172,7 @@
               ref="upload"
               class="upload-demo"
               :action="uploadUrl"
+                    :headers="uploadHeaders"
               :on-success="handleVideoSuccess"
               :before-upload="beforeVideoUpload"
               :limit="1"
@@ -212,6 +214,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import { listHomeArticle, getHomeArticle, delHomeArticle, addHomeArticle, updateHomeArticle, exportHomeArticle } from "@/api/his/homeArticle";
 import { listHomeCategory, allListHomeCategory } from "@/api/his/homeCategory";
 import Editor from '@/components/Editor/wang';
@@ -221,6 +224,7 @@ export default {
   components: { Editor },
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       // 遮罩层
       loading: true,
       // 选中数组

+ 3 - 0
src/views/his/homeCategory/index.vue

@@ -122,6 +122,7 @@
           <el-upload
             class="avatar-uploader"
             :action="uploadUrl"
+                    :headers="uploadHeaders"
             :show-file-list="false"
             :on-success="handleImageUrlSuccess"
             :before-upload="beforeImageUrlUpload">
@@ -148,12 +149,14 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import { listHomeCategory, getHomeCategory, delHomeCategory, addHomeCategory, updateHomeCategory, exportHomeCategory } from "@/api/his/homeCategory";
 
 export default {
   name: "HomeCategory",
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       // 遮罩层
       loading: true,
       // 选中数组

+ 3 - 0
src/views/his/hospital/index.vue

@@ -255,6 +255,7 @@
             v-model="form.imgUrl"
             class="avatar-uploader"
             :action="uploadUrl"
+                    :headers="headers"
             :show-file-list="false"
             :on-success="handleAvatarSuccess"
             :before-upload="beforeAvatarUpload">
@@ -295,6 +296,7 @@
            v-model="form.sealUrl"
            class="avatar-uploader"
            :action="uploadUrl2"
+                         :headers="uploadHeaders"
            :show-file-list="false"
            :on-success="handleAvatarSuccess2"
            :before-upload="beforeAvatarUpload">
@@ -364,6 +366,7 @@ export default {
      components: { hospitalDetails,getPointMap },
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       dialogVisibleMap:false,
       show:{
         title:"医院详情",

+ 33 - 1
src/views/his/integralGoods/index.vue

@@ -110,6 +110,11 @@
         </template>
       </el-table-column>
       <el-table-column label="商品名称" align="center" prop="goodsName" />
+      <el-table-column label="关联店铺" align="center" prop="storeId" min-width="120">
+        <template slot-scope="scope">
+          <span>{{ scope.row.storeName || storeLabel(scope.row.storeId) }}</span>
+        </template>
+      </el-table-column>
       <el-table-column label="原价" align="center" prop="otPrice" />
       <el-table-column label="商品分类" align="center" prop="goodsType">
         <template slot-scope="scope">
@@ -195,6 +200,16 @@
         <el-form-item label="商品编号" prop="barCode">
           <el-input v-model="form.barCode" placeholder="请输入商品编号"  style="width: 200px;"/>
         </el-form-item>
+        <el-form-item label="关联店铺" prop="storeId">
+          <el-select v-model="form.storeId" placeholder="请选择店铺(推聚水潭用该店编号)" filterable clearable style="width: 100%;">
+            <el-option
+              v-for="dict in storeOptions"
+              :key="dict.dictValue"
+              :label="dict.dictLabel"
+              :value="parseInt(dict.dictValue)"
+            />
+          </el-select>
+        </el-form-item>
 
         <el-form-item label="排序" prop="sort">
           <el-input-number v-model="form.sort"  :min="0"  label="排序"></el-input-number>
@@ -281,6 +296,7 @@
 
 <script>
 import { importTemplate,listIntegralGoods, getIntegralGoods, delIntegralGoods, addIntegralGoods, updateIntegralGoods, exportIntegralGoods } from "@/api/his/integralGoods";
+import { listStore } from "@/api/his/storeProduct";
 import Material from '@/components/Material';
 import Editor from '@/components/Editor/wang';
 import { getToken } from "@/utils/auth";
@@ -343,6 +359,8 @@ export default {
       goodsTypeOptions: [],
       // 状态字典
       statusOptions: [],
+      // 店铺列表
+      storeOptions: [],
       // 查询参数
       queryParams: {
         pageNum: 1,
@@ -363,6 +381,9 @@ export default {
         goodsName: [
           { required: true, message: "商品名称不能为空", trigger: "blur" }
         ],
+        storeId: [
+          { required: true, message: "请选择关联店铺", trigger: "change" }
+        ],
       },
       // 运费模板列表
       tempList: [],
@@ -377,8 +398,18 @@ export default {
     this.getDicts("sys_company_status").then(response => {
       this.statusOptions = response.data;
     });
+    listStore().then(response => {
+      this.storeOptions = response.rows || [];
+    });
   },
   methods: {
+        storeLabel(storeId) {
+          if (storeId == null || storeId === '') {
+            return '-';
+          }
+          const hit = this.storeOptions.find(d => String(d.dictValue) === String(storeId));
+          return hit ? hit.dictLabel : storeId;
+        },
         handledetails(row){
             this.show.open=true;
             setTimeout(() => {
@@ -445,7 +476,8 @@ export default {
         stock: null,
         descs: null,
         createTime: null,
-        tempId: null
+        tempId: null,
+        storeId: null
       };
       this.imageArr = []
       this.photoArr = []

+ 28 - 0
src/views/his/integralOrder/index.vue

@@ -120,6 +120,11 @@
     <el-table v-loading="loading" border :data="integralOrderList" @selection-change="handleSelectionChange">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="订单编号" align="center" prop="orderCode" />
+      <el-table-column label="商品编号" align="center" prop="barCode" min-width="120" show-overflow-tooltip>
+        <template slot-scope="scope">
+          <span>{{ formatGoodsBarCodes(scope.row) }}</span>
+        </template>
+      </el-table-column>
       <el-table-column label="用户名称" align="center" prop="userName" />
       <el-table-column label="用户电话" align="center" prop="userPhone" />
       <el-table-column label="用户地址" align="center" prop="userAddress" show-overflow-tooltip />
@@ -421,6 +426,29 @@ export default {
              this.$refs.Details.getDetails(row.orderId);
         }, 1);
     },
+    /** 从 itemJson / barCode 解析商品编号(多商品用逗号拼接) */
+    formatGoodsBarCodes(row) {
+      if (!row) {
+        return '-';
+      }
+      // 后端已按 goodsId 回查补全 barCode
+      if (row.barCode) {
+        return row.barCode;
+      }
+      try {
+        if (row.itemJson) {
+          const parsed = typeof row.itemJson === 'string' ? JSON.parse(row.itemJson) : row.itemJson;
+          const list = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : []);
+          const codes = list.map(g => g && (g.barCode || g.bar_code)).filter(Boolean);
+          if (codes.length) {
+            return codes.join(',');
+          }
+        }
+      } catch (e) {
+        // ignore
+      }
+      return '-';
+    },
     /** 查询积分商品订单列表 */
     getList() {
       this.loading = true;

+ 1 - 0
src/views/his/medicatedFood/index.vue

@@ -160,6 +160,7 @@
                       v-model="form.imgUrl"
                       class="avatar-uploader"
                       :action="uploadUrl"
+                    :headers="headers"
                       :show-file-list="false"
                       :on-success="handleAvatarSuccess"
                       :before-upload="beforeAvatarUpload">

+ 1 - 0
src/views/his/package/index.vue

@@ -258,6 +258,7 @@
             v-model="form.imgUrl"
             class="avatar-uploader"
             :action="uploadUrl"
+                    :headers="headers"
             :show-file-list="false"
             :on-success="handleAvatarSuccess"
             :before-upload="beforeAvatarUpload">

+ 3 - 0
src/views/his/packageCate/index.vue

@@ -136,6 +136,7 @@
             v-model="form.imgUrl"
             class="avatar-uploader"
             :action="uploadUrl"
+                    :headers="uploadHeaders"
             :show-file-list="false"
             :on-success="handleAvatarSuccess"
             :before-upload="beforeAvatarUpload">
@@ -181,12 +182,14 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import { listPackageCate, getPackageCate, delPackageCate, addPackageCate, updatePackageCate, exportPackageCate } from "@/api/his/packageCate";
 
 export default {
   name: "PackageCate",
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       // 遮罩层
       loading: true,
       // 导出遮罩层

+ 4 - 0
src/views/his/store/index.vue

@@ -212,6 +212,7 @@
                  v-model="form.logoUrl"
                  class="avatar-uploader"
                  :action="uploadUrl"
+                    :headers="uploadHeaders"
                  :show-file-list="false"
                  :on-success="handleAvatarSuccess"
                  :before-upload="beforeAvatarUpload">
@@ -228,6 +229,7 @@
             v-model="form.licenseImages"
             class="avatar-uploader"
             :action="licenseuploadUrl"
+                    :headers="uploadHeaders"
             :show-file-list="false"
             :on-success="licensehandleAvatarSuccess"
             :before-upload="beforeAvatarUpload">
@@ -368,6 +370,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import { listStore, getStore, delStore, addStore, updateStore, exportStore } from "@/api/his/store";
 import storeDetails from '../../components/his/storeDetails.vue';
 import {getCitys} from "@/api/store/city";
@@ -376,6 +379,7 @@ export default {
   components: { storeDetails },
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       show:{
               title:"店铺详情",
               open:false,

+ 1 - 0
src/views/his/storeProduct/index.vue

@@ -378,6 +378,7 @@
               ref="uploadVideo"
               class="upload-demo"
               :action="uploadUrl"
+                    :headers="headers"
               :on-success="handleSuccess"
               :before-upload="beforeUpload"
               :limit="1"

+ 3 - 0
src/views/his/storeProductCategory/index.vue

@@ -138,6 +138,7 @@
                       v-model="form.pic"
                       class="avatar-uploader"
                       :action="uploadUrl"
+                    :headers="uploadHeaders"
                       :show-file-list="false"
                       :on-success="handleAvatarSuccess"
                       :before-upload="beforeAvatarUpload">
@@ -160,6 +161,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import { getPidList,getAllStoreProductCategory,pidlist, listCategory, getCategory, delCategory, addCategory, updateCategory, exportCategory } from "@/api/his/storeProductCategory";
 import Treeselect from "@riophae/vue-treeselect";
 import "@riophae/vue-treeselect/dist/vue-treeselect.css";
@@ -168,6 +170,7 @@ export default {
   components: {Treeselect},
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       categoryOptions:[],
       uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
       baseUrl: process.env.VUE_APP_BASE_API,

+ 3 - 0
src/views/his/testTemp/index.vue

@@ -163,6 +163,7 @@
                               v-model="form.img"
                               class="avatar-uploader"
                               :action="uploadUrl"
+                    :headers="uploadHeaders"
                               :show-file-list="false"
                               :on-success="handleAvatarSuccess"
                               :before-upload="beforeAvatarUpload">
@@ -293,6 +294,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import { listTestTemp, getTestTemp, delTestTemp, addTestTemp, updateTestTemp, exportTestTemp } from "@/api/his/testTemp";
 import testTempDetails from '../../components/his/testTempDetails.vue';
 import testTempItem from '../testTempItem/index.vue';
@@ -302,6 +304,7 @@ export default {
   components: { testTempDetails ,testTempItem,Editor},
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       // 遮罩层
       loading: true,
       show:{

+ 1 - 0
src/views/his/vessel/index.vue

@@ -172,6 +172,7 @@
                       v-model="form.imgUrl"
                       class="avatar-uploader"
                       :action="uploadUrl"
+                    :headers="headers"
                       :show-file-list="false"
                       :on-success="handleAvatarSuccess"
                       :before-upload="beforeAvatarUpload">

+ 4 - 0
src/views/hisStore/adv/index.vue

@@ -141,6 +141,7 @@
               v-model="form.icon"
               class="avatar-uploader"
               :action="uploadUrl"
+                    :headers="uploadHeaders"
               :show-file-list="false"
               :on-success="handleAvatarSuccess"
               :before-upload="beforeAvatarUpload">
@@ -154,6 +155,7 @@
               ref="upload"
               class="upload-demo"
               :action="uploadUrl"
+                    :headers="uploadHeaders"
               :on-success="handleSuccess"
               :before-upload="beforeUpload"
               :limit="1"
@@ -202,6 +204,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import { listAdv, getAdv, delAdv, addAdv, updateAdv, exportAdv } from "@/api/hisStore/adv";
 import Editor from '@/components/Editor/wang';
 export default {
@@ -211,6 +214,7 @@ export default {
   },
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
       baseUrl: process.env.VUE_APP_BASE_API,
       videoAccept:"video/*",

+ 32 - 1
src/views/hisStore/integralGoods/index.vue

@@ -110,6 +110,11 @@
         </template>
       </el-table-column>
       <el-table-column label="商品名称" align="center" prop="goodsName" />
+      <el-table-column label="关联店铺" align="center" prop="storeId" min-width="120">
+        <template slot-scope="scope">
+          <span>{{ scope.row.storeName || storeLabel(scope.row.storeId) }}</span>
+        </template>
+      </el-table-column>
       <el-table-column label="原价" align="center" prop="otPrice" />
       <el-table-column label="商品分类" align="center" prop="goodsType">
         <template slot-scope="scope">
@@ -191,6 +196,16 @@
         <el-form-item label="商品编号" prop="barCode">
           <el-input v-model="form.barCode" placeholder="请输入商品编号"  style="width: 200px;"/>
         </el-form-item>
+        <el-form-item label="关联店铺" prop="storeId">
+          <el-select v-model="form.storeId" placeholder="请选择店铺(推聚水潭用该店编号)" filterable clearable style="width: 100%;">
+            <el-option
+              v-for="dict in storeOptions"
+              :key="dict.dictValue"
+              :label="dict.dictLabel"
+              :value="parseInt(dict.dictValue)"
+            />
+          </el-select>
+        </el-form-item>
 
         <el-form-item label="排序" prop="sort">
           <el-input-number v-model="form.sort"  :min="0"  label="排序"></el-input-number>
@@ -258,6 +273,7 @@
 
 <script>
 import { importTemplate,listIntegralGoods, getIntegralGoods, delIntegralGoods, addIntegralGoods, updateIntegralGoods, exportIntegralGoods } from "@/api/hisStore/integralGoods";
+import { listStore } from "@/api/his/storeProduct";
 import Material from '@/components/Material';
 import Editor from '@/components/Editor/wang';
 import { getToken } from "@/utils/auth";
@@ -318,6 +334,7 @@ export default {
       goodsTypeOptions: [],
       // 状态字典
       statusOptions: [],
+      storeOptions: [],
       // 查询参数
       queryParams: {
         pageNum: 1,
@@ -338,6 +355,9 @@ export default {
         goodsName: [
           { required: true, message: "商品名称不能为空", trigger: "blur" }
         ],
+        storeId: [
+          { required: true, message: "请选择关联店铺", trigger: "change" }
+        ],
       }
     };
   },
@@ -349,8 +369,18 @@ export default {
     this.getDicts("sys_company_status").then(response => {
       this.statusOptions = response.data;
     });
+    listStore().then(response => {
+      this.storeOptions = response.rows || [];
+    });
   },
   methods: {
+        storeLabel(storeId) {
+          if (storeId == null || storeId === '') {
+            return '-';
+          }
+          const hit = this.storeOptions.find(d => String(d.dictValue) === String(storeId));
+          return hit ? hit.dictLabel : storeId;
+        },
         handledetails(row){
             this.show.open=true;
             setTimeout(() => {
@@ -415,7 +445,8 @@ export default {
         sort: null,
         stock: null,
         descs: null,
-        createTime: null
+        createTime: null,
+        storeId: null
       };
       this.resetForm("form");
     },

+ 10 - 0
src/views/hisStore/store/index.vue

@@ -233,6 +233,7 @@
                 v-model="form.logoUrl"
                 class="avatar-uploader"
                 :action="uploadUrl"
+                    :headers="uploadHeaders"
                 :show-file-list="false"
                 :on-success="handleAvatarSuccess"
                 :before-upload="beforeAvatarUpload">
@@ -292,6 +293,7 @@
               <el-upload
                 class="avatar-uploader"
                 :action="uploadUrl"
+                    :headers="uploadHeaders"
                 :show-file-list="false"
                 :on-success="(response, file) => handleFileSuccess(response, file, 'businessLicense')"
                 :before-upload="beforeAvatarUpload">
@@ -320,6 +322,7 @@
                 <el-upload
                   class="avatar-uploader"
                   :action="uploadUrl"
+                    :headers="uploadHeaders"
                   :show-file-list="false"
                   :on-success="(response, file) => handleFileSuccess(response, file, 'drugLicense')"
                   :before-upload="beforeAvatarUpload">
@@ -347,6 +350,7 @@
                 <el-upload
                   class="avatar-uploader"
                   :action="uploadUrl"
+                    :headers="uploadHeaders"
                   :show-file-list="false"
                   :on-success="(response, file) => handleFileSuccess(response, file, 'medicalDevice1')"
                   :before-upload="beforeAvatarUpload">
@@ -374,6 +378,7 @@
                 <el-upload
                   class="avatar-uploader"
                   :action="uploadUrl"
+                    :headers="uploadHeaders"
                   :show-file-list="false"
                   :on-success="(response, file) => handleFileSuccess(response, file, 'medicalDevice2')"
                   :before-upload="beforeAvatarUpload">
@@ -401,6 +406,7 @@
                 <el-upload
                   class="avatar-uploader"
                   :action="uploadUrl"
+                    :headers="uploadHeaders"
                   :show-file-list="false"
                   :on-success="(response, file) => handleFileSuccess(response, file, 'medicalDevice3')"
                   :before-upload="beforeAvatarUpload">
@@ -428,6 +434,7 @@
                 <el-upload
                   class="avatar-uploader"
                   :action="uploadUrl"
+                    :headers="uploadHeaders"
                   :show-file-list="false"
                   :on-success="(response, file) => handleFileSuccess(response, file, 'foodLicense')"
                   :before-upload="beforeAvatarUpload">
@@ -455,6 +462,7 @@
                 <el-upload
                   class="avatar-uploader"
                   :action="uploadUrl"
+                    :headers="uploadHeaders"
                   :show-file-list="false"
                   :on-success="(response, file) => handleFileSuccess(response, file, 'medicalLicense')"
                   :before-upload="beforeAvatarUpload">
@@ -571,6 +579,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import { listStore, getStore, delStore, addStore, updateStore, exportStore, refreshPasWod } from '@/api/hisStore/store'
 import storeDetails from '../components/storeDetails.vue';
 import {getCitys} from "@/api/store/city";
@@ -579,6 +588,7 @@ export default {
   components: { storeDetails },
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       show: {
         title: "店铺详情",
         open: false,

+ 1 - 0
src/views/hisStore/storeProduct/index.vue

@@ -559,6 +559,7 @@
                       ref="upload"
                       class="upload-demo"
                       :action="uploadUrl"
+                    :headers="headers"
                       :on-success="handleSuccess"
                       :before-upload="beforeUpload"
                       :limit="1"

+ 1 - 0
src/views/hisStore/storeProductAudit/index.vue

@@ -343,6 +343,7 @@
               ref="upload"
               class="upload-demo"
               :action="uploadUrl"
+                    :headers="headers"
               :on-success="handleSuccess"
               :before-upload="beforeUpload"
               :limit="1"

+ 3 - 0
src/views/qw/friendMaterial/index.vue

@@ -70,6 +70,7 @@
                 <el-col :span="12" style="text-align: right;">
                   <el-upload
                     :action="uploadUrl"
+                    :headers="uploadHeaders"
                     :file-list="[]"
                     :on-progress="handleProgress"
                     :before-upload="beforeUpload"
@@ -138,6 +139,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import {addMaterial, delMaterial, listMaterial,} from "@/api/qw/material";
 import {addMaterialGroup, delMaterialGroup, listMaterialGroup, updateMaterialGroup} from "@/api/qw/materialGroup";
 
@@ -179,6 +181,7 @@ export default {
   },
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
       dialogVisible: false,
       materialUrl: '',

+ 1 - 0
src/views/qw/sop/ImageUpload.vue

@@ -2,6 +2,7 @@
   <div class="component-upload-image">
     <el-upload
       :action="uploadUrl"
+                    :headers="headers"
       list-type="picture-card"
       :on-success="handleUploadSuccess"
       :before-upload="handleBeforeUpload"

+ 4 - 0
src/views/qw/sopTemp/addSopTemp.vue

@@ -202,6 +202,7 @@
                                                 v-model="setList.fileUrl"
                                                 class="avatar-uploader"
                                                 :action="uploadUrl"
+                    :headers="uploadHeaders"
                                                 :show-file-list="false"
                                                 :on-success="(res, file) => handleAvatarSuccessFile(res, file, setList)"
                                                 :before-upload="beforeAvatarUploadFile">
@@ -220,6 +221,7 @@
                                                 v-model="setList.videoUrl"
                                                 class="avatar-uploader"
                                                 :action="uploadUrl"
+                    :headers="uploadHeaders"
                                                 :show-file-list="false"
                                                 :on-success="(res, file) => handleAvatarSuccessVideo(res, file, setList)"
                                                 :before-upload="beforeAvatarUploadVideo">
@@ -350,6 +352,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import {addSopTemp, delSopTemp, exportSopTemp, getSopTemp, listSopTemp, updateSopTemp} from "@/api/qw/sopTemp";
 import {courseList, videoList} from "@/api/qw/sop";
 import ImageUpload from "@/views/qw/sop/ImageUpload";
@@ -361,6 +364,7 @@ export default {
   components: { ImageUpload,FriendMaterial,userVideo},
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
 
       //图片放大
       dialogVisible: false,

+ 5 - 0
src/views/qw/sopTemp/addSopTempOld.vue

@@ -167,6 +167,7 @@
                                         v-model="content.fileUrl"
                                         class="avatar-uploader"
                                         :action="uploadUrl"
+                    :headers="uploadHeaders"
                                         :show-file-list="false"
                                         :on-success="(res, file) => handleAvatarSuccessFile(res, file, content)"
                                         :before-upload="beforeAvatarUploadFile">
@@ -185,6 +186,7 @@
                                         v-model="content.videoUrl"
                                         class="avatar-uploader"
                                         :action="uploadUrl"
+                    :headers="uploadHeaders"
                                         :show-file-list="false"
                                         :on-success="(res, file) => handleAvatarSuccessVideo(res, file, content)"
                                         :before-upload="beforeAvatarUploadVideo">
@@ -202,6 +204,7 @@
                                           v-model="content.voiceUrl"
                                           class="avatar-uploader"
                                           :action="uploadUrl"
+                    :headers="uploadHeaders"
                                           :show-file-list="false"
                                           :on-success="(res, file) => handleAvatarSuccessVoice(res, file, content)"
                                           :before-upload="beforeAvatarUploadVoice">
@@ -277,6 +280,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import { listSopTemp, getSopTemp, delSopTemp, addSopTemp, updateSopTemp, exportSopTemp } from "@/api/qw/sopTemp";
 import { courseList,videoList } from "@/api/qw/sop";
 import ImageUpload from "@/views/qw/sop/ImageUpload";
@@ -286,6 +290,7 @@ export default {
       components: { ImageUpload,FriendMaterial},
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
       // 遮罩层
       loading: true,

+ 4 - 0
src/views/qw/sopTemp/updateSopTemp.vue

@@ -386,6 +386,7 @@
                                                 v-model="setList.fileUrl"
                                                 class="avatar-uploader"
                                                 :action="uploadUrl"
+                    :headers="uploadHeaders"
                                                 :show-file-list="false"
                                                 :on-success="(res, file) => handleAvatarSuccessFile(res, file, setList)"
                                                 :before-upload="beforeAvatarUploadFile">
@@ -406,6 +407,7 @@
                                                 v-model="setList.videoUrl"
                                                 class="avatar-uploader"
                                                 :action="uploadUrl"
+                    :headers="uploadHeaders"
                                                 :show-file-list="false"
                                                 :on-success="(res, file) => handleAvatarSuccessVideo(res, file, setList)"
                                                 :before-upload="beforeAvatarUploadVideo">
@@ -680,6 +682,7 @@
 }
 </style>
 <script>
+import { getToken } from '@/utils/auth';
 import draggable from 'vuedraggable';
 import {
   listSopTemp,
@@ -704,6 +707,7 @@ export default {
   components: {ImageUpload, userVideo, draggable},
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       addTag: [{
         addTag: [],
         inputVisible: false,

+ 4 - 0
src/views/qw/sopTemp/updateSopTemp2.vue

@@ -169,6 +169,7 @@
                                                 v-model="setList.fileUrl"
                                                 class="avatar-uploader"
                                                 :action="uploadUrl"
+                    :headers="uploadHeaders"
                                                 :show-file-list="false"
                                                 :on-success="(res, file) => handleAvatarSuccessFile(res, file, setList)"
                                                 :before-upload="beforeAvatarUploadFile">
@@ -187,6 +188,7 @@
                                                 v-model="setList.videoUrl"
                                                 class="avatar-uploader"
                                                 :action="uploadUrl"
+                    :headers="uploadHeaders"
                                                 :show-file-list="false"
                                                 :on-success="(res, file) => handleAvatarSuccessVideo(res, file, setList)"
                                                 :before-upload="beforeAvatarUploadVideo">
@@ -312,6 +314,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import { listSopTemp, getSopTemp, delSopTemp, addSopTemp, updateSopTemp, exportSopTemp } from "@/api/qw/sopTemp";
 import { courseList,videoList } from "@/api/qw/sop";
 import ImageUpload from "@/views/qw/sop/ImageUpload";
@@ -321,6 +324,7 @@ export default {
   components: { ImageUpload,userVideo},
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS2",
       uploadUrlByVoice:process.env.VUE_APP_BASE_API+"/common/uploadOSSByHOOKVoice",
       //上传语音的遮罩层

+ 5 - 0
src/views/qw/sopTemp/updateSopTempOld.vue

@@ -159,6 +159,7 @@
                                         v-model="content.fileUrl"
                                         class="avatar-uploader"
                                         :action="uploadUrl"
+                    :headers="uploadHeaders"
                                         :show-file-list="false"
                                         :on-success="(res, file) => handleAvatarSuccessFile(res, file, content)"
                                         :before-upload="beforeAvatarUploadFile">
@@ -177,6 +178,7 @@
                                         v-model="content.videoUrl"
                                         class="avatar-uploader"
                                         :action="uploadUrl"
+                    :headers="uploadHeaders"
                                         :show-file-list="false"
                                         :on-success="(res, file) => handleAvatarSuccessVideo(res, file, content)"
                                         :before-upload="beforeAvatarUploadVideo">
@@ -194,6 +196,7 @@
                                         v-model="content.voiceUrl"
                                         class="avatar-uploader"
                                         :action="uploadUrl"
+                    :headers="uploadHeaders"
                                         :show-file-list="false"
                                         :on-success="(res, file) => handleAvatarSuccessVoice(res, file, content)"
                                         :before-upload="beforeAvatarUploadVoice">
@@ -267,6 +270,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import { listSopTemp, getSopTemp, delSopTemp, addSopTemp, updateSopTemp, exportSopTemp } from "@/api/qw/sopTemp";
 import { courseList,videoList } from "@/api/qw/sop";
 import ImageUpload from "@/views/qw/sop/ImageUpload";
@@ -275,6 +279,7 @@ export default {
   components: { ImageUpload},
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
       // 遮罩层
       loading: true,

+ 91 - 1
src/views/system/config/config.vue

@@ -1345,6 +1345,34 @@
           <el-form-item label="侧边栏公共图" prop="sidebarImageUrl">
             <image-upload v-model="form18.sidebarImageUrl" :limit="1"/>
           </el-form-item>
+          <el-form-item label="口令看课封面mediaId">
+            <el-tooltip class="item" effect="dark" content="全局兜底。多服务号请到「点播播放源配置」里,在对应服务号行填写口令卡片封面mediaId" placement="top-end">
+              <el-input v-model="form18.watchPasswordThumbMediaId" placeholder="仅单服务号兜底用,多号请配到服务号行" style="width: 420px"></el-input>
+            </el-tooltip>
+          </el-form-item>
+          <el-form-item label="服务号海报目录">
+            <el-tooltip class="item" effect="dark" content="关注海报底图本地目录。文件名={公司绑定的服务号appId}.png,例如 C:/Users/Administrator/Desktop/mp-poster/wxxxxx.png" placement="top-end">
+              <el-input v-model="form18.mpPosterDir" placeholder="目录例:F:/hdt/mp-poster(放 appId.png);也可填图片完整路径" style="width: 520px"></el-input>
+            </el-tooltip>
+          </el-form-item>
+          <el-form-item label="海报底部提示">
+            <el-input v-model="form18.mpPosterTip" placeholder="默认:扫码关注服务号(仅底部拼接模式)" style="width: 420px"></el-input>
+          </el-form-item>
+          <el-form-item label="海报二维码布局">
+            <el-radio-group v-model="form18.mpPosterQrLayout">
+              <el-radio label="auto">自动放入白框</el-radio>
+              <el-radio label="frame">手动坐标</el-radio>
+              <el-radio label="bottom">拼在底部</el-radio>
+            </el-radio-group>
+          </el-form-item>
+          <el-form-item v-if="form18.mpPosterQrLayout === 'frame'" label="二维码坐标">
+            <el-input-number v-model="form18.mpPosterQrX" :min="0" placeholder="X" style="width: 110px"></el-input-number>
+            <span style="margin: 0 6px">X</span>
+            <el-input-number v-model="form18.mpPosterQrY" :min="0" placeholder="Y" style="width: 110px"></el-input-number>
+            <span style="margin: 0 6px">Y</span>
+            <el-input-number v-model="form18.mpPosterQrSize" :min="40" placeholder="边长" style="width: 120px"></el-input-number>
+            <span style="margin-left: 6px; color: #909399">边长(px)</span>
+          </el-form-item>
 
 
           <el-form-item label="开启评论/弹幕" prop="openCommentStatus">
@@ -1556,6 +1584,29 @@
               <el-input v-model="scope.row.aesKey" placeholder="请输入aesKey" :disabled="!scope.row.editing"/>
             </template>
           </el-table-column>
+          <el-table-column label="口令卡片封面mediaId" align="center" prop="thumbMediaId" min-width="280">
+            <template slot-scope="scope">
+              <template v-if="scope.row.type == 2 || scope.row.type === '2'">
+                <div style="display:flex;flex-direction:column;gap:4px;align-items:stretch;">
+                  <el-input
+                    v-model="scope.row.thumbMediaId"
+                    placeholder="该服务号素材库 media_id"
+                    :disabled="!scope.row.editing"
+                    size="mini"
+                  />
+                  <el-upload
+                    :show-file-list="false"
+                    accept=".jpg,.jpeg,.png,.bmp,image/jpeg,image/png,image/bmp"
+                    :disabled="!scope.row.appid"
+                    :http-request="(opt) => uploadMpThumbForRow(scope.row, opt)"
+                  >
+                    <el-button size="mini" type="text" :disabled="!scope.row.appid">上传封面</el-button>
+                  </el-upload>
+                </div>
+              </template>
+              <span v-else>-</span>
+            </template>
+          </el-table-column>
           <el-table-column label="msgDataFormat" align="center" prop="msgDataFormat">
             <template slot-scope="scope">
               <el-input v-model="scope.row.msgDataFormat" placeholder="请输入消息格式" :disabled="!scope.row.editing"/>
@@ -1795,6 +1846,7 @@
                 ref="upload"
                 class="upload-demo"
                 :action="uploadUrl"
+                    :headers="uploadHeaders"
                 :on-success="handleSuccess"
                 :before-upload="beforeUpload"
                 :limit="1"
@@ -1825,6 +1877,7 @@
                 ref="upload"
                 class="upload-demo"
                 :action="uploadUrl"
+                    :headers="uploadHeaders"
                 :on-success="appHandleSuccess"
                 :before-upload="beforeUpload"
                 :limit="1"
@@ -2321,6 +2374,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import { getConfigByKey, updateConfigByKey, clearCache, updateIsTownOn } from '@/api/system/config'
 import { listStore } from '@/api/his/storeProduct'
 import { js } from 'js-beautify'
@@ -2338,6 +2392,7 @@ import { listCompany } from '@/api/company/company'
 import { getStoreProductColumns } from '@/api/hisStore/storeProduct'
 import { getStoreColumns } from '@/api/hisStore/store'
 import TestConfig from '@/views/system/config/testConfig.vue'
+import { uploadMpThumb } from '@/api/course/mpMaterial'
 
 export default {
   name: 'Config',
@@ -2350,6 +2405,7 @@ export default {
   },
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       citys: [],
       images: [],
       appImages: [],
@@ -2399,7 +2455,13 @@ export default {
       form17: {},
       form18: {
         viewCommentNum: 200,
-        isRedPackageBalanceDeduction: 0
+        isRedPackageBalanceDeduction: 0,
+        mpPosterDir: '',
+        mpPosterTip: '',
+        mpPosterQrLayout: 'auto',
+        mpPosterQrX: null,
+        mpPosterQrY: null,
+        mpPosterQrSize: null
       },
       form19: {},
       form20: {
@@ -2817,6 +2879,9 @@ export default {
         }
         if (key == 'course.config') {
           this.form18 = JSON.parse(response.data.configValue)
+          if (!this.form18.mpPosterQrLayout) {
+            this.$set(this.form18, 'mpPosterQrLayout', 'auto')
+          }
         }
         if (key == 'redPacket.config') {
           this.form19 = JSON.parse(response.data.configValue)
@@ -3215,6 +3280,8 @@ export default {
         token: '',
         aesKey: '',
         msgDataFormat: '',
+        type: '2',
+        thumbMediaId: '',
         editing: true
       })
     },
@@ -3229,6 +3296,29 @@ export default {
       }
       this.$set(this.courseMaConfigList[index], 'editing', false)
     },
+    uploadMpThumbForRow(row, option) {
+      if (!row || !row.appid) {
+        this.$message.error('请先填写 appid')
+        return
+      }
+      const loading = this.$loading({ lock: true, text: '上传到微信素材库...', background: 'rgba(0,0,0,0.6)' })
+      uploadMpThumb(row.appid, option.file).then(res => {
+        if (res.code === 200 && res.data && res.data.mediaId) {
+          this.$set(row, 'thumbMediaId', res.data.mediaId)
+          this.$set(row, 'editing', true)
+          this.$message.success('已获取 mediaId,请点击下方「保存点播播放源配置」写入系统参数')
+          option.onSuccess && option.onSuccess(res)
+        } else {
+          this.$message.error((res && res.msg) || '上传失败')
+          option.onError && option.onError(res)
+        }
+      }).catch(err => {
+        this.$message.error((err && err.message) || '上传失败')
+        option.onError && option.onError(err)
+      }).finally(() => {
+        loading.close()
+      })
+    },
     addDisabledTime() {
       if (this.form18.disabledTimeList == null || this.form18.disabledTimeList == undefined) {
         this.form18.disabledTimeList = []

+ 80 - 1
src/views/system/config/config2.vue

@@ -1016,6 +1016,29 @@
            <el-form-item label="课程LOGO" prop="courseLogo">
              <image-upload v-model="form18.courseLogo" :limit="1" />
            </el-form-item>
+           <el-form-item label="服务号海报目录">
+             <el-tooltip class="item" effect="dark" content="关注海报底图本地目录。文件名={公司绑定的服务号appId}.png" placement="top-end">
+               <el-input v-model="form18.mpPosterDir" placeholder="目录例:F:/hdt/mp-poster(放 appId.png);也可填图片完整路径" style="width: 520px"></el-input>
+             </el-tooltip>
+           </el-form-item>
+           <el-form-item label="海报底部提示">
+             <el-input v-model="form18.mpPosterTip" placeholder="默认:扫码关注服务号(仅底部拼接模式)" style="width: 420px"></el-input>
+           </el-form-item>
+           <el-form-item label="海报二维码布局">
+             <el-radio-group v-model="form18.mpPosterQrLayout">
+               <el-radio label="auto">自动放入白框</el-radio>
+               <el-radio label="frame">手动坐标</el-radio>
+               <el-radio label="bottom">拼在底部</el-radio>
+             </el-radio-group>
+           </el-form-item>
+           <el-form-item v-if="form18.mpPosterQrLayout === 'frame'" label="二维码坐标">
+             <el-input-number v-model="form18.mpPosterQrX" :min="0" style="width: 110px"></el-input-number>
+             <span style="margin: 0 6px">X</span>
+             <el-input-number v-model="form18.mpPosterQrY" :min="0" style="width: 110px"></el-input-number>
+             <span style="margin: 0 6px">Y</span>
+             <el-input-number v-model="form18.mpPosterQrSize" :min="40" style="width: 120px"></el-input-number>
+             <span style="margin-left: 6px; color: #909399">边长(px)</span>
+           </el-form-item>
 
            <el-form-item label="开启评论/弹幕" prop="openCommentStatus">
              <el-radio-group v-model="form18.openCommentStatus">
@@ -1089,6 +1112,29 @@
                <el-input v-model="scope.row.aesKey" placeholder="请输入aesKey" :disabled="!scope.row.editing" />
              </template>
            </el-table-column>
+           <el-table-column label="口令卡片封面mediaId" align="center" prop="thumbMediaId" min-width="280">
+             <template slot-scope="scope">
+               <template v-if="scope.row.type == 2 || scope.row.type === '2'">
+                 <div style="display:flex;flex-direction:column;gap:4px;align-items:stretch;">
+                   <el-input
+                     v-model="scope.row.thumbMediaId"
+                     placeholder="该服务号素材库 media_id"
+                     :disabled="!scope.row.editing"
+                     size="mini"
+                   />
+                   <el-upload
+                     :show-file-list="false"
+                     accept=".jpg,.jpeg,.png,.bmp,image/jpeg,image/png,image/bmp"
+                     :disabled="!scope.row.appid"
+                     :http-request="(opt) => uploadMpThumbForRow(scope.row, opt)"
+                   >
+                     <el-button size="mini" type="text" :disabled="!scope.row.appid">上传封面</el-button>
+                   </el-upload>
+                 </div>
+               </template>
+               <span v-else>-</span>
+             </template>
+           </el-table-column>
            <el-table-column label="msgDataFormat" align="center" prop="msgDataFormat">
              <template slot-scope="scope">
                <el-input v-model="scope.row.msgDataFormat" placeholder="请输入消息格式" :disabled="!scope.row.editing" />
@@ -1266,6 +1312,7 @@
                  ref="upload"
                  class="upload-demo"
                  :action="uploadUrl"
+                    :headers="uploadHeaders"
                  :on-success="handleSuccess"
                  :before-upload="beforeUpload"
                  :limit="1"
@@ -1296,6 +1343,7 @@
 </template>
 
 <script>
+import { getToken } from '@/utils/auth';
 import {   getConfigByKey,updateConfigByKey, clearCache } from "@/api/system/config";
 import {listStore} from "@/api/his/storeProduct";
 import { js } from "js-beautify";
@@ -1306,6 +1354,7 @@ import productAttrValueSelect from "../../components/his/productGiftValueSelect.
 import productDeliveryGiftValueSelect from "../../components/his/productDeliveryGiftValueSelect.vue";
 import { Col } from "element-ui";
 import Editor from '@/components/Editor/wang';
+import { uploadMpThumb } from '@/api/course/mpMaterial'
 export default {
   name: "Config",
   components: {
@@ -1313,6 +1362,7 @@ export default {
   },
   data() {
     return {
+      uploadHeaders: { Authorization: 'Bearer ' + getToken() },
       images:[],
       uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
       videoAccept:"video/*",
@@ -1364,7 +1414,8 @@ export default {
       form17:{
       },
       form18:{
-        viewCommentNum: 200
+        viewCommentNum: 200,
+        mpPosterQrLayout: 'auto'
       },
       form19:{
       },
@@ -1601,6 +1652,9 @@ export default {
           }
           if(key=="course.config"){
               this.form18 =JSON.parse(response.data.configValue);
+              if (!this.form18.mpPosterQrLayout) {
+                this.$set(this.form18, 'mpPosterQrLayout', 'auto');
+              }
           }
           if(key=="redPacket.config"){
             this.form19 =JSON.parse(response.data.configValue);
@@ -1861,6 +1915,8 @@ export default {
         token: "",
         aesKey: "",
         msgDataFormat: "",
+        type: "2",
+        thumbMediaId: "",
         editing: true
       });
     },
@@ -1875,6 +1931,29 @@ export default {
       }
       this.$set(this.courseMaConfigList[index], 'editing', false);
     },
+    uploadMpThumbForRow(row, option) {
+      if (!row || !row.appid) {
+        this.$message.error('请先填写 appid');
+        return;
+      }
+      const loading = this.$loading({ lock: true, text: '上传到微信素材库...', background: 'rgba(0,0,0,0.6)' });
+      uploadMpThumb(row.appid, option.file).then(res => {
+        if (res.code === 200 && res.data && res.data.mediaId) {
+          this.$set(row, 'thumbMediaId', res.data.mediaId);
+          this.$set(row, 'editing', true);
+          this.$message.success('已获取 mediaId,请点击「提交」写入系统参数');
+          option.onSuccess && option.onSuccess(res);
+        } else {
+          this.$message.error((res && res.msg) || '上传失败');
+          option.onError && option.onError(res);
+        }
+      }).catch(err => {
+        this.$message.error((err && err.message) || '上传失败');
+        option.onError && option.onError(err);
+      }).finally(() => {
+        loading.close();
+      });
+    },
     addDisabledTime(){
       if(this.form18.disabledTimeList == null || this.form18.disabledTimeList == undefined){
         this.form18.disabledTimeList = []