Преглед на файлове

Merge branch 'master' of http://1.14.104.71:10880/root/ylrz_his_scrm_adminUI

caoliqin преди 1 ден
родител
ревизия
e8180fc7e9

+ 40 - 0
.env.prod-sczy

@@ -0,0 +1,40 @@
+# 页面标题
+VUE_APP_TITLE =致医总管理系统
+# 首页菜单标题
+VUE_APP_TITLE_INDEX =致医管理系统
+# 公司名称
+VUE_APP_COMPANY_NAME =重庆云联融智科技有限公司
+# ICP备案号
+VUE_APP_ICP_RECORD =渝ICP备2024031984号-1
+# ICP网站访问地址
+VUE_APP_ICP_URL =https://beian.miit.gov.cn
+# 网站LOG
+VUE_APP_LOG_URL =@/assets/logo/sczy.png
+# 存储桶配置
+VUE_APP_OBS_ACCESS_KEY_ID = K2UTJGIN7UTZJR2XMXYG
+# 存储桶配置
+VUE_APP_OBS_SECRET_ACCESS_KEY = sbyeNJLbcYmH6copxeFP9pAoksM4NIT9Zw4x0SRX
+# 存储桶配置
+VUE_APP_OBS_SERVER = https://obs.cn-north-4.myhuaweicloud.com
+# 存储桶配置
+VUE_APP_OBS_BUCKET = sczy-hw079058881
+# 存储桶配置
+VUE_APP_COS_BUCKET = sczy-1323137866
+# 存储桶配置
+VUE_APP_COS_REGION = ap-chongqing
+# 线路一地址
+VUE_APP_VIDEO_LINE_1 = https://sczycpv.ylrzcloud.com
+# 线路二地址
+VUE_APP_VIDEO_LINE_2 = https://sczyobs.ylrztop.com
+
+# 开发环境配置
+ENV = 'development'
+
+# FS管理系统/开发环境
+VUE_APP_BASE_API = '/prod-api'
+
+#默认 1、会员 2、企微
+VUE_APP_COURSE_DEFAULT = 2
+
+# 路由懒加载
+VUE_CLI_BABEL_TRANSPILE_MODULES = true

+ 1 - 0
package.json

@@ -46,6 +46,7 @@
     "build:prod-hst": "vue-cli-service build --mode prod-hst",
     "build:prod-czt": "vue-cli-service build --mode prod-czt",
     "build:prod-hat": "vue-cli-service build --mode prod-hat",
+    "build:prod-sczy": "vue-cli-service build --mode prod-sczy",
     "build:prod-ddgy": "vue-cli-service build --mode prod-ddgy",
     "build:prod-yxj": "vue-cli-service build --mode prod-yxj",
     "build:prod-bjzm": "vue-cli-service build --mode prod-bjzm",

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

@@ -52,6 +52,15 @@ export function exportCourseQuestionBank(query) {
   })
 }
 
+export function exportFail(data) {
+  return request({
+    url: '/course/courseQuestionBank/exportFail',
+    method: 'post',
+    data: data
+  })
+}
+
+
 // 下载模板
 export function importTemplate() {
   return request({

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

@@ -75,6 +75,15 @@ export function delUserCourseVideo(videoId) {
   })
 }
 
+// 同步课程模板
+export function syncTemplate(courseId) {
+  console.log(courseId)
+  return request({
+    url: '/course/userCourse/syncTemplate/' + courseId,
+    method: 'post'
+  })
+}
+
 // 导出课堂视频
 export function exportUserCourseVideo(query) {
   return request({

+ 30 - 0
src/api/his/integralOrder.js

@@ -102,4 +102,34 @@ export function finishOrder(orderCode) {
     url: '/his/integralOrder/finishOrder/' + orderCode,
     method: 'get'
   })
+}
+//数据分捡
+export function batchSetErpOrder(params) {
+  return request({
+    url: '/his/integralOrder/batchSetErpOrder',
+    method: 'post',
+    data: params
+  })
+}
+//数据分捡
+export function batchCreateErpOrder(params) {
+  return request({
+    url: '/his/integralOrder/batchCreateErpOrder',
+    method: 'post',
+    data: params
+  })
+}
+//获取城市
+export function getCitys(){
+  return request({
+    url: '/his/city/getCitys',
+    method: 'get'
+  })
+}
+//获取模版
+export function getIntegralTemplate(){
+  return request({
+    url: '/his/integralOrder/importUpdateOrderTemplate',
+    method: 'get'
+  })
 }

+ 1 - 1
src/api/hisStore/city.js

@@ -10,7 +10,7 @@ export function listCity(query) {
 }
 export function getAllList(query) {
   return request({
-    url: '/store/city/getAllList',
+    url: '/his/city/getAllList',
     method: 'get',
     params: query
   })

+ 9 - 0
src/api/hisStore/storeOrder.js

@@ -377,3 +377,12 @@ export function getErpAccount() {
     method: 'get'
   })
 }
+
+// 导出发货单
+export function healthExportShippingOrder(query) {
+  return request({
+    url: '/store/store/storeOrder/healthExportShippingOrder',
+    method: 'get',
+    params: query
+  })
+}

+ 17 - 0
src/api/live/liveOrder.js

@@ -293,3 +293,20 @@ export function updateErp(data) {
     data: data
   })
 }
+
+
+// 导出发货单
+export function healthExportShippingOrder(query) {
+  return request({
+    url: '/live/liveOrder/healthExportShippingOrder',
+    method: 'get',
+    params: query
+  })
+}
+
+export function importDeliveryNoteExpressTemplate() {
+  return request({
+    url: '/live/liveOrder/importDeliveryNoteExpressTemplate',
+    method: 'get'
+  })
+}

+ 1 - 1
src/api/statistics/statistics.js

@@ -378,7 +378,7 @@ export function getSearchUserInfo(param){
  */
 export function getStatisticsDataN(data) {
   return request({
-    url: '/crm/ComprehensiveStatistics/statisticMainN',
+    url: '/statistic/manage/statisticMainN',
     method: 'post',
     data: data  // 使用 data 而不是 params
   })

BIN
src/assets/logo/sczy.png


+ 101 - 62
src/views/components/course/userCourseCatalogDetails.vue

@@ -6,7 +6,7 @@
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
       <el-form-item label="小节名称" prop="title">
         <el-input v-model="queryParams.title" placeholder="请输入小节名称" clearable size="small"
-                  @keyup.enter.native="handleQuery" />
+                  @keyup.enter.native="handleQuery"/>
       </el-form-item>
       <el-form-item>
         <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
@@ -16,34 +16,45 @@
     <el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
-                   v-hasPermi="['course:userCourseVideo:add']">新增目录</el-button>
+                   v-hasPermi="['course:userCourseVideo:add']">新增目录
+        </el-button>
       </el-col>
       <el-col :span="1.5">
         <el-button type="primary" plain :disabled="!ids || ids.length <= 0" size="mini" @click="openUpdates"
-                   v-hasPermi="['course:userCourseVideo:updateTime']">修改时间</el-button>
+                   v-hasPermi="['course:userCourseVideo:updateTime']">修改时间
+        </el-button>
       </el-col>
       <el-col :span="1.5">
         <el-button type="primary" plain size="mini" @click="openAdds"
-                   v-hasPermi="['course:userCourseVideo:batchAdd']">批量添加</el-button>
+                   v-hasPermi="['course:userCourseVideo:batchAdd']">批量添加
+        </el-button>
       </el-col>
       <el-col :span="1.5">
         <el-button type="primary" plain size="mini" @click="updateRedPageckeOpen"
-                   v-hasPermi="['course:userCourseVideo:updateRed']">修改红包</el-button>
+                   v-hasPermi="['course:userCourseVideo:updateRed']">修改红包
+        </el-button>
       </el-col>
       <el-col :span="1.5">
         <el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete"
-                   v-hasPermi="['course:userCourseVideo:remove']">删除</el-button>
+                   v-hasPermi="['course:userCourseVideo:remove']">删除
+        </el-button>
       </el-col>
       <el-col :span="1.5">
         <el-button type="warning" plain icon="el-icon-edit" size="mini" @click="handleCourseSort"
-                   v-hasPermi="['course:userCourseVideo:sort']">修改课节排序</el-button>
+                   v-hasPermi="['course:userCourseVideo:sort']">修改课节排序
+        </el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button type="primary" plain icon="el-icon-delete" size="mini" @click="handleSync"
+                   v-hasPermi="['course:userCourseVideo:sync']">同步模板数据
+        </el-button>
       </el-col>
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
     <el-table border v-loading="loading" :data="userCourseVideoList" @selection-change="handleSelectionChange">
-      <el-table-column type="selection" width="55" align="center" />
-      <el-table-column label="视频ID" align="center" prop="videoId" />
-      <el-table-column label="小节名称" align="center" show-overflow-tooltip prop="title" />
+      <el-table-column type="selection" width="55" align="center"/>
+      <el-table-column label="视频ID" align="center" prop="videoId"/>
+      <el-table-column label="小节名称" align="center" show-overflow-tooltip prop="title"/>
       <el-table-column label="视频文件名称" align="center" show-overflow-tooltip prop="fileName">
       </el-table-column>
       <el-table-column label="视频时长" align="center" prop="duration">
@@ -69,30 +80,33 @@
           <el-tag type="danger" v-if="!row.lastJoinTime">无</el-tag>
         </template>
       </el-table-column>
-      <el-table-column label="红包金额" align="center" prop="redPacketMoney" />
-      <el-table-column label="排序" align="center" prop="courseSort" />
-      <el-table-column label="上传时间" align="center" prop="createTime" />
+      <el-table-column label="红包金额" align="center" prop="redPacketMoney"/>
+      <el-table-column label="排序" align="center" prop="courseSort"/>
+      <el-table-column label="上传时间" align="center" prop="createTime"/>
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
         <template slot-scope="scope">
           <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
-                     v-hasPermi="['course:userCourseVideo:edit']">修改</el-button>
+                     v-hasPermi="['course:userCourseVideo:edit']">修改
+          </el-button>
           <el-button size="mini" type="text" icon="el-icon-edit" @click="handleComment(scope.row)"
-                     v-hasPermi="['course:courseWatchComment:list']">查看评论</el-button>
+                     v-hasPermi="['course:courseWatchComment:list']">查看评论
+          </el-button>
           <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
-                     v-hasPermi="['course:userCourseVideo:remove']">删除</el-button>
+                     v-hasPermi="['course:userCourseVideo:remove']">删除
+          </el-button>
         </template>
       </el-table-column>
     </el-table>
 
     <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
-                @pagination="getList" />
+                @pagination="getList"/>
     <el-dialog :title="title" :visible.sync="open" width="1000px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="110px" v-loading="uploadLoading">
         <el-form-item label="视频标题" prop="title">
-          <el-input v-model="form.title" placeholder="请输入内容" />
+          <el-input v-model="form.title" placeholder="请输入内容"/>
         </el-form-item>
         <el-form-item label="视频描述" prop="description">
-          <el-input v-model="form.description" type="textarea" :rows="2" placeholder="请输入内容" />
+          <el-input v-model="form.description" type="textarea" :rows="2" placeholder="请输入内容"/>
         </el-form-item>
         <el-form-item label="课程排序" prop="courseSort">
           <el-input-number v-model="form.courseSort" :min="1"></el-input-number>
@@ -110,7 +124,8 @@
                       :line_2.sync="form.lineTwo" :line_3.sync="form.lineThree" :thumbnail.sync="form.thumbnail"
                       :uploadType.sync="form.uploadType" :isTranscode.sync="form.isTranscode"
                       :transcodeFileKey.sync="form.transcodeFileKey" @video-duration="handleVideoDuration"
-                      @change="handleVideoChange" @selectProjects="handleSelectProjects" ref="videoUpload" append-to-body />
+                      @change="handleVideoChange" @selectProjects="handleSelectProjects" ref="videoUpload"
+                      append-to-body/>
 
         <el-form-item label="课题选择" prop="questionBankId">
           <el-button size="small" type="primary" @click="chooseQuestionBank">选取课题</el-button>
@@ -127,14 +142,15 @@
             </el-table-column>
             <el-table-column label="类别" align="center" prop="type">
               <template slot-scope="scope">
-                <dict-tag :options="typeOptions" :value="scope.row.type" />
+                <dict-tag :options="typeOptions" :value="scope.row.type"/>
               </template>
             </el-table-column>
-            <el-table-column label="答案" align="center" prop="answer" />
+            <el-table-column label="答案" align="center" prop="answer"/>
             <el-table-column label="操作" align="center" width="100px" fixed="right">
               <template slot-scope="scope">
                 <el-button size="mini" type="text" icon="el-icon-delete"
-                           @click="handleQuestionBankDelete(scope.row)">删除</el-button>
+                           @click="handleQuestionBankDelete(scope.row)">删除
+                </el-button>
               </template>
             </el-table-column>
           </el-table>
@@ -155,14 +171,15 @@
         <el-form-item label="商品选择" v-if="form.isProduct === 1">
           <el-button size="small" type="primary" @click="chooseCourseProduct">选取商品</el-button>
           <el-table border width="100%" style="margin-top:5px;" :data="form.courseProducts">
-            <el-table-column label="商品名称" align="center" prop="productName" />
-            <el-table-column label="产品条码" align="center" prop="barCode" />
-            <el-table-column label="商品价格" align="center" prop="productPrice" />
-            <el-table-column label="库存" align="center" prop="stock" />
+            <el-table-column label="商品名称" align="center" prop="productName"/>
+            <el-table-column label="产品条码" align="center" prop="barCode"/>
+            <el-table-column label="商品价格" align="center" prop="productPrice"/>
+            <el-table-column label="库存" align="center" prop="stock"/>
             <el-table-column label="操作" align="center" width="100px" fixed="right">
               <template slot-scope="scope">
                 <el-button size="mini" type="text" icon="el-icon-delete"
-                           @click="handleCourseProductDelete(scope.row)">删除</el-button>
+                           @click="handleCourseProductDelete(scope.row)">删除
+                </el-button>
               </template>
             </el-table-column>
           </el-table>
@@ -188,7 +205,8 @@
     <el-dialog :title="title" :visible.sync="updateBatchData.open" width="1000px" append-to-body>
       <el-form ref="form" :model="updateBatchData.form" label-width="110px">
         <el-form-item label="看课时间" prop="timeRange">
-          <el-time-picker is-range v-model="updateBatchData.form.timeRange" range-separator="至" start-placeholder="开始时间"
+          <el-time-picker is-range v-model="updateBatchData.form.timeRange" range-separator="至"
+                          start-placeholder="开始时间"
                           value-format="HH:mm:ss" end-placeholder="结束时间" placeholder="选择时间范围">
           </el-time-picker>
         </el-form-item>
@@ -215,19 +233,20 @@
       <el-form :inline="true" :model="addBatchData.queryParams" class="library-search">
         <el-form-item label="素材名称">
           <el-input v-model="addBatchData.queryParams.resourceName" placeholder="请输入素材名称" clearable size="small"
-                    @keyup.enter.native="resourceList" />
+                    @keyup.enter.native="resourceList"/>
         </el-form-item>
         <el-form-item label="类型">
-          <el-select v-model="addBatchData.queryParams.typeId" @change="changeCateType" placeholder="请选择素材类型" clearable
+          <el-select v-model="addBatchData.queryParams.typeId" @change="changeCateType" placeholder="请选择素材类型"
+                     clearable
                      size="small">
             <el-option v-for="item in addBatchData.typeOptions" :key="item.dictValue" :label="item.dictLabel"
-                       :value="item.dictValue" />
+                       :value="item.dictValue"/>
           </el-select>
         </el-form-item>
         <el-form-item label="子类型">
           <el-select v-model="addBatchData.queryParams.typeSubId" placeholder="请选择素材子类型" clearable size="small">
             <el-option v-for="item in addBatchData.typeSubOptions" :key="item.dictValue" :label="item.dictLabel"
-                       :value="item.dictValue" />
+                       :value="item.dictValue"/>
           </el-select>
         </el-form-item>
         <el-form-item>
@@ -238,15 +257,15 @@
       <!-- 视频列表 -->
       <el-table v-loading="addBatchData.loading" :data="addBatchData.list"
                 @selection-change="handVideoleSelectionChange" height="400px">
-        <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="素材名称" align="center" prop="resourceName" />
-        <el-table-column label="文件名称" align="center" prop="fileName" />
-        <el-table-column label="排序" align="center" prop="sort" />
+        <el-table-column type="selection" width="55" align="center"/>
+        <el-table-column label="素材名称" align="center" prop="resourceName"/>
+        <el-table-column label="文件名称" align="center" prop="fileName"/>
+        <el-table-column label="排序" align="center" prop="sort"/>
         <el-table-column label="缩略图" align="center">
           <template slot-scope="scope">
             <el-popover placement="right" title="" trigger="hover">
-              <img alt="" slot="reference" :src="scope.row.thumbnail" style="width: 80px; height: 50px" />
-              <img alt="" :src="scope.row.thumbnail" style="max-width: 150px;" />
+              <img alt="" slot="reference" :src="scope.row.thumbnail" style="width: 80px; height: 50px"/>
+              <img alt="" :src="scope.row.thumbnail" style="max-width: 150px;"/>
             </el-popover>
           </template>
         </el-table-column>
@@ -260,7 +279,7 @@
       <!-- 分页 -->
       <pagination v-show="addBatchData.total > 0" :total="addBatchData.total"
                   :page.sync="addBatchData.queryParams.pageNum" :limit.sync="addBatchData.queryParams.pageSize"
-                  @pagination="resourceList" />
+                  @pagination="resourceList"/>
 
       <div slot="footer" class="dialog-footer">
         <el-button type="primary" @click="batchVideoSave">确 定</el-button>
@@ -268,7 +287,7 @@
     </el-dialog>
     <el-dialog title="章节红包" :visible.sync="redData.open" width="900px" append-to-body>
       <el-table border v-loading="redData.loading" :data="redData.list" height="600px">
-        <el-table-column label="小节名称" align="center" show-overflow-tooltip prop="title" />
+        <el-table-column label="小节名称" align="center" show-overflow-tooltip prop="title"/>
         <el-table-column label="视频文件名称" align="center" show-overflow-tooltip prop="fileName">
         </el-table-column>
         <el-table-column label="视频时长" align="center" prop="duration">
@@ -278,11 +297,11 @@
         </el-table-column>
         <el-table-column label="红包金额" align="center" prop="redPacketMoney">
           <template slot-scope="scope">
-            <el-input class="el-input" v-model="scope.row.redPacketMoney" />
+            <el-input class="el-input" v-model="scope.row.redPacketMoney"/>
           </template>
         </el-table-column>
-        <el-table-column label="排序" align="center" prop="courseSort" />
-        <el-table-column label="上传时间" align="center" prop="createTime" />
+        <el-table-column label="排序" align="center" prop="courseSort"/>
+        <el-table-column label="上传时间" align="center" prop="createTime"/>
       </el-table>
       <div slot="footer" class="dialog-footer">
         <el-button type="primary" @click="batchRedSave">确 定</el-button>
@@ -298,7 +317,8 @@
 
     <el-dialog title="修改课节排序" :visible.sync="openVideoSort" style="width: 1600px;" append-to-body>
       <draggable v-model="userCourseVideoSortList" @end="onDragEndDay" style="padding: 10px">
-        <el-button style="margin: 8px 4px;" v-for="(item, index) in userCourseVideoSortList" :class="item.newCourseSort != item.courseSort ? 'red':''">第{{
+        <el-button style="margin: 8px 4px;" v-for="(item, index) in userCourseVideoSortList"
+                   :class="item.newCourseSort != item.courseSort ? 'red':''">第{{
             item.newCourseSort
           }}序(原排序第{{ item.courseSort }})
         </el-button>
@@ -314,27 +334,31 @@
 <script>
 import {
   addUserCourseVideo,
+  batchSaveVideo,
+  batchUpdateRed,
   delUserCourseVideo,
   getSort,
   getUserCourseVideo,
   getVideoListByCourseId,
+  getVideoListByCourseIdAll,
+  sortCourseVideo,
   updates,
-  batchSaveVideo,
-  batchUpdateRed,
-  updateUserCourseVideo, getVideoListByCourseIdAll, sortCourseVideo
+  updateUserCourseVideo,
+  syncTemplate
 } from '@/api/course/userCourseVideo'
+// import {syncTemplate} from '@/api/course/userCourse'
 import QuestionBank from "@/views/course/courseQuestionBank/QuestionBank.vue";
 import CourseProduct from "@/views/course/fsCourseProduct/CourseProduct.vue";
 import VideoUpload from "@/components/VideoUpload/index.vue";
-import { listVideoResource } from '@/api/course/videoResource';
-import { getByIds } from '@/api/course/courseQuestionBank'
+import {listVideoResource} from '@/api/course/videoResource';
+import {getByIds} from '@/api/course/courseQuestionBank'
 import CourseWatchComment from "./courseWatchComment.vue";
-import { getCateListByPid, getCatePidList } from '@/api/course/userCourseCategory'
+import {getCateListByPid, getCatePidList} from '@/api/course/userCourseCategory'
 import draggable from 'vuedraggable'
 
 export default {
   name: "userCourseCatalog",
-  components: { VideoUpload, QuestionBank, CourseWatchComment, CourseProduct, draggable },
+  components: {VideoUpload, QuestionBank, CourseWatchComment, CourseProduct, draggable},
   data() {
     return {
       duration: null,
@@ -357,8 +381,8 @@ export default {
       isPrivate: null,
       videoUrl: "",
       uploadTypeOptions: [
-        { dictLabel: "线路一", dictValue: 2 },
-        { dictLabel: "线路二", dictValue: 3 },
+        {dictLabel: "线路一", dictValue: 2},
+        {dictLabel: "线路二", dictValue: 3},
       ],
       uploadLoading: false,
       courseId: null,
@@ -390,8 +414,7 @@ export default {
         list: [],
         open: false,
         loading: true,
-        form: {
-        }
+        form: {}
       },
       queryParams: {
         pageNum: 1,
@@ -440,10 +463,10 @@ export default {
       // 表单校验
       rules: {
         title: [
-          { required: true, message: "小节名称不能为空", trigger: "change" }
+          {required: true, message: "小节名称不能为空", trigger: "change"}
         ],
         courseSort: [
-          { required: true, message: "排序不能为空", trigger: "change" }
+          {required: true, message: "排序不能为空", trigger: "change"}
         ],
 
       },
@@ -581,7 +604,7 @@ export default {
         return
       }
 
-      const params = { ids: projectIds }
+      const params = {ids: projectIds}
       getByIds(params).then(response => {
         if (response.code === 200) {
           response.data.forEach(item => {
@@ -804,7 +827,7 @@ export default {
             this.form.packageJson = JSON.stringify(this.packageList);
           }
           if (this.form.courseProducts != null) {
-            this.form.productId = this.form.courseProducts[0].id
+            this.form.productId = this.form.courseProducts.map(item => item.id).join(',');
           }
           if (this.form.videoId != null) {
             updateUserCourseVideo(this.form).then(response => {
@@ -851,7 +874,23 @@ export default {
       }).then(() => {
         this.getList();
         this.msgSuccess("删除成功");
-      }).catch(() => { });
+      }).catch(() => {
+      });
+    },
+    /** 同步模板数据*/
+    handleSync() {
+      const courseId = this.courseId;
+      this.$confirm('是否同步课程数据至模板', "确认", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function () {
+        return syncTemplate(courseId);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("同步成功");
+      }).catch(() => {
+      });
     },
 
     handleCourseSort() {
@@ -879,7 +918,7 @@ export default {
 
     saveSorts() {
       let list = this.userCourseVideoSortList.filter(e => e.courseSort != e.newCourseSort).map(e => {
-        return { courseSort: e.newCourseSort, videoId: e.videoId }
+        return {courseSort: e.newCourseSort, videoId: e.videoId}
       })
       this.loading3 = true;
       sortCourseVideo(list).then(e => {

+ 201 - 18
src/views/components/his/integralOrderDetails.vue

@@ -64,6 +64,12 @@
               }}</span></el-descriptions-item>
           <el-descriptions-item label="快递单号"><span v-if="item != null">{{ item.deliverySn
               }}</span></el-descriptions-item>
+          <!-- <el-descriptions-item label="物流状态"><span v-if="item != null">{{ item.deliveryType
+              }}</span></el-descriptions-item> -->
+          <el-descriptions-item label="物流状态" ><dict-tag :options="deliveryStatusOptions" :value="item.deliveryStatus"/></el-descriptions-item>
+
+          <el-descriptions-item label="物流跟踪状态" ><span v-if="item!=null"><dict-tag :options="deliveryTypeOptions" :value="item.deliveryType"/> </span></el-descriptions-item>
+
           <el-descriptions-item label="发货时间"><span v-if="item != null">{{ item.deliveryTime
               }}</span></el-descriptions-item>
           <el-descriptions-item label="提交时间"><span v-if="item != null">{{ item.createTime
@@ -100,7 +106,7 @@
 
       </el-table>
     </div>
-    <el-dialog width="50%" title="发货" :visible.sync="sendVisible" append-to-body @close="sendCancel">
+    <el-dialog width="35%" title="发货" :visible.sync="sendVisible" append-to-body @close="sendCancel">
       <el-form ref="form" :model="form" label-width="120px">
         <el-form-item label="快递名称" prop="deliveryName">
           <el-select v-model="selectedExpress" placeholder="请选择快递名称" value-key="name">
@@ -118,11 +124,10 @@
         </el-form-item>
 
         <!-- 代服账号选择表格 -->
-        <el-form-item label="代服账号选择" prop="selectedAccount" required>
+        <!-- <el-form-item label="代服账号选择" prop="selectedAccount" required>
           <div style="border: 1px solid #e6ebf5; border-radius: 4px; padding: 10px;">
             <el-table ref="accountTable" :data="tableData" highlight-current-row
               @current-change="handleAccountSelectionChange" style="width: 100%" size="small" max-height="400">
-              <!-- 固定高度,超出滚动 -->
 
               <el-table-column type="index" width="60" label="序号" align="center"></el-table-column>
               <el-table-column prop="loginAccount" label="登录账号" align="center" width="120"
@@ -147,7 +152,7 @@
 
             </el-table>
 
-            <!-- 分页 -->
+            
             <el-pagination small layout="prev, pager, next" :total="total" :page-size="queryParams.pageSize"
               :current-page="queryParams.pageNum" @current-change="handlePageChange"
               style="margin-top: 10px; text-align: center;">
@@ -157,13 +162,13 @@
               提示:点击表格行选择代服账号
             </div>
 
-            <!-- 显示选中信息 -->
+            
             <div v-if="selectedRow" style="margin-top: 10px; padding: 8px; background: #f5f7fa; border-radius: 4px;">
               <span style="color: #67C23A;">已选择:</span>
               <span>{{ selectedRow.loginAccount }} ({{ selectedRow.senderName }})</span>
             </div>
           </div>
-        </el-form-item>
+        </el-form-item> -->
 
       </el-form>
       <div slot="footer" class="dialog-footer">
@@ -198,11 +203,74 @@
         <el-form-item label="订单状态" prop="status">
           <el-select v-model="editForm.status" placeholder="请选择状态" clearable size="small" filterable>
             <el-option v-for="dict in statusOptions" :key="dict.dictValue" :label="dict.dictLabel"
-              :value="dict.dictValue" />
+              :value="dict.dictValue" :disabled="dict.dictValue==4||dict.dictValue==6||dict.dictValue==-1"/>
           </el-select>
         </el-form-item>
-        <el-form-item label="详情地址" prop="userAddress">
-          <el-input v-model="editForm.userAddress" placeholder="请输入" />
+        <el-form-item label="收货地址" required v-if="item.status == 1">
+          <div style="margin-bottom: 10px;">
+            <el-select 
+              v-model="selectedProvince" 
+              placeholder="请选择省" 
+              style="width: 32%; margin-right: 1%"
+              @change="onProvinceChange"
+              clearable
+            >
+              <el-option
+                v-for="item in provinceOptions"
+                :key="item.value"
+                :label="item.label"
+                :value="item"
+              />
+            </el-select>
+            
+            <el-select 
+              v-model="selectedCity" 
+              placeholder="请选择市" 
+              style="width: 32%; margin-right: 1%"
+              @change="onCityChange"
+              :disabled="!selectedProvince"
+              clearable
+            >
+              <el-option
+                v-for="item in cityOptions"
+                :key="item.value"
+                :label="item.label"
+                :value="item"
+              />
+            </el-select>
+            
+            <el-select 
+              v-model="selectedDistrict" 
+              placeholder="请选择区" 
+              style="width: 32%;"
+              @change="onDistrictChange"
+              :disabled="!selectedCity"
+              clearable
+            >
+              <el-option
+                v-for="item in districtOptions"
+                :key="item.value"
+                :label="item.label"
+                :value="item"
+              />
+            </el-select>
+          </div>
+          
+          <el-input 
+            v-model="detailAddress" 
+            placeholder="请输入详细地址(街道、门牌号等)" 
+            style="width: 100%"
+            clearable
+          />
+        </el-form-item>
+
+        <!-- 其他状态显示只读的地址信息 -->
+        <el-form-item label="收货地址" v-else>
+          <el-input 
+            v-model="editForm.userAddress" 
+            placeholder="请输入" 
+            disabled
+          />
         </el-form-item>
 
         <!-- 修改:快递信息改为下拉选择 -->
@@ -220,12 +288,11 @@
         </el-form-item>
 
         <!-- 代服账号选择表格 - 使用同一个数据源 -->
-        <el-form-item label="代服账号" prop="loginAccount">
+        <!-- <el-form-item label="代服账号" prop="loginAccount">
           <div style="border: 1px solid #e6ebf5; border-radius: 4px; padding: 10px;">
             <el-table ref="editAccountTable" :data="tableData" highlight-current-row
               @current-change="handleEditAccountSelectionChange" style="width: 100%" size="small" max-height="300">
 
-              <!-- 列定义与发货弹窗相同 -->
               <el-table-column type="index" width="60" label="序号" align="center"></el-table-column>
               <el-table-column prop="loginAccount" label="登录账号" align="center" width="120"
                 show-overflow-tooltip></el-table-column>
@@ -249,7 +316,6 @@
 
             </el-table>
 
-            <!-- 分页 - 使用同一个分页数据 -->
             <el-pagination small layout="prev, pager, next" :total="total" :page-size="queryParams.pageSize"
               :current-page="queryParams.pageNum" @current-change="handlePageChange"
               style="margin-top: 10px; text-align: center;">
@@ -259,14 +325,13 @@
               提示:点击表格行选择代服账号
             </div>
 
-            <!-- 显示选中信息 -->
             <div v-if="editSelectedRow"
               style="margin-top: 10px; padding: 8px; background: #f5f7fa; border-radius: 4px;">
               <span style="color: #67C23A;">已选择:</span>
               <span>{{ editSelectedRow.loginAccount }} ({{ editSelectedRow.senderName }})</span>
             </div>
           </div>
-        </el-form-item>
+        </el-form-item> -->
 
         <el-form-item label="备注" prop="remark">
           <el-input v-model="editForm.remark" placeholder="请输入备注" />
@@ -280,13 +345,14 @@
 </template>
 
 <script>
-import { getExpress,mandatoryRefunds,finishOrder, listIntegralOrder, sendgoods, getIntegralOrder, delIntegralOrder, addIntegralOrder, updateIntegralOrder, exportIntegralOrder, getOrderUserPhone } from "@/api/his/integralOrder";
+import { getExpress,mandatoryRefunds,getCitys,finishOrder, listIntegralOrder, sendgoods, getIntegralOrder, delIntegralOrder, addIntegralOrder, updateIntegralOrder, exportIntegralOrder, getOrderUserPhone } from "@/api/his/integralOrder";
 import { getExpressList } from "@/api/his/express";
 import { listAccount } from "@/api/his/dfAccount";
 export default {
   name: "integralOrder",
   data() {
     return {
+      deliveryStatusOptions:[],
       selectedExpress: null,
       expressOption: [],
       expressDialog: {
@@ -300,7 +366,7 @@ export default {
       editForm: {
         orderId: null,
         status: null,
-        userAddress: null,
+        userAddress: null, // 这个字段将存储拼接后的完整地址
         remark: "",
         loginAccount: null,
         // 新增快递相关字段
@@ -354,6 +420,15 @@ export default {
       total: 0, // 总条数,
       expressOptions: [], // 物流产品字典
       editSelectedExpress: null,
+      // 地址选择相关数据
+      provinceOptions: [], // 省列表
+      cityOptions: [], // 市列表
+      districtOptions: [], // 区列表
+      selectedProvince: null, // 选中的省
+      selectedCity: null, // 选中的市
+      selectedDistrict: null, // 选中的区
+      detailAddress: '', // 详细地址
+      deliveryTypeOptions:[],
     }
   },
   created() {
@@ -363,6 +438,12 @@ export default {
     this.getDicts("df_account_express").then(response => {
       this.expressOptions = response.data;
     });
+    this.getDicts("sys_store_order_delivery_status").then(response => {
+          this.deliveryStatusOptions = response.data;
+    });
+    this.getDicts("sys_delivery_type").then(response => {
+              this.deliveryTypeOptions = response.data;
+            });
   },
   watch: {
     selectedExpress(newVal) {
@@ -454,10 +535,15 @@ export default {
           }
         }
 
-        // 3. 等待代服账号数据加载完成
+        // 3. 初始化地址选择器(只在待发货状态时执行)
+        if (this.item.status == 1) {
+          await this.initAddressSelector();
+        }
+
+        // 4. 等待代服账号数据加载完成
         await this.getAccountList();
 
-        // 4. 在数据都加载完成后尝试选中对应的行
+        // 5. 在数据都加载完成后尝试选中对应的行
         if (this.item.loginAccount) {
           this.$nextTick(() => {
             this.selectCurrentAccount();
@@ -467,6 +553,88 @@ export default {
         console.error('加载数据失败:', error);
       }
     },
+    // 初始化地址选择器
+    async initAddressSelector() {
+      // 重置地址选择器
+      this.provinceOptions = [];
+      this.cityOptions = [];
+      this.districtOptions = [];
+      this.selectedProvince = null;
+      this.selectedCity = null;
+      this.selectedDistrict = null;
+      this.detailAddress = '';
+
+      // 只有在待发货状态时才初始化地址选择器
+      if (this.item.status == 1) {
+        // 获取省份数据
+        try {
+          const response = await getCitys(); // 假设getCitys是您导入的方法
+          this.provinceOptions = response.data || [];
+          
+          // 如果现有地址不为空,尝试解析并设置初始值
+          if (this.item.userAddress) {
+            this.parseExistingAddress(this.item.userAddress);
+          }
+        } catch (error) {
+          console.error('获取地址数据失败:', error);
+        }
+      }
+    },
+
+    // 解析现有地址
+    parseExistingAddress(address) {
+      if (!address || this.item.status != 1) return;
+      
+      // 假设地址格式为 "省 市 区 详细地址"
+      const parts = address.split(' ');
+      if (parts.length >= 4) {
+        const provinceName = parts[0];
+        const cityName = parts[1];
+        const districtName = parts[2];
+        this.detailAddress = parts.slice(3).join(' '); // 剩余部分作为详细地址
+        
+        // 设置省份
+        const province = this.provinceOptions.find(p => p.label === provinceName);
+        if (province) {
+          this.selectedProvince = province;
+          this.onProvinceChange(province);
+          
+          // 设置城市
+          const city = province.children?.find(c => c.label === cityName);
+          if (city) {
+            this.selectedCity = city;
+            this.onCityChange(city);
+            
+            // 设置区域
+            const district = city.children?.find(d => d.label === districtName);
+            if (district) {
+              this.selectedDistrict = district;
+            }
+          }
+        }
+      } else {
+        // 如果格式不匹配,将整个地址作为详细地址
+        this.detailAddress = address;
+      }
+    },
+    // 省份选择变化
+    onProvinceChange(province) {
+      this.selectedCity = null;
+      this.selectedDistrict = null;
+      this.cityOptions = province?.children || [];
+      this.districtOptions = [];
+    },
+
+    // 城市选择变化
+    onCityChange(city) {
+      this.selectedDistrict = null;
+      this.districtOptions = city?.children || [];
+    },
+
+    // 区域选择变化
+    onDistrictChange(district) {
+      // 可以在这里处理区域选择后的逻辑
+    },
     // 选中当前账号(修改订单弹窗使用)
     selectCurrentAccount() {
       if (!this.item.loginAccount || !this.tableData || this.tableData.length === 0) {
@@ -492,6 +660,21 @@ export default {
     },
     //修改订单状态
     submitEditForm() {
+      // 只有在待发货状态(status=1)时才更新地址
+      if (this.item.status == 1) {
+        // 拼接完整地址
+        const province = this.selectedProvince ? this.selectedProvince.label : '';
+        const city = this.selectedCity ? this.selectedCity.label : '';
+        const district = this.selectedDistrict ? this.selectedDistrict.label : '';
+        const detail = this.detailAddress || '';
+        
+        // 使用空格拼接四个字段
+        this.editForm.userAddress = `${province} ${city} ${district} ${detail}`.trim();
+      } else {
+        // 其他状态保持原地址不变
+        this.editForm.userAddress = this.item.userAddress;
+      }
+      
       this.$refs["editForm"].validate(valid => {
         if (valid) {
           updateIntegralOrder(this.editForm).then(response => {

+ 50 - 3
src/views/course/courseQuestionBank/index.vue

@@ -307,13 +307,31 @@
     <el-dialog title="导入结果" :close-on-press-escape="false" :close-on-click-modal="false" :visible.sync="importMsgOpen" width="500px" append-to-body>
       <div class="import-msg" v-html="importMsg">
       </div>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="importMsgOpen = false">关 闭</el-button>
+        <el-button
+          v-if="failList && failList.length > 0"
+          type="primary"
+          @click="handleExportFailList(failList)"
+          :loading="exportFailLoading"
+        >导出失败信息</el-button>
+      </div>
     </el-dialog>
 
   </div>
 </template>
 
 <script>
-import { listCourseQuestionBank, getCourseQuestionBank, delCourseQuestionBank, addCourseQuestionBank, updateCourseQuestionBank, exportCourseQuestionBank, importTemplate } from "@/api/course/courseQuestionBank";
+import {
+  listCourseQuestionBank,
+  getCourseQuestionBank,
+  delCourseQuestionBank,
+  addCourseQuestionBank,
+  updateCourseQuestionBank,
+  exportCourseQuestionBank,
+  importTemplate,
+  exportFail
+} from "@/api/course/courseQuestionBank";
 import { getToken } from "@/utils/auth";
 import {
   listUserCourseCategory,
@@ -334,7 +352,7 @@ export default {
   },
   data() {
     return {
-
+      exportFailLoading: false,
       //单选
       selectedAnswer:null,
       //多选
@@ -397,6 +415,7 @@ export default {
       },
       // 导入
       importMsgOpen:false,
+      failList:[],
       importMsg: '',
       upload: {
         // 是否显示弹出层(文件导入)
@@ -426,6 +445,33 @@ export default {
     getOptionLabel(index) {
       return this.alphabet[index];
     },
+// 导出失败信息
+    handleExportFailList(failList) {
+      if (!failList || failList.length === 0) {
+        this.msgWarning("没有失败信息可导出");
+        return;
+      }
+
+
+      this.exportFailLoading = true;
+      this.$confirm('是否确认导出失败信息?', "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "info"
+      }).then(() => {
+        // 调用导出失败信息的API
+        exportFail(failList).then(response => {
+          this.download(response.msg);
+          this.msgSuccess("失败信息导出成功");
+          this.exportFailLoading = false;
+        }).catch(() => {
+          this.msgError("失败信息导出失败");
+          this.exportFailLoading = false;
+        });
+      }).catch(() => {
+        this.exportFailLoading = false;
+      });
+    },
 
     // 分类树
     getCategoryTree() {
@@ -661,7 +707,8 @@ export default {
       this.upload.isUploading = false;
       this.$refs.upload.clearFiles();
       this.importMsgOpen=true;
-      this.importMsg = response.msg
+      this.importMsg = response.data.message
+      this.failList = response.data.failList
       this.getList();
     },
   }

+ 46 - 10
src/views/course/userCoursePeriod/index.vue

@@ -366,15 +366,33 @@
             />
           </el-select>
         </el-form-item>
-        <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
-              v-for="dict in videoList"
-              :key="dict.dictValue"
-              :label="dict.dictLabel"
-              :value="parseInt(dict.dictValue)"
-            />
-          </el-select>
+         <el-form-item label="小节" prop="videoIds">
+          <div style="display: flex; align-items: center;">
+            <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="flex: 1; margin-right: 10px;"
+              :value-key="'dictValue'">
+              <el-option
+                v-for="dict in videoList"
+                :key="dict.dictValue"
+                :label="dict.dictLabel"
+                :value="parseInt(dict.dictValue)"
+              />
+            </el-select>
+            <el-button
+              type="primary"
+              size="small"
+              @click="selectAllVideos"
+              :disabled="videoList.length === 0 || !course.form.courseId">
+              全选
+            </el-button>
+          </div>
         </el-form-item>
         <el-form-item label="看课时间" prop="timeRange">
           <el-time-picker
@@ -439,7 +457,21 @@
           </el-date-picker>
           <p style="color: red;margin: 0;font-size: 12px">超过领取红包时间,只允许看课,不允许领取红包</p>
         </el-form-item>
+         <!--  是否批量修改开关 0-关 1-开 默认关闭 -->
+      </el-form>
+      <el-form>
+        <el-form-item label="是否批量修改" prop="batchUpdateSwitch" label-width="110px">
+          <el-radio-group
+          v-model="updateCourse.form.batchUpdateSwitch"
+          >
+          <el-radio :label="0">关</el-radio>
+          <el-radio :label="1">开</el-radio>
+          </el-radio-group>
+          <br />
+          <span style="color: red;margin: 0;font-size: 12px">批量修改开关开启后,后续的课程会默认+1修改课程和红包时间</span>
+        </el-form-item>
       </el-form>
+
       <div slot="footer" class="dialog-footer">
         <el-button type="primary" @click="submitUpdateCourseForm">确 定</el-button>
         <el-button @click="closeUpdateCourse">取 消</el-button>
@@ -706,7 +738,8 @@ export default {
         ids: [],
         form: {
           timeRange: null,
-          joinTime: null
+          joinTime: null,
+          batchUpdateSwitch: 0
         },
       },
       // 表单校验
@@ -855,6 +888,9 @@ export default {
 
   },
   methods: {
+    selectAllVideos() {
+      this.course.form.videoIds = this.videoList.map(video => parseInt(video.dictValue));
+    },
     /** 删除按钮操作 */
     async handleDeleteCourse(row) {
       const periodDayIds = row.id || this.updateCourse.ids;

+ 42 - 10
src/views/course/userCoursePeriod/statistics.vue

@@ -7,7 +7,11 @@
           <el-select
             v-model="queryParams.videoIdList"
             multiple
-            placeholder="请选择营期课程"
+            filterable
+            remote
+            :remote-method="handleCourseSearch"
+            :loading="courseLoading"
+            placeholder="请输入关键词搜索营期课程"
             style="width: 400px"
           >
             <el-option
@@ -146,6 +150,7 @@ export default {
     return {
       // 遮罩层
       loading: false,
+      courseLoading: false,
       // 总条数
       total: 0,
       // 课程选项
@@ -168,7 +173,7 @@ export default {
         pageNum: 1,
         pageSize: 100,
         videoIdList: [],
-        // videoId: '',
+        companyId: '',
         periodId: ''
       },
       // 是否已初始化
@@ -198,10 +203,10 @@ export default {
     /** 初始化数据 */
     initializeData() {
       this.getCourseOptions();
-      this.getCountList();
-      this.getCompanyOptions()
+      this.getCompanyOptions();
       this.initialized = true;
     },
+
     getCompanyOptions() {
       getPeriodCompanyList({
         periodId: this.periodId
@@ -209,9 +214,10 @@ export default {
         this.companyOptions = response.data || [];
       });
     },
+
     /** 获取课程选项 */
     getCourseOptions() {
-      this.loading = true;
+      this.courseLoading = true;
       getDays(this.queryParams).then(r => {
         if (r.code === 200) {
           this.courseOptions = r.rows;
@@ -219,19 +225,44 @@ export default {
         } else {
           this.$message.error(r.msg || '获取数据失败');
         }
-        this.loading = false;
+        this.courseLoading = false;
       }).catch(() => {
-        this.loading = false;
+        this.courseLoading = false;
       });
     },
+
+    /** 营期课程搜索 */
+    handleCourseSearch(query) {
+      if (query !== '') {
+        this.courseLoading = true;
+        // 这里可以根据实际接口调整搜索参数
+        getDays({
+          periodId: this.queryParams.periodId,
+          pageNum: 1,
+          pageSize: 100,
+          videoName: query // 假设接口支持按课程名称搜索
+        }).then(r => {
+          if (r.code === 200) {
+            this.courseOptions = r.rows;
+          } else {
+            this.$message.error(r.msg || '搜索失败');
+          }
+          this.courseLoading = false;
+        }).catch(() => {
+          this.courseLoading = false;
+        });
+      } else {
+        // 如果搜索词为空,重新加载所有课程
+        this.getCourseOptions();
+      }
+    },
+
     /** 查询按钮操作 */
     handleQuery() {
       this.queryParams.pageNum = 1;
       this.getCountList();
     },
-    /** 课程选择变化 */
-    handleCourseChange() {
-    },
+
     /** 获取列表数据 */
     getCountList() {
       this.loading = true;
@@ -255,6 +286,7 @@ export default {
         this.loading = false;
       });
     },
+
     /** 计算总统计数据 */
     calculateTotalStatistics() {
       // 初始化统计数据

+ 2 - 2
src/views/his/company/index.vue

@@ -1004,7 +1004,7 @@ export default {
       this.redSubmit=false
       this.redRechargeForm.companyId = row.companyId
       this.redRechargeForm.companyName = row.companyName
-      this.redRechargeForm.balance = row.money
+      this.redRechargeForm.balance = row.redPackageMoney
       this.redRechargeForm.money = null
       this.redRecharge.open = true
     },
@@ -1019,7 +1019,7 @@ export default {
     handleRedDeduct(row) {
       this.redDeductForm.companyId = row.companyId
       this.redDeductForm.companyName = row.companyName
-      this.redDeductForm.balance = row.money
+      this.redDeductForm.balance = row.redPackageMoney
       this.redDeductForm.money = null
       this.redDeduct.open = true
     },

+ 532 - 5
src/views/his/integralOrder/index.vue

@@ -82,6 +82,17 @@
           />
         </el-select>
       </el-form-item>
+      <!-- 这里就是之前添加的ERP账号下拉框 -->
+      <el-form-item label="ERP" prop="loginAccount">
+        <el-select v-model="queryParams.loginAccount" placeholder="ERP账号" size="small" clearable>
+          <el-option
+            v-for="account in erpAccountList"
+            :key="account"
+            :label="account"
+            :value="account"
+          />
+        </el-select>
+      </el-form-item>
       <el-form-item>
         <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
@@ -100,7 +111,28 @@
           v-hasPermi="['his:integralOrder:export']"
         >导出</el-button>
       </el-col>
+      
       <el-col :span="1.5">
+          <el-button
+            type="info"
+            plain
+            icon="el-icon-upload2"
+            size="mini"
+            @click="handleImportStatus"
+          >导入订单状态</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-tooltip content="默认erp推送手机号" placement="top">
+          <el-button
+            type="warning"
+            plain
+            icon="el-icon-phone"
+            size="mini"
+            @click="handleErpPhone"
+          >推送手机号码</el-button>
+        </el-tooltip>
+      </el-col>
+<el-col :span="1.5">
         <el-button
           type="info"
           plain
@@ -108,9 +140,29 @@
           size="mini"
           @click="handleImport"
           v-hasPermi="['his:integralOrder:exportDeliver']"
+          v-show="actName === '6'||actName === '1'"
         >导入发货</el-button>
       </el-col>
-
+      <el-col :span="1.5">
+        <el-button
+          type="success"
+          plain
+          icon="el-icon-s-operation"
+          size="mini"
+          @click="handleDataSort"
+          v-show="actName === '6'"
+        >数据分拣</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleCreateErp"
+          v-show="actName === '6'"
+        >创建ERP</el-button>
+      </el-col>
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
     <el-tabs type="card" v-model="actName" @tab-click="handleClickX">
@@ -119,12 +171,14 @@
     </el-tabs>
     <el-table v-loading="loading" border :data="integralOrderList" @selection-change="handleSelectionChange">
       <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="ERP账号" align="center" prop="loginAccount" />
+      <el-table-column label="ERP电话" align="center" prop="erpPhone" />
       <el-table-column label="订单编号" align="center" prop="orderCode" />
       <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 />
       <el-table-column label="支付积分" align="center" prop="integral" />
-      <el-table-column label="支付金额" align="center" prop="cash" />
+      <el-table-column label="支付金额" align="center" prop="payMoney" />
       <el-table-column label="状态" align="center" prop="status">
         <template slot-scope="scope">
           <dict-tag :options="statusOptions" :value="scope.row.status"/>
@@ -259,16 +313,170 @@
          <el-button @click="upload.open = false">取 消</el-button>
        </div>
      </el-dialog>
+     <!-- 数据分拣弹窗 -->
+    <el-dialog :title="erpAccountDialog.title" :visible.sync="erpAccountDialog.open" width="600px" append-to-body>
+      <div v-loading="erpAccountDialog.loading">
+        <el-form :model="erpAccountForm" label-width="100px">
+          <el-form-item label="ERP账户" required>
+            <el-select 
+              v-model="erpAccountForm.selectedAccount" 
+              placeholder="请选择ERP账户" 
+              style="width: 100%"
+              filterable
+            >
+              <el-option
+                v-for="account in erpAccountList"
+                :key="account"
+                :label="account"
+                :value="account"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="推送手机号">
+            <el-select 
+              v-model="erpAccountForm.selectedPhones" 
+              multiple 
+              placeholder="请选择推送手机号,不填默认订单用户电话"
+              style="width: 100%"
+              filterable
+            >
+              <el-option
+                v-for="phone in phoneList"
+                :key="phone.phone"
+                :label="phone.phone"
+                :value="phone.phone"
+              />
+            </el-select>
+          </el-form-item>
+        </el-form>
+        
+        <!-- 订单统计信息 -->
+        <div class="order-summary" v-if="orderSummary">
+          <el-divider content-position="left">订单统计</el-divider>
+          <el-row :gutter="20">
+            <el-col :span="8">
+              <div class="summary-item">
+                <span class="label">选中订单数:</span>
+                <span class="value">{{ orderSummary.selectedCount }}</span>
+              </div>
+            </el-col>
+            <el-col :span="8">
+              <div class="summary-item">
+                <span class="label">总金额:</span>
+                <span class="value">¥{{ orderSummary.totalAmount }}</span>
+              </div>
+            </el-col>
+            <el-col :span="8">
+              <div class="summary-item">
+                <span class="label">查询条件订单:</span>
+                <span class="value">{{ orderSummary.queryCount }}</span>
+              </div>
+            </el-col>
+          </el-row>
+        </div>
+      </div>
+      
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="cancelErpAccountDialog">取 消</el-button>
+        <el-button 
+          type="primary" 
+          @click="confirmCreateErpOrder"
+          :disabled="!erpAccountForm.selectedAccount"
+          :loading="erpAccountDialog.submitting"
+        >确认</el-button>
+      </div>
+    </el-dialog>
+    <!-- 推送手机号码管理弹窗 -->
+    <el-dialog :title="erpPhone.title" :visible.sync="erpPhone.open" width="600px" append-to-body>
+      <div style="margin-bottom: 20px;">
+        <el-button type="primary" size="small" @click="handleAddPhone">新增手机号</el-button>
+      </div>
+      <el-table :data="phoneList" border style="width: 100%">
+        <el-table-column prop="phone" label="手机号" align="center">
+          <template slot-scope="scope">
+            <el-input 
+              v-if="scope.row.editing" 
+              v-model="scope.row.phone" 
+              placeholder="请输入手机号"
+              @blur="validatePhone(scope.row)"
+              @keyup.enter.native="handleSavePhone(scope.$index)"
+            />
+            <span v-else>{{ scope.row.phone }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="center" width="300">
+          <template slot-scope="scope">
+            <el-button 
+              v-if="scope.row.editing" 
+              type="success" 
+              size="mini" 
+              @click="handleSavePhone(scope.$index)"
+            >保存</el-button>
+            <el-button 
+              v-if="scope.row.editing" 
+              type="info" 
+              size="mini" 
+              @click="handleCancelEdit(scope.$index)"
+            >取消</el-button>
+            <el-button 
+              v-if="!scope.row.editing" 
+              type="primary" 
+              size="mini" 
+              @click="handleEditPhone(scope.$index)"
+            >修改</el-button>
+            <el-button 
+              type="danger" 
+              size="mini" 
+              @click="handleDeletePhone(scope.$index)"
+            >删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="handleSavePhoneList">确 定</el-button>
+        <el-button @click="handleCancelPhoneDialog">取 消</el-button>
+      </div>
+    </el-dialog>
+
+    <el-dialog :title="uploadStatus.title" :visible.sync="uploadStatus.open" width="400px" append-to-body>
+        <el-upload
+          ref="uploadStatus"
+          :limit="1"
+          accept=".xlsx, .xls"
+          :headers="uploadStatus.headers"
+          :action="uploadStatus.url + '?updateSupport=' + upload.updateSupport"
+          :disabled="uploadStatus.isUploading"
+          :on-progress="handleFileUploadProgressOrder"
+          :on-success="handleFileSuccessOrder"
+          :auto-upload="false"
+          drag
+        >
+          <i class="el-icon-upload"></i>
+          <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
+          <div class="el-upload__tip text-center" slot="tip">
+            <div class="el-upload__tip" slot="tip">
+            </div>
+            <span>仅允许导入xls、xlsx格式文件。</span>
+            <el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" @click="importUpdateOrderTemplate">下载模板</el-link>
+          </div>
+        </el-upload>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitOrderStatusFileForm">确 定</el-button>
+        <el-button @click="uploadStatus.open = false">取 消</el-button>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
 <script>
-import {importTemplate, listIntegralOrder,importExpressTemplate, getIntegralOrder, delIntegralOrder, addIntegralOrder, updateIntegralOrder, exportIntegralOrder,cancelOrder } from "@/api/his/integralOrder";
+import {importTemplate,batchSetErpOrder,batchCreateErpOrder,listIntegralOrder,getIntegralTemplate, getIntegralOrder, delIntegralOrder, addIntegralOrder, updateIntegralOrder, exportIntegralOrder,cancelOrder } from "@/api/his/integralOrder";
 import integralOrderDetails from '../../components/his/integralOrderDetails.vue';
 import { getToken } from "@/utils/auth";
 import {getCompanyList} from "@/api/company/company";
 import {getAllUserlist} from "@/api/company/companyUser";
 import {getQwUserInfo} from "@/api/qw/qwUser";
+import {getErpAccount } from "@/api/his/storeOrder";
+import {queryErpPhone, saveErpPhone} from "@/api/his/storeOrder";
 
 export default {
   name: "IntegralOrder",
@@ -331,6 +539,7 @@ export default {
         companyUserId:null,
         qwUserId:null,
         companyId:null,
+        loginAccount: null  // 添加ERP账号筛选字段
       },
        createTime:null,
       qwCompanyList:[],
@@ -346,7 +555,44 @@ export default {
         status: [
           { required: true, message: "1:待发货;2:待收货;3:已完成不能为空", trigger: "change" }
         ],
-      }
+      },
+      dataSortOpen: false, // 数据分拣弹窗显示控制
+      erpAccountDialog: {
+        open: false,
+        loading: false,
+        submitting: false,
+        title: "数据分拣"
+      },
+      erpAccountForm: {
+        selectedAccount: null,
+        selectedPhones: [] // 添加推送手机号字段
+      },
+      erpAccountList: [],
+      orderSummary: {
+        selectedCount: 0,
+        totalAmount: 0,
+        queryCount: 0
+      },
+      erpPhone: {
+      open: false,
+        title: "设置推送手机号"
+      },
+      phoneList: [], // 手机号列表
+      originalPhoneList: [], // 原始手机号列表,用于取消时恢复
+      uploadStatus: {
+        // 是否显示弹出层
+        open: false,
+        // 弹出层标题
+        title: "",
+        // 是否禁用上传
+        isUploading: false,
+        // 是否更新已经存在的用户数据
+        updateSupport: 0,
+        // 设置上传的请求头部
+        headers: { Authorization: "Bearer " + getToken() },
+        // 上传的地址
+        url: process.env.VUE_APP_BASE_API + "/his/integralOrder/importOrderStatusData"
+      },
     };
   },
   created() {
@@ -360,8 +606,29 @@ export default {
     getCompanyList().then(response => {
       this.qwCompanyList = response.data;
     });
+    this.loadErpAccountData();
   },
   methods: {
+    handleImportStatus() {
+        this.uploadStatus.title = "导入";
+        this.uploadStatus.open = true;
+       },
+    handleFileUploadProgressOrder(event, file, fileList) {
+          this.uploadStatus.isUploading = true;
+        },
+    // 文件上传成功处理
+         handleFileSuccessOrder(response, file, fileList) {
+          this.uploadStatus.open = false;
+          this.uploadStatus.isUploading = false;
+          this.$refs.uploadStatus.clearFiles();
+          this.$alert(response.msg, "导入结果", { dangerouslyUseHTMLString: true });
+          this.getList();
+        },
+    importUpdateOrderTemplate(){
+        getIntegralTemplate().then(response => {
+          this.download(response.msg);
+        });
+      },
      //取消订单
      cancelOrder(orderCode){
       this.$confirm('确定取消此订单?', '提示', {
@@ -565,7 +832,267 @@ export default {
           this.qwUserList=response.data;
         })
       }
-    }
+    },
+    // 数据分拣按钮点击事件
+    handleDataSort() {
+      this.erpAccountDialog.open = true;
+      this.erpAccountDialog.title = "数据分拣";
+      this.getErpPhoneList(); // 加载手机号列表
+      this.updateOrderSummary();
+    },
+    
+    // 加载ERP账户数据
+    async loadErpAccountData() {
+      try {
+        const response = await getErpAccount();
+        this.erpAccountList = response.data;
+      } catch (error) {
+        console.error('加载ERP账户失败:', error);
+      }
+    },
+    
+    // 更新订单统计
+    updateOrderSummary() {
+      // 如果没有选择任何数据,则使用查询出来的所有数据
+      if (this.ids.length === 0) {
+        this.orderSummary.selectedCount = this.total;
+        this.orderSummary.queryCount = this.total;
+      } else {
+        // 如果选择了数据,则使用选中的数据
+        this.orderSummary.selectedCount = this.ids.length;
+        this.orderSummary.queryCount = this.total;
+      }
+      
+      // 计算总金额(这里需要根据实际数据结构调整)
+      this.calculateTotalAmount();
+    },
+    // 计算总金额
+    calculateTotalAmount() {
+      let totalAmount = 0;
+      
+      if (this.ids.length === 0) {
+        // 使用所有查询数据计算金额
+        this.integralOrderList.forEach(order => {
+          totalAmount += parseFloat(order.payMoney || 0);
+        });
+      } else {
+        // 使用选中的数据计算金额
+        const selectedOrders = this.integralOrderList.filter(order => 
+          this.ids.includes(order.orderId)
+        );
+        selectedOrders.forEach(order => {
+          totalAmount += parseFloat(order.payMoney || 0);
+        });
+      }
+      
+      this.orderSummary.totalAmount = totalAmount.toFixed(2);
+    },
+    
+    // 取消ERP账户对话框
+    cancelErpAccountDialog() {
+      this.erpAccountDialog.open = false;
+      this.erpAccountForm.selectedAccount = null;
+    },
+    
+    // 确认创建ERP订单
+    async confirmCreateErpOrder() {
+      // 收集选中的orderId
+      let selectedOrderIds = [];
+      
+      if (this.ids.length === 0) {
+        // 如果没有选择任何数据,使用查询出来的所有数据的orderId
+        selectedOrderIds = this.integralOrderList.map(order => order.orderId);
+      } else {
+        // 如果选择了数据,使用选中的orderId
+        selectedOrderIds = this.ids;
+      }
+      
+      // 收集ERP账户和手机号
+      const selectedErpAccount = this.erpAccountForm.selectedAccount;
+      const selectedPhones = this.erpAccountForm.selectedPhones;
+      
+      // 准备请求参数
+      const params = {
+        orderIds: selectedOrderIds,
+        loginAccount: selectedErpAccount,
+        erpPhones: selectedPhones // 添加手机号参数
+      };
+      try {
+        this.erpAccountDialog.submitting = true;
+        
+        // 根据弹窗标题判断是数据分拣还是创建ERP
+        if (this.erpAccountDialog.title === "数据分拣") {
+          // 调用数据分拣接口
+          const response = await batchSetErpOrder(params);
+          this.$message.success('数据分拣成功');
+        } else if (this.erpAccountDialog.title === "创建ERP") {
+          // 调用创建ERP接口
+          const response = await batchCreateErpOrder(params);
+          // console.log("参数:",params)
+          this.$message.success('创建ERP成功');
+        }
+        
+        // 关闭弹窗
+        this.cancelErpAccountDialog();
+        
+        // 刷新列表
+        this.getList();
+        
+      } catch (error) {
+        console.error('操作失败:', error);
+        this.$message.error('操作失败');
+      } finally {
+        this.erpAccountDialog.submitting = false;
+      }
+    },
+    handleCreateErp() {
+      this.erpAccountDialog.open = true;
+      this.erpAccountDialog.title = "创建ERP";
+      this.getErpPhoneList(); // 加载手机号列表
+      this.updateOrderSummary();
+    },
+    // 获取ERP手机号列表
+    getErpPhoneList() {
+      queryErpPhone().then(response => {
+        if (response.data && response.data != null && response.data.length > 0) {
+          const phones = response.data.filter(phone => phone.trim());
+          this.phoneList = phones.map(phone => ({
+            phone: phone.trim(),
+            editing: false,
+            originalPhone: phone.trim()
+          }));
+        } else {
+          this.phoneList = [];
+        }
+      });
+    },
+    // 取消ERP账户对话框
+    cancelErpAccountDialog() {
+      this.erpAccountDialog.open = false;
+      this.erpAccountForm.selectedAccount = null;
+      this.erpAccountForm.selectedPhones = []; // 清空手机号选择
+    },
+    // 推送手机号码管理
+    handleErpPhone() {
+      this.getErpPhoneList();
+      this.erpPhone.open = true;
+    },
+    // 新增手机号
+    handleAddPhone() {
+      this.phoneList.push({
+        phone: '',
+        editing: true,
+        originalPhone: '',
+        isNew: true
+      });
+    },
+
+    // 编辑手机号
+    handleEditPhone(index) {
+      this.$set(this.phoneList[index], 'editing', true);
+      this.$set(this.phoneList[index], 'originalPhone', this.phoneList[index].phone);
+    },
+
+    // 保存手机号
+    handleSavePhone(index) {
+      const phone = this.phoneList[index].phone.trim();
+      if (!phone) {
+        this.$message.error('手机号不能为空');
+        return;
+      }
+      if (!this.validatePhoneFormat(phone)) {
+        this.$message.error('请输入正确的手机号格式');
+        return;
+      }
+      // 检查是否重复
+      const duplicateIndex = this.phoneList.findIndex((item, idx) => 
+        idx !== index && item.phone === phone
+      );
+      if (duplicateIndex !== -1) {
+        this.$message.error('手机号已存在');
+        return;
+      }
+      this.$set(this.phoneList[index], 'editing', false);
+      this.$set(this.phoneList[index], 'isNew', false);
+    },
+
+    // 取消编辑
+    handleCancelEdit(index) {
+      if (this.phoneList[index].isNew) {
+        // 如果是新增的,直接删除
+        this.phoneList.splice(index, 1);
+      } else {
+        // 如果是编辑的,恢复原值
+        this.$set(this.phoneList[index], 'phone', this.phoneList[index].originalPhone);
+        this.$set(this.phoneList[index], 'editing', false);
+      }
+    },
+
+    // 删除手机号
+    handleDeletePhone(index) {
+      this.$confirm('确认删除该手机号?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.phoneList.splice(index, 1);
+        this.$message.success('删除成功');
+      });
+    },
+
+    // 验证手机号格式
+    validatePhoneFormat(phone) {
+      const phoneRegex = /^1[3-9]\d{9}$/;
+      return phoneRegex.test(phone);
+    },
+
+    // 验证手机号
+    validatePhone(row) {
+      if (row.phone && !this.validatePhoneFormat(row.phone)) {
+        this.$message.error('请输入正确的手机号格式');
+      }
+    },
+
+    // 保存手机号列表
+    handleSavePhoneList() {
+      // 检查是否有正在编辑的项
+      const editingItem = this.phoneList.find(item => item.editing);
+      if (editingItem) {
+        this.$message.error('请先保存正在编辑的手机号');
+        return;
+      }
+      
+      // 检查是否有空的手机号
+      const emptyPhone = this.phoneList.find(item => !item.phone.trim());
+      if (emptyPhone) {
+        this.$message.error('存在空的手机号,请删除或填写完整');
+        return;
+      }
+      
+      // 构造手机号列表
+      const phoneList = this.phoneList.map(item => item.phone);
+      
+      // 调用保存接口
+      saveErpPhone(phoneList).then(response => {
+        if (response.code === 200) {
+          this.$message.success('保存成功');
+          this.erpPhone.open = false;
+        } else {
+          this.$message.error(response.msg || '保存失败');
+        }
+      }).catch(() => {
+        this.$message.error('保存失败');
+      });
+    },
+
+    // 取消手机号对话框
+    handleCancelPhoneDialog() {
+      this.erpPhone.open = false;
+      this.getErpPhoneList(); // 重新加载原始数据
+    },
+    submitOrderStatusFileForm(){
+      this.$refs.uploadStatus.submit();
+    },
   }
 };
 </script>

+ 35 - 1
src/views/hisStore/menu/index.vue

@@ -10,6 +10,17 @@
           @keyup.enter.native="handleQuery"
         />
       </el-form-item>
+
+      <el-form-item label="小程序" prop="appId">
+        <el-select v-model="queryParams.appId" placeholder="请选择所属小程序" clearable size="small">
+          <el-option
+            v-for="dict in appMallOptions"
+            :key="dict.appid"
+            :label="dict.name + '(' + dict.appid + ')'"
+            :value="dict.appid"
+          />
+        </el-select>
+      </el-form-item>
       <el-form-item>
         <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
@@ -55,6 +66,11 @@
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="ID" align="center" prop="menuId" />
       <el-table-column label="菜单名称" align="center" prop="menuName" />
+      <el-table-column label="小程序" align="center" prop="appId" >
+        <template slot-scope="scope">
+          <el-tag prop="appId" v-for="(item, index) in appMallOptions"    v-if="scope.row.appId==item.appid">{{item.name}}</el-tag>
+        </template>
+      </el-table-column>
       <el-table-column label="图标" align="center" width="120">
         <template slot-scope="scope">
           <el-popover
@@ -142,6 +158,16 @@
             />
           </el-select>
         </el-form-item>
+        <el-form-item label="小程序" prop="appId">
+          <el-select style="width: 200px" v-model="form.appId" placeholder="请选择所属小程序" clearable size="small" >
+            <el-option
+              v-for="dict in appMallOptions"
+              :key="dict.appid"
+              :label="dict.name + '(' + dict.appid + ')'"
+              :value="dict.appid"
+            />
+          </el-select>
+        </el-form-item>
         <el-form-item label="状态" prop="isShow">
               <el-radio-group v-model="form.isShow">
                 <el-radio v-for="dict in statusOptions" :key="dict.dictValue" :label="dict.dictValue">{{dict.dictLabel}}</el-radio>
@@ -158,6 +184,7 @@
 
 <script>
 import { listMenu, getMenu, delMenu, addMenu, updateMenu, exportMenu } from "@/api/hisStore/menu";
+import {list as getAppMallOptions} from '@/api/course/coursePlaySourceConfig';
 import Material from '@/components/Material'
 export default {
   name: "Menu",
@@ -171,6 +198,7 @@ export default {
   },
   data() {
     return {
+      appMallOptions:[],
       activeName:"00",
       menuLinkTypeOptions:[],
       menuTypeOptions:[
@@ -231,11 +259,17 @@ export default {
     this.getDicts("common_status").then((response) => {
       this.statusOptions = response.data;
     });
-
+    this.getAppMallOptions();
     this.getList();
 
   },
   methods: {
+    // 获取小程序选项列表
+    getAppMallOptions() {
+      getAppMallOptions({pageNum:1,pageSize:100,isMall:1}).then(response => {
+        this.appMallOptions = response.rows;
+      })
+    },
     handleClick(tab,event){
       this.activeName=tab.name;
       if(tab.name=="00"){

+ 67 - 21
src/views/hisStore/storeOrder/healthStoreList.vue

@@ -304,6 +304,16 @@
         >批量导入物流单号
         </el-button>
       </el-col>
+      <el-col :span="1.5">
+        <el-button
+          v-hasPermi="['store:storeOrder:healthExportShippingOrder']"
+          icon="el-icon-tickets"
+          size="mini"
+          type="success"
+          @click="handleExportShippingOrder"
+        >导出发货单
+        </el-button>
+      </el-col>
       <el-col :span="1.5">
         <el-button
           type="danger"
@@ -724,24 +734,24 @@
       width="35%"
     >
       <span slot="footer" class="dialog-footer">
-        <!-- 小程序Appid选择 -->
-<!--        <el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">-->
-<!--          <el-form-item label="小程序:" prop="miniAppId">-->
-<!--        <el-select-->
-<!--          v-model="ruleForm.miniAppId"-->
-<!--          clearable-->
-<!--          placeholder="请选择发货小程序"-->
-<!--          style="width: 100%"-->
-<!--        >-->
-<!--          <el-option-->
-<!--            v-for="item in miniAppList"-->
-<!--            :key="item.appId"-->
-<!--            :label="item.appName"-->
-<!--            :value="item.appId"-->
-<!--          />-->
-<!--        </el-select>-->
-<!--      </el-form-item>-->
-<!--        </el-form>-->
+         小程序Appid选择
+        <el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
+          <el-form-item label="小程序:" prop="miniAppId">
+        <el-select
+          v-model="ruleForm.miniAppId"
+          clearable
+          placeholder="请选择发货小程序"
+          style="width: 100%"
+        >
+          <el-option
+            v-for="item in miniAppList"
+            :key="item.appId"
+            :label="item.appName"
+            :value="item.appId"
+          />
+        </el-select>
+      </el-form-item>
+        </el-form>
 
         <el-upload ref="upload" :action="orderUpload.url" :auto-upload="false" :disabled="orderUpload.isUploading" :headers="orderUpload.headers"
                    :limit="1" :on-progress="handleFileUploadProgress"
@@ -947,7 +957,7 @@ import {
   delStoreOrder,
   addStoreOrder,
   updateStoreOrder,
-  exportHealthStoreOrder, exportHealthStoreOrderDetails, exportHealthStoreOrderItemsDetails,getErpAccount,
+  exportHealthStoreOrder, exportHealthStoreOrderDetails, exportHealthStoreOrderItemsDetails,getErpAccount,healthExportShippingOrder,
   queryErpPhone,
   saveErpPhone,editErpPhone,batchCreateErpOrder,batchSetErpOrder
 } from '@/api/hisStore/storeOrder'
@@ -2360,7 +2370,7 @@ export default {
     //打开发货单
     openDeliveryNote() {
       this.deliveryNoteOpen = true
-      // this.getAppList();
+      this.getAppList();
     },
     handleClose(done) {
       this.$confirm('确认关闭?')
@@ -2400,7 +2410,43 @@ export default {
         return;
       }
       this.$refs.upload.submit();
-    }
+    },
+    handleExportShippingOrder() {
+      if (this.queryParams.status == '00') {
+        this.queryParams.status = null
+      }
+      if (this.createTimeRange != null && this.createTimeRange.length == 2) {
+        this.queryParams.createTimeRange = this.createTimeRange[0] + '--' + this.createTimeRange[1]
+      } else {
+        this.queryParams.createTimeRange = null
+      }
+      if (this.payTimeRange != null && this.payTimeRange.length == 2) {
+        this.queryParams.payTimeRange = this.payTimeRange[0] + '--' + this.payTimeRange[1]
+      } else {
+        this.queryParams.payTimeRange = null
+      }
+      if (this.deliveryImportTimeRange != null && this.deliveryImportTimeRange.length == 2) {
+        this.queryParams.deliveryImportTimeRange = this.deliveryImportTimeRange[0] + '--' + this.deliveryImportTimeRange[1]
+      } else {
+        this.queryParams.deliveryImportTimeRange = null
+      }
+      if (this.deliverySendTimeRange != null && this.deliverySendTimeRange.length == 2) {
+        this.queryParams.deliverySendTimeRange = this.deliverySendTimeRange[0] + '--' + this.deliverySendTimeRange[1]
+      } else {
+        this.queryParams.deliverySendTimeRange = null
+      }
+      const queryParams = this.addDateRange(this.queryParams, this.dateRange)
+      this.$confirm('是否确认导出所有订单明细数据项?', '警告', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(function() {
+        return healthExportShippingOrder(queryParams)
+      }).then(response => {
+        this.download(response.msg)
+      }).catch(function() {
+      })
+    },
 
   }
 }

+ 33 - 17
src/views/hisStore/storeProduct/index.vue

@@ -437,6 +437,16 @@
               <el-input v-model="form.unitName" placeholder="请输入单位名" />
             </el-form-item>
           </el-col>
+          <el-col :span="12">
+            <el-form-item label="品牌" prop="brand">
+              <el-input v-model="form.brand" placeholder="请输入品牌" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="食品生产许可证编码" prop="unitName">
+              <el-input v-model="form.foodProductionLicenseCode" placeholder="请输入食品生产许可证编码" />
+            </el-form-item>
+          </el-col>
 
         </el-row>
         <el-row :gutter="10">
@@ -543,13 +553,13 @@
           </el-form-item>
         </div>
         </div>
-        <el-row>
-          <el-col :span="24">
-            <el-form-item label="商品简介" prop="productInfo">
-              <el-input v-model="form.productInfo" type="textarea" :rows="2" placeholder="请输入商品简介" />
-            </el-form-item>
-          </el-col>
-        </el-row>
+<!--        <el-row>-->
+<!--          <el-col :span="24">-->
+<!--            <el-form-item label="商品简介" prop="productInfo">-->
+<!--              <el-input v-model="form.productInfo" type="textarea" :rows="2" placeholder="请输入商品简介" />-->
+<!--            </el-form-item>-->
+<!--          </el-col>-->
+<!--        </el-row>-->
         <el-form-item label="商品图片" prop="image">
           <Material v-model="imageArr" type="image" :num="1" :width="150" :height="150" />
         </el-form-item>
@@ -859,6 +869,9 @@
             />
           </el-select>
         </el-form-item>
+        <el-form-item label="退货地址" prop="returnAddress">
+          <el-input v-model="form.returnAddress" type="textarea" :rows="1" placeholder="请输入退货地址" />
+        </el-form-item>
         <el-form-item label="国药准字" v-if="form.productType==2" prop="prescribeCode">
           <el-input v-model="form.prescribeCode" placeholder="请输入国药准字" />
         </el-form-item>
@@ -1088,15 +1101,15 @@ export default {
         productName: [
           { required: true, message: "商品名称不能为空", trigger: "blur" }
         ],
-        productInfo: [
-          { required: true, message: "商品简介不能为空", trigger: "blur" }
-        ],
-        unitName: [
-          { required: true, message: "单位名不能为空", trigger: "blur" }
-        ],
-        keyword: [
-          { required: true, message: "关键字不能为空", trigger: "blur" }
-        ],
+        // productInfo: [
+        //   { required: true, message: "商品简介不能为空", trigger: "blur" }
+        // ],
+        // unitName: [
+        //   { required: true, message: "单位名不能为空", trigger: "blur" }
+        // ],
+        // keyword: [
+        //   { required: true, message: "关键字不能为空", trigger: "blur" }
+        // ],
         cateId: [
           { required: true, message: "分类id不能为空", trigger: "blur" }
         ],
@@ -1233,7 +1246,7 @@ export default {
       param.productId = this.ids;
       param.goodsStatus = this.form1.isShow;
       param.goodsIsShow = this.form1.isDisplay;
-      param.companyIds = this.companyId+''
+      param.companyIds = this.form1.companyId.join(',');
       batchModify(param).then(res=>{
         if(res.code === 200){
           this.$message.success("批量修改成功");
@@ -1481,6 +1494,8 @@ export default {
         createTime: null,
         updateTime: null,
         isPostage: null,
+        foodProductionLicenseCode: null,
+        brand: null,
         isDel: null,
         giveIntegral: null,
         cost: null,
@@ -1498,6 +1513,7 @@ export default {
         prescribeName: null,
         isDisplay:"1",
         companyIds:[],
+        returnAddress: "", // 退货地址
         isDrug: "1", // 是否药品
         drugImage: null, // 药品展示图
         drugRegCertNo: null, // 药品注册证书编号

+ 2 - 2
src/views/live/liveConfig/task.vue

@@ -723,9 +723,9 @@ export default {
         if (this.form.taskType == 1) {
           this.form.content = content.goodsId;
         }else if (this.form.taskType == 2) {
-          this.form.content = content.desc;
+          this.form.content = content.redId;
         }else if (this.form.taskType == 4) {
-          this.form.content = content.desc;
+          this.form.content = content.lotteryId;
         }else if(this.form.taskType == 5){
           this.form.content = content.couponId;
         }else if(this.form.taskType == 6){

+ 11 - 10
src/views/live/liveConsole/LiveConsole.vue

@@ -113,7 +113,16 @@
         </div>
         <el-scrollbar class="custom-scrollbar" style="height: 500px; width: 100%;" ref="manageRightRef">
           <el-row v-for="m in msgList" :key="m.msgId">
-            <el-row v-if="m.userId !== userId" style="margin-top: 5px" type="flex" align="top" >
+            <el-row v-if="m.userId === userId && m.msgId == null" style="padding: 8px 0" type="flex" align="top" justify="end">
+              <div style="display: flex;justify-content: flex-end">
+                <div style="display: flex;justify-content: flex-end;flex-direction: column;max-width: 200px;align-items: flex-end">
+                  <div style="font-size: 12px; color: #999; margin-bottom: 3px;">{{ m.nickName }}</div>
+                  <div style="white-space: normal; word-wrap: break-word;width: 100%; background-color: #e6f7ff; padding: 8px; border-radius: 5px;font-size: 14px;">{{ m.msg }}</div>
+                </div>
+                <el-avatar :src="m.avatar" style="margin-left: 10px; margin-right: 10px;"/>
+              </div>
+            </el-row>
+            <el-row v-else style="margin-top: 5px" type="flex" align="top" >
               <el-col :span="3" style="margin-left: 10px"><el-avatar :src="m.avatar"/></el-col>
               <el-col :span="15">
                 <el-row style="margin-left: 10px">
@@ -132,15 +141,7 @@
                 </el-row>
               </el-col>
             </el-row>
-            <el-row v-if="m.userId === userId" style="padding: 8px 0" type="flex" align="top" justify="end">
-              <div style="display: flex;justify-content: flex-end">
-                <div style="display: flex;justify-content: flex-end;flex-direction: column;max-width: 200px;align-items: flex-end">
-                  <div style="font-size: 12px; color: #999; margin-bottom: 3px;">{{ m.nickName }}</div>
-                  <div style="white-space: normal; word-wrap: break-word;width: 100%; background-color: #e6f7ff; padding: 8px; border-radius: 5px;font-size: 14px;">{{ m.msg }}</div>
-                </div>
-                <el-avatar :src="m.avatar" style="margin-left: 10px; margin-right: 10px;"/>
-              </div>
-            </el-row>
+
           </el-row>
           <!-- 底部留白 -->
           <div style="height: 20px;"></div>

+ 629 - 730
src/views/live/liveData/index.vue

@@ -1,760 +1,659 @@
 <template>
-  <div class="app-container">
-    <div class="title">近期直播</div>
-    <div class="live-container">
-      <div class="live-card" v-for="live in displayLives" :key="live.id">
-        <!-- 直播状态 -->
-        <div class="status">已结束</div>
-
-        <!-- 直播信息 -->
-        <div class="content">
-          <img :src="live.image" alt="直播封面" class="cover-image" />
-          <div class="info">
-            <h3 class="live-title">{{ live.title }}</h3>
-            <p class="time">开播时间: {{ live.time }}</p>
-          </div>
-        </div>
-
-        <!-- 直播数据 -->
-        <div class="stats">
-          <div class="stat-item">
-            <p class="label">直播间浏览量</p>
-            <p class="value">{{ live.views }}</p>
+  <div class="el-container-md">
+    <!-- 数据统计指标展示区域 -->
+    <el-card class="statistics-card" shadow="never">
+      <el-row :gutter="20">
+        <el-col :span="4">
+          <div class="statistics-item">
+            <div class="statistics-title">累计观看人数</div>
+            <div class="statistics-value">{{ statisticsData.totalViewers || 0 }}</div>
           </div>
-          <div class="stat-item">
-            <p class="label">直播间访客数</p>
-            <p class="value">{{ live.visitors }}</p>
+        </el-col>
+        <el-col :span="4">
+          <div class="statistics-item">
+            <div class="statistics-title">
+              累计完课人数
+              <el-popover placement="top" width="200" trigger="click">
+                <div>
+                  <el-input-number v-model="completeWatchTime" :min="0" :precision="0" placeholder="看课时长(分钟)" style="width: 100%;"></el-input-number>
+                  <el-button type="primary" size="mini" style="width: 100%; margin-top: 10px;" @click="updateCompleteWatchTime('total')">确定</el-button>
+                </div>
+                <el-button slot="reference" size="mini" type="text" icon="el-icon-setting" style="margin-left: 5px;">设置</el-button>
+              </el-popover>
+            </div>
+            <div class="statistics-value">{{ statisticsData.totalCompletedCourses || 0 }}</div>
           </div>
-        </div>
-      </div>
-    </div>
-    <!-- 直播趋势统计 -->
-    <div class="trend-section">
-      <div class="trend-header">
-        <h3>直播趋势</h3>
-        <div class="filter-container">
-        <!-- 时间范围选择(下拉框) -->
-          <span class="filter-label">时间筛选:</span>
-          <el-select v-model="selectedTimeRange" placeholder="选择时间范围" @change="changeTimeRange" style="width: 180px">
-            <el-option label="自然天" value="day"></el-option>
-            <el-option label="自然周" value="week"></el-option>
-            <el-option label="自然月" value="month"></el-option>
-          </el-select>
-
-          <!-- 日期选择器 -->
-          <!-- 选择日期 -->
-          <el-date-picker
-            v-model="selectedDate"
-            :type="datePickerType"
-            :format="selectedTimeRange === 'week' ? weekRange : dateFormat"
-            :value-format="valueFormat"
-            placeholder="选择日期"
-            style="width: 280px"
-            @change="changeDate"
-          ></el-date-picker>
-        </div>
-      </div>
-
-      <div class="trend-cards">
-        <div
-          class="trend-card"
-          v-for="item in trendData"
-          :key="item.id"
-          :class="{ 'active': selectedMetric.id === item.id }"
-          @click="changeMetric(item)"
-        >
-          <div class="trend-header">
-            <p class="trend-title">{{ item.title }}</p>
-            <el-tooltip class="item" effect="dark" :content="item.tip" placement="top">
-              <i class="el-icon-info"></i>
-            </el-tooltip>
+        </el-col>
+        <el-col :span="4">
+          <div class="statistics-item">
+            <div class="statistics-title">直播观看人数</div>
+            <div class="statistics-value">{{ statisticsData.liveViewers || 0 }}</div>
           </div>
-          <p class="trend-value">{{ item.value }}</p>
-          <p class="trend-change" :class="{ 'up': computedChange(item) > 0, 'down': computedChange(item) < 0 }">
-            {{ changeLabel }} {{ computedChange(item) }}%
-          </p>
-        </div>
-      </div>
-
-      <!-- 直播趋势折线图 -->
-      <div id="liveChart" class="chart"></div>
-    </div>
-    <div class="top10-section">
-      <h3>直播TOP10排行榜</h3>
-      <div class="ranking-container">
-          <!-- 红榜 -->
-          <div class="ranking-section red-rank">
-            <span class="rank-title">红榜</span>
-            <div class="rank-filters">
-              <button
-                v-for="item in redRankTypes"
-                :key="item.value"
-                :class="{ active: selectedRank === item.value }"
-                @click="selectRank(item.value)"
-              >
-                {{ item.label }}
-              </button>
+        </el-col>
+        <el-col :span="4">
+          <div class="statistics-item">
+            <div class="statistics-title">
+              直播完课人数
+              <el-popover placement="top" width="200" trigger="click">
+                <div>
+                  <el-input-number v-model="liveCompleteWatchTime" :min="0" :precision="0" placeholder="看课时长(分钟)" style="width: 100%;"></el-input-number>
+                  <el-button type="primary" size="mini" style="width: 100%; margin-top: 10px;" @click="updateCompleteWatchTime('live')">确定</el-button>
+                </div>
+                <el-button slot="reference" size="mini" type="text" icon="el-icon-setting" style="margin-left: 5px;">设置</el-button>
+              </el-popover>
             </div>
+            <div class="statistics-value">{{ statisticsData.liveCompletedCourses || 0 }}</div>
           </div>
-
-          <!-- 黑榜 -->
-          <div class="ranking-section black-rank">
-            <div class="rank-filters">
-              <button
-                v-for="item in blackRankTypes"
-                :key="item.value"
-                :class="{ active: selectedRank === item.value }"
-                @click="selectRank(item.value)"
-              >
-                {{ item.label }}
-              </button>
-            </div>
-            <span class="rank-title">黑榜</span>
+        </el-col>
+        <el-col :span="4">
+          <div class="statistics-item">
+            <div class="statistics-title">回放观看人数</div>
+            <div class="statistics-value">{{ statisticsData.playbackViewers || 0 }}</div>
           </div>
-      </div>
-      <el-table :data="top10List" border style="width: 100%">
-        <el-table-column prop="rank" label="排名" width="80"></el-table-column>
-        <el-table-column prop="name" label="直播名称">
-          <template #default="scope">
-            <div class="live-name">
-              <img :src="scope.row.cover" class="live-cover" alt="封面">
-              <span class="live-title">{{ scope.row.name }}</span>
+        </el-col>
+        <el-col :span="4">
+          <div class="statistics-item">
+            <div class="statistics-title">
+              回放完课人数
+              <el-popover placement="top" width="200" trigger="click">
+                <div>
+                  <el-input-number v-model="replayCompleteWatchTime" :min="0" :precision="0" placeholder="看课时长(分钟)" style="width: 100%;"></el-input-number>
+                  <el-button type="primary" size="mini" style="width: 100%; margin-top: 10px;" @click="updateCompleteWatchTime('replay')">确定</el-button>
+                </div>
+                <el-button slot="reference" size="mini" type="text" icon="el-icon-setting" style="margin-left: 5px;">设置</el-button>
+              </el-popover>
             </div>
-          </template>
-        </el-table-column>
-        <el-table-column prop="pv" label="直播间浏览量(PV)"></el-table-column>
-        <el-table-column prop="uv" label="直播间访客数(UV)"></el-table-column>
-        <el-table-column prop="watchPV" label="累计观看人次(PV)"></el-table-column>
-        <el-table-column prop="watchUV" label="累计观看人数(UV)"></el-table-column>
-        <el-table-column prop="avgTime" label="人均观看时长"></el-table-column>
-        <el-table-column prop="maxOnline" label="最高在线人数"></el-table-column>
-      </el-table>
+            <div class="statistics-value">{{ statisticsData.playbackCompletedCourses || 0 }}</div>
+          </div>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20" style="margin-top: 20px;">
+        <el-col :span="4">
+          <div class="statistics-item">
+            <div class="statistics-title">直播峰值</div>
+            <div class="statistics-value">{{ statisticsData.livePeak || 0 }}</div>
+          </div>
+        </el-col>
+        <el-col :span="4">
+          <div class="statistics-item">
+            <div class="statistics-title">直播平均时长</div>
+            <div class="statistics-value">{{ formatDuration(statisticsData.liveAvgDuration) }}</div>
+          </div>
+        </el-col>
+        <el-col :span="4">
+          <div class="statistics-item">
+            <div class="statistics-title">回放平均时长</div>
+            <div class="statistics-value">{{ formatDuration(statisticsData.playbackAvgDuration) }}</div>
+          </div>
+        </el-col>
+        <el-col :span="4">
+          <div class="statistics-item">
+            <div class="statistics-title">GMV</div>
+            <div class="statistics-value">¥{{ statisticsData.gmv || 0 }}</div>
+          </div>
+        </el-col>
+        <el-col :span="4">
+          <div class="statistics-item">
+            <div class="statistics-title">付费人数</div>
+            <div class="statistics-value">{{ statisticsData.paidUsers || 0 }}</div>
+          </div>
+        </el-col>
+        <el-col :span="4">
+          <div class="statistics-item">
+            <div class="statistics-title">付费单数</div>
+            <div class="statistics-value">{{ statisticsData.paidOrders || 0 }}</div>
+          </div>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20" style="margin-top: 20px;">
+        <el-col :span="4">
+          <div class="statistics-item">
+            <div class="statistics-title">销量统计</div>
+            <div class="statistics-value">{{ statisticsData.salesCount || 0 }}</div>
+          </div>
+        </el-col>
+      </el-row>
+    </el-card>
+
+    <!-- 筛选条件区域 -->
+    <el-form :model="queryParams" class="live-data-css" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+      <el-form-item label="直播名称" prop="liveName">
+        <el-input
+          v-model="queryParams.liveName"
+          placeholder="请输入直播名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="观看类型" prop="watchType">
+        <el-select v-model="queryParams.watchType" placeholder="请选择观看类型" clearable size="small">
+          <el-option label="直播" value="live"></el-option>
+          <el-option label="回放" value="replay"></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="完课状态" prop="completeStatus">
+        <el-select v-model="queryParams.completeStatus" placeholder="请选择完课状态" clearable size="small">
+          <el-option label="已完课" value="1"></el-option>
+          <el-option label="未完课" value="0"></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="付费状态" prop="payStatus">
+        <el-select v-model="queryParams.payStatus" placeholder="请选择付费状态" clearable size="small">
+          <el-option label="已付费" value="1"></el-option>
+          <el-option label="未付费" value="0"></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="观看时长" prop="watchDuration">
+        <el-input-number
+          v-model="queryParams.watchDuration"
+          :min="0"
+          :precision="0"
+          placeholder="观看时长(分钟)"
+          size="small"
+        ></el-input-number>
+      </el-form-item>
+      <el-form-item label="时间范围" prop="dateRange">
+        <el-date-picker
+          v-model="dateRange"
+          type="datetimerange"
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          size="small"
+          value-format="yyyy-MM-dd HH:mm:ss"
+        ></el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <!-- 操作工具栏 -->
+    <div class="selection-toolbar">
+      <el-checkbox :indeterminate="isIndeterminate" v-model="allChecked" @change="toggleSelectAll">
+        {{ multipleSelection.length > 0 ? `已选 ${multipleSelection.length} 条` : '选中本页' }}
+      </el-checkbox>
+      <!--      <el-button plain size="mini" @click="handleAutoTag">自动打标签</el-button>-->
+      <!--      <el-button plain size="mini" @click="handleAutoRemark">自动备注</el-button>-->
+      <el-button plain type="primary" size="mini" icon="el-icon-download" :loading="exportLoading" @click="handleExport">导出</el-button>
     </div>
+
+    <!-- 数据表格 -->
+    <el-table
+      ref="dataTable"
+      :data="dataList"
+      style="width: 100%"
+      v-loading="loading"
+      @selection-change="handleSelectionChange"
+    >
+      <el-table-column type="selection" width="55"></el-table-column>
+      <el-table-column type="index" label="序号" width="55" align="center"></el-table-column>
+      <el-table-column prop="liveName" label="直播间名称" min-width="160" show-overflow-tooltip></el-table-column>
+      <el-table-column prop="liveType" label="直播类型" width="100" align="center">
+        <template slot-scope="scope">
+          <el-tag type="danger" v-if="scope.row.liveType == 1">直播</el-tag>
+          <el-tag type="success" v-else-if="scope.row.liveType == 2">录播</el-tag>
+          <el-tag v-else-if="scope.row.liveType == 3">直播回放</el-tag>
+          <span v-else>-</span>
+        </template>
+      </el-table-column>
+      <el-table-column prop="status" label="状态" width="100" align="center">
+        <template slot-scope="scope">
+          <el-tag type="info" v-if="scope.row.status == 1">未开始</el-tag>
+          <el-tag type="warning" v-else-if="scope.row.status == 2">进行中</el-tag>
+          <el-tag type="success" v-else-if="scope.row.status == 3">已结束</el-tag>
+          <span v-else>-</span>
+        </template>
+      </el-table-column>
+      <el-table-column prop="startTime" label="开始时间" width="180" align="center"></el-table-column>
+      <el-table-column prop="finishTime" label="结束时间" width="180" align="center"></el-table-column>
+      <el-table-column prop="totalViewers" label="累计观看" width="110" align="center"></el-table-column>
+      <el-table-column prop="liveViewers" label="直播观看" width="110" align="center"></el-table-column>
+      <el-table-column prop="playbackViewers" label="回放观看" width="110" align="center"></el-table-column>
+      <el-table-column prop="liveAvgDuration" label="直播平均时长" width="140" align="center">
+        <template slot-scope="scope">{{ formatDuration(scope.row.liveAvgDuration) }}</template>
+      </el-table-column>
+      <el-table-column prop="playbackAvgDuration" label="回放平均时长" width="140" align="center">
+        <template slot-scope="scope">{{ formatDuration(scope.row.playbackAvgDuration) }}</template>
+      </el-table-column>
+      <el-table-column prop="totalCompletedCourses" label="累计完课" width="110" align="center"></el-table-column>
+      <el-table-column prop="liveCompletedCourses" label="直播完课" width="110" align="center"></el-table-column>
+      <el-table-column prop="playbackCompletedCourses" label="回放完课" width="110" align="center"></el-table-column>
+      <el-table-column prop="gmv" label="GMV" width="120" align="center">
+        <template slot-scope="scope">¥{{ scope.row.gmv || 0 }}</template>
+      </el-table-column>
+      <el-table-column prop="paidUsers" label="付费人数" width="100" align="center"></el-table-column>
+      <el-table-column prop="paidOrders" label="付费单数" width="100" align="center"></el-table-column>
+      <el-table-column prop="salesCount" label="销量统计" width="100" align="center"></el-table-column>
+      <el-table-column label="操作" width="120" fixed="right">
+        <template slot-scope="scope">
+          <el-button type="text" size="small" @click="handleViewDetail(scope.row)">查看详情</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 分页组件 -->
+    <pagination
+      v-show="total > 0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+      style="margin-top: 20px;"
+    />
+
+    <!-- 自动打标签弹窗 -->
+    <el-dialog
+      title="自动打标签"
+      :visible.sync="tagDialogVisible"
+      width="600px"
+      :close-on-click-modal="false"
+    >
+      <el-form :model="tagForm" label-width="120px">
+        <el-form-item label="打标签规则">
+          <el-checkbox-group v-model="tagForm.rules">
+            <el-checkbox label="liveComplete">直播完课</el-checkbox>
+            <el-checkbox label="replayComplete">回放完课</el-checkbox>
+            <el-checkbox label="pay">付费行为</el-checkbox>
+          </el-checkbox-group>
+        </el-form-item>
+        <el-form-item label="标签名称">
+          <el-input v-model="tagForm.tagName" placeholder="请输入标签名称"></el-input>
+        </el-form-item>
+        <el-form-item label="备注格式">
+          <el-radio-group v-model="tagForm.remarkFormat">
+            <el-radio label="time">时间—行为(如:2024-01-01—直播完课)</el-radio>
+            <el-radio label="simple">仅行为(如:直播完课)</el-radio>
+          </el-radio-group>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="tagDialogVisible = false">取 消</el-button>
+        <el-button type="primary" @click="confirmAutoTag">确 定</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 自动备注弹窗 -->
+    <el-dialog
+      title="自动备注"
+      :visible.sync="remarkDialogVisible"
+      width="600px"
+      :close-on-click-modal="false"
+    >
+      <el-form :model="remarkForm" label-width="120px">
+        <el-form-item label="备注规则">
+          <el-checkbox-group v-model="remarkForm.rules">
+            <el-checkbox label="liveComplete">直播完课</el-checkbox>
+            <el-checkbox label="replayComplete">回放完课</el-checkbox>
+            <el-checkbox label="pay">付费行为</el-checkbox>
+          </el-checkbox-group>
+        </el-form-item>
+        <el-form-item label="备注格式">
+          <el-radio-group v-model="remarkForm.remarkFormat">
+            <el-radio label="time">时间—行为(如:2024-01-01—直播完课)</el-radio>
+            <el-radio label="simple">仅行为(如:直播完课)</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="备注位置">
+          <el-radio-group v-model="remarkForm.position">
+            <el-radio :label="1">添加在最前面</el-radio>
+            <el-radio :label="2">添加在最后面</el-radio>
+            <el-radio :label="3">替换所有备注</el-radio>
+          </el-radio-group>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="remarkDialogVisible = false">取 消</el-button>
+        <el-button type="primary" @click="confirmAutoRemark">确 定</el-button>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
-
 <script>
-  import * as echarts from "echarts";
-  import { listLive, getLive, delLive, addLive, updateLive, exportLive } from "@/api/live/live";
-  import Editor from '@/components/Editor/wang';
-
-  export default {
-    name: "Live",
-    components: { Editor },
-    data() {
-      return {
-        top10List: [
-          {
-            rank: 1,
-            name: "御君方年末会员福利专场!",
-            cover:"https://cos.his.cdwjyyh.com/fs/20250304/710ea5b1896749b58438b76baf881d05.jpeg",
-            pv: 88332,
-            uv: 32674,
-            watchPV: 41866,
-            watchUV: 24714,
-            avgTime: "00:13:12",
-            maxOnline: 4317,
-          },
-          {
-            rank: 2,
-            name: "立秋养生大作战",
-            cover:"https://cos.his.cdwjyyh.com/fs/20250304/710ea5b1896749b58438b76baf881d05.jpeg",
-            pv: 55092,
-            uv: 15066,
-            watchPV: 33522,
-            watchUV: 12343,
-            avgTime: "01:18:30",
-            maxOnline: 3160,
-          },
-          {
-            rank: 3,
-            name: "寒露养生大挑战:防寒防燥防疾病",
-            cover:"https://cos.his.cdwjyyh.com/fs/20250304/710ea5b1896749b58438b76baf881d05.jpeg",
-            pv: 36044,
-            uv: 12205,
-            watchPV: 26056,
-            watchUV: 11086,
-            avgTime: "01:16:08",
-            maxOnline: 3232,
-          },
-        ],
-        selectedRank: 'highFlow',
-        redRankTypes: [
-          { label: '高流量', value: 'highFlow' },
-          { label: '高观看', value: 'highView' },
-          { label: '高时长', value: 'highTime' },
-        ],
-        blackRankTypes: [
-          { label: '低时长', value: 'lowTime' },
-          { label: '低观看', value: 'lowView' },
-          { label: '低流量', value: 'lowFlow' },
-        ],
-        selectedTimeRange: "day", // 默认选中“自然天”
-        datePickerType: "date", // 默认 date 类型
-        weekRange: "", // 存储选择周的时间范围
-        selectedDate: new Date().toISOString().split("T")[0], // 默认今天
-        lives: [
-          {
-            id: 1,
-            image: "https://cos.his.cdwjyyh.com/fs/20250117/d9e3b268e3834a2497e0bb1f900eac90.jpg",
-            title: "李悦的直播",
-            time: "2024-10-10 15:00:00",
-            views: 352821,
-            visitors: 14505,
-          },
-          {
-            id: 2,
-            image: "https://cos.his.cdwjyyh.com/fs/20250117/d9e3b268e3834a2497e0bb1f900eac90.jpg",
-            title: "小付的直播",
-            time: "2024-10-12 18:00:00",
-            views: 284210,
-            visitors: 10234,
-          },
-          {
-            id: 3,
-            image: "https://cos.his.cdwjyyh.com/fs/20250117/d9e3b268e3834a2497e0bb1f900eac90.jpg",
-            title: "生活妙招分享",
-            time: "2024-11-05 20:00:00",
-            views: 182401,
-            visitors: 8734,
-          },
-          {
-            id: 4,
-            image: "https://cos.his.cdwjyyh.com/fs/20250117/d9e3b268e3834a2497e0bb1f900eac90.jpg",
-            title: "家庭健康指南",
-            time: "2024-12-01 14:00:00",
-            views: 254321,
-            visitors: 12345,
-          },
-          {
-            id: 5,
-            image: "https://cos.his.cdwjyyh.com/fs/20250117/d9e3b268e3834a2497e0bb1f900eac90.jpg",
-            title: "额外的直播",
-            time: "2024-12-15 19:00:00",
-            views: 100000,
-            visitors: 5000,
-          }
-        ],
-        trendData: [
-          {
-            id: "views",
-            title: "直播间浏览量",
-            tip: "直播间总浏览量",
-            value: 352421,
-            dailyChange:1,
-            weeklyChange:2,
-            monthlyChange:3,
-            change: 5.2,
-            dates: ["02-02", "02-04", "02-06", "02-08", "02-10"],
-            data: [10, 20, 15, 25, 30],
-          },
-          {
-            id: "visitors",
-            title: "直播间访客数",
-            tip: "直播间访客数",
-            value: 14505,
-            dailyChange:1,
-            weeklyChange:2,
-            monthlyChange:3,
-            change: 5.2,
-            dates: ["02-02", "02-04", "02-06", "02-08", "02-10"],
-            data: [5, 8, 6, 10, 12],
-          },
-          {
-            id: "streams",
-            title: "创建直播数",
-            tip: "每日直播场次",
-            value: 20,
-            dailyChange:1,
-            weeklyChange:2,
-            monthlyChange:3,
-            change: 5.2,
-            dates: ["02-02", "02-04", "02-06", "02-08", "02-10"],
-            data: [1, 2, 1, 3, 2],
-          },
-          {
-            id: "pv",
-            title: "累计观看人次(PV)",
-            tip: "累计观看人次",
-            value: 5000,
-            dailyChange:1,
-            weeklyChange:2,
-            monthlyChange:3,
-            change: 5.2,
-            dates: ["02-02", "02-04", "02-06", "02-08", "02-10"],
-            data: [0, 0, 1, 0, 0],
-          },
-          {
-            id: "uv",
-            title: "累计观看人数(UV)",
-            tip: "累计观看人数",
-            value: 3000,
-            dailyChange:1,
-            weeklyChange:2,
-            monthlyChange:3,
-            change: 5.2,
-            dates: ["02-02", "02-04", "02-06", "02-08", "02-10"],
-            data: [0, 0, 0, 1, 0],
-          },
-        ],
-        selectedMetric: {}, // 选中的卡片数据
-        chart: null, // ECharts 实例
-      };
-    },
-    computed: {
-      changeLabel() {
-        switch (this.selectedTimeRange) {
-          case 'day': return '比前一天';
-          case 'week': return '比上周';
-          case 'month': return '比上月';
-          default: return '变化';
-        }
-      },
-      displayLives() {
-        return this.lives.slice(0, 4); // 最多只显示前 4 条数据
+import { listLiveData, exportLiveData, autoTagAndRemark, dashboardData } from "@/api/live/liveData";
+import { batchUpdateExternalContactNotes } from "@/api/qw/externalContact";
+import { addTag } from "@/api/qw/externalContact";
+
+export default {
+  name: "LiveData",
+  data() {
+    return {
+      liveId: '',
+      loading: true,
+      exportLoading: false,
+      showSearch: true,
+      dataList: [],
+      total: 0,
+      multipleSelection: [],
+      allChecked: false,
+      isIndeterminate: false,
+      dateRange: [],
+      // 统计数据
+      statisticsData: {
+        gmv: 0,
+        liveAvgDuration: 0,
+        liveCompletedCourses: 0,
+        livePeak: 0,
+        liveViewers: 0,
+        paidOrders: 0,
+        paidUsers: 0,
+        playbackAvgDuration: 0,
+        playbackCompletedCourses: 0,
+        playbackViewers: 0,
+        salesCount: 0,
+        totalCompletedCourses: 0,
+        totalViewers: 1
       },
-      /*datePickerType() {
-        return this.selectedTimeRange === "day"
-          ? "date"
-          : this.selectedTimeRange === "week"
-            ? "week"
-            : "month";
-      },*/
-      dateFormat() {
-        return this.selectedTimeRange === "day"
-          ? "yyyy-MM-dd"
-          : this.selectedTimeRange === "week"
-            ? "yyyy-MM-dd 至 yyyy-MM-dd"
-            : "yyyy-MM";
+      // 完课时长设置
+      completeWatchTime: 0,
+      liveCompleteWatchTime: 0,
+      replayCompleteWatchTime: 0,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        liveId: null,
+        liveName: null,
+        watchType: null,
+        completeStatus: null,
+        payStatus: null,
+        watchDuration: null,
+        startTime: null,
+        endTime: null
       },
-      valueFormat() {
-        return this.selectedTimeRange === "day"
-          ? "yyyy-MM-dd"
-          : this.selectedTimeRange === "week"
-            ? "yyyy-MM-dd"
-            : "yyyy-MM";
+      // 打标签弹窗
+      tagDialogVisible: false,
+      tagForm: {
+        rules: [],
+        tagName: '',
+        remarkFormat: 'time'
       },
+      // 备注弹窗
+      remarkDialogVisible: false,
+      remarkForm: {
+        rules: [],
+        remarkFormat: 'time',
+        position: 1
+      }
+    };
+  },
+  created() {
+    this.liveId = this.$route.query.liveId;
+    this.queryParams.liveId = this.liveId;
+
+    this.getList();
+  },
+  methods: {
+    /** 获取统计数据 */
+    getStatistics() {
+      if (!this.liveId) return;
+      dashboardData(this.liveId).then(response => {
+        if (response.code === 200) {
+          this.statisticsData = response.data || {};
+        }
+      });
+    },
+    /** 获取列表数据 */
+    getList() {
+      this.loading = true;
+      // 处理时间范围
+      if (this.dateRange && this.dateRange.length === 2) {
+        this.queryParams.startTime = this.dateRange[0];
+        this.queryParams.endTime = this.dateRange[1];
+      } else {
+        this.queryParams.startTime = null;
+        this.queryParams.endTime = null;
+      }
+      listLiveData(this.queryParams).then(response => {
+        if (response.code === 200) {
+          this.statisticsData = response.data || {};
+          this.dataList = response.list || [];
+          this.total = response.total || 0;
+        }
+        this.loading = false;
+      }).catch(() => {
+        this.loading = false;
+      });
     },
-    created() {
-      //this.getList();
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
     },
-    mounted() {
-      this.selectedMetric = this.trendData[0]; // 默认选中第一个
-      this.initChart(); // 初始化折线图
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.dateRange = [];
+      this.$refs.queryForm.resetFields();
+      this.handleQuery();
     },
-    methods: {
-      changeDate(value) {
-        if (this.selectedTimeRange === "week" && value) {
-          /*console.log("🟢 监听到 selectedWeek 变化:", newVal);*/
-          this.weekRange = this.getWeekRange(value);
-        } else {
-          this.weekRange = "";
-        }
-        console.log("选择的时间:", value, "筛选方式:", this.selectedTimeRange);
-      },
-      getWeekRange(selectedWeek) {
-        console.log("🔹 selectedWeek 输入值:", selectedWeek); // 检查传入值
-        let date = new Date(selectedWeek);
-
-        if (isNaN(date.getTime())) {
-          console.error("Invalid Date:", selectedWeek);
-          return "日期错误";
+    /** 全选或取消全选 */
+    toggleSelectAll(val) {
+      if (val) {
+        this.toggleSelection(this.dataList);
+      } else {
+        this.toggleSelection();
+      }
+    },
+    toggleSelection(rows) {
+      if (rows) {
+        rows.forEach(row => {
+          this.$refs.dataTable.toggleRowSelection(row);
+        });
+      } else {
+        this.$refs.dataTable.clearSelection();
+      }
+    },
+    /** 多选框选中数据 */
+    handleSelectionChange(val) {
+      this.multipleSelection = val;
+      this.allChecked = val.length === this.dataList.length;
+      this.isIndeterminate = val.length > 0 && val.length < this.dataList.length;
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      if (this.dateRange && this.dateRange.length === 2) {
+        this.queryParams.startTime = this.dateRange[0];
+        this.queryParams.endTime = this.dateRange[1];
+      } else {
+        this.queryParams.startTime = null;
+        this.queryParams.endTime = null;
+      }
+      this.$confirm('是否确认导出所有直播数据?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        this.exportLoading = true;
+        return exportLiveData(this.queryParams);
+      }).then(response => {
+        if (response.code === 200) {
+          this.download(response.msg);
         }
-
-        let dayOfWeek = date.getDay(); // 0(星期天)- 6(星期六)
-
-        // 计算周一
-        let startDate = new Date(date); // 创建一个新的 Date 实例
-        startDate.setDate(date.getDate() - dayOfWeek + (dayOfWeek === 0 ? -6 : 1));
-
-        // 计算周日(确保 `endDate` 是新实例)
-        let endDate = new Date(startDate.getTime()); // 复制 `startDate`
-        endDate.setDate(startDate.getDate() + 6);
-
-        console.log("周一:", this.formatDate(startDate));
-        console.log("周日:", this.formatDate(endDate));
-
-        return `${this.formatDate(startDate)} 至 ${this.formatDate(endDate)}`;
-      },
-      formatDate(date) {
-        const year = date.getFullYear();
-        const month = String(date.getMonth() + 1).padStart(2, "0");
-        const day = String(date.getDate()).padStart(2, "0");
-        return `${year}-${month}-${day}`;
-      },
-      computedChange(item) {
-        switch (this.selectedTimeRange) {
-          case 'day': return item.dailyChange;
-          case 'week': return item.weeklyChange;
-          case 'month': return item.monthlyChange;
-          default: return 0;
+        this.exportLoading = false;
+      }).catch(() => {
+        this.exportLoading = false;
+      });
+    },
+    /** 自动打标签 */
+    // handleAutoTag() {
+    //   if (this.multipleSelection.length === 0) {
+    //     this.$message.warning('请选择要打标签的数据');
+    //     return;
+    //   }
+    //   this.tagDialogVisible = true;
+    // },
+    /** 确认自动打标签 */
+    confirmAutoTag() {
+      if (this.tagForm.rules.length === 0) {
+        this.$message.warning('请选择打标签规则');
+        return;
+      }
+      const userIds = this.multipleSelection.map(item => item.userId).filter(id => id);
+      if (userIds.length === 0) {
+        this.$message.warning('所选数据中没有有效的用户ID');
+        return;
+      }
+      const data = {
+        userIds: userIds,
+        rules: this.tagForm.rules,
+        tagName: this.tagForm.tagName,
+        remarkFormat: this.tagForm.remarkFormat,
+        liveId: this.liveId
+      };
+      autoTagAndRemark(data).then(response => {
+        if (response.code === 200) {
+          this.$message.success('打标签成功');
+          this.tagDialogVisible = false;
+          this.tagForm = {
+            rules: [],
+            tagName: '',
+            remarkFormat: 'time'
+          };
+          this.getList();
         }
-      },
-      selectRank(type) {
-        this.selectedRank = type; // 只允许一个按钮被选中
-        console.log(`选中的排行方式: ${type}`);
-      },
-      fetchRankingData() {
-        // 这里根据 `selectedRedRank` 和 `selectedBlackRank` 来请求不同的排行榜数据
-        console.log(`获取排行榜数据: 红榜-${this.selectedRedRank}, 黑榜-${this.selectedBlackRank}`);
-      },
-      // 切换时间范围
-      changeTimeRange() {
-        // 根据选中的时间范围修改 datePicker 类型
-        if (this.selectedTimeRange === "day") {
-          this.datePickerType = "date";
-        } else if (this.selectedTimeRange === "month") {
-          this.datePickerType = "month";
-        } else if (this.selectedTimeRange === "week") {
-          this.datePickerType = "week";
+      });
+    },
+    /** 自动备注 */
+    // handleAutoRemark() {
+    //   if (this.multipleSelection.length === 0) {
+    //     this.$message.warning('请选择要备注的数据');
+    //     return;
+    //   }
+    //   this.remarkDialogVisible = true;
+    // },
+    /** 确认自动备注 */
+    confirmAutoRemark() {
+      if (this.remarkForm.rules.length === 0) {
+        this.$message.warning('请选择备注规则');
+        return;
+      }
+      const userIds = this.multipleSelection.map(item => item.userId).filter(id => id);
+      if (userIds.length === 0) {
+        this.$message.warning('所选数据中没有有效的用户ID');
+        return;
+      }
+      // 生成备注内容
+      let notes = '';
+      const now = new Date();
+      const dateStr = now.getFullYear() + '-' +
+        String(now.getMonth() + 1).padStart(2, '0') + '-' +
+        String(now.getDate()).padStart(2, '0');
+
+      this.remarkForm.rules.forEach((rule, index) => {
+        let ruleText = '';
+        if (rule === 'liveComplete') {
+          ruleText = '直播完课';
+        } else if (rule === 'replayComplete') {
+          ruleText = '回放完课';
+        } else if (rule === 'pay') {
+          ruleText = '付费行为';
         }
-        this.selectedDate = null; // 重置已选日期
-      },
-      // 切换时间(但目前只是选日期,没有请求后端)
-      /*changeDate() {
-        console.log("选择日期:", this.selectedDate);
-        // 这里可以做后端请求:this.fetchTrendData();
-      },*/
-      // 获取直播趋势数据
-      async fetchTrendData() {
-        try {
-          const response = await axios.get("/api/trend-data", { params: { date: this.selectedDate } });
-          this.trendData = response.data;
 
-          // 默认选择第一个指标
-          if (this.trendData.length > 0) {
-            this.changeMetric(this.trendData[0]);
-          }
-        } catch (error) {
-          console.error("获取直播数据失败:", error);
+        if (this.remarkForm.remarkFormat === 'time') {
+          notes += (index > 0 ? ';' : '') + dateStr + '—' + ruleText;
+        } else {
+          notes += (index > 0 ? ';' : '') + ruleText;
         }
-      },
-      // 切换日期时更新数据
-      updateTrendData() {
-        console.log(`更新 ${this.selectedDate} 的直播数据`);
-        // 这里可以加接口请求,获取新的数据
-        this.updateChart();
-      },
-      // 切换指标
-      // changeMetric(metric) {
-      //   this.selectedMetric = metric;
-      //   this.updateChart();
-      // },
-      // updateTrendData() {
-      //   console.log("选择的时间:", this.selectedDate);
-      //   this.trendData = this.trendData.map(item => ({
-      //     ...item,
-      //     value: Math.floor(Math.random() * 50),
-      //     change: (Math.random() * 10 - 5).toFixed(2)
-      //   }));
-      //   this.updateChart();
-      // },
-      initChart() {
-        let chartDom = document.getElementById("liveChart");
-        if (!chartDom) return;
-        this.chart = echarts.init(chartDom);
-        this.updateChart();
-      },
-      // 更新折线图
-      updateChart() {
-        if (!this.chart) return;
-
-        let options = {
-          tooltip: { trigger: "axis", backgroundColor: "rgba(0, 0, 0, 0.7)", textStyle: { color: "#fff" } },
-          grid: { left: "5%", right: "5%", bottom: "10%", top: "10%", containLabel: true },
-          xAxis: { type: "category", data: this.selectedMetric.dates || [], axisLine: { lineStyle: { color: "#ddd" } } },
-          yAxis: { type: "value", splitLine: { lineStyle: { type: "dashed", color: "#ddd" } } },
-          series: [
-            {
-              name: this.selectedMetric.title,
-              data: this.selectedMetric.data || [],
-              type: "line",
-              smooth: true,
-              symbol: "circle",
-              symbolSize: 8,
-              itemStyle: { color: "#007BFF", borderColor: "#fff", borderWidth: 2 },
-              lineStyle: { width: 3, color: "#007BFF" },
-              areaStyle: {
-                color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
-                  { offset: 0, color: "rgba(0, 123, 255, 0.5)" },
-                  { offset: 1, color: "rgba(0, 123, 255, 0)" },
-                ]),
-              },
-              emphasis: {
-                focus: "series",
-                label: { show: true, formatter: "{c}", fontSize: 14, color: "#333", backgroundColor: "#fff", padding: [4, 6] },
-              },
-              animationDuration: 1200,
-              animationEasing: "quadraticOut",
-            },
-          ],
-        };
+      });
+
+      const data = {
+        userIds: userIds,
+        notes: notes,
+        type: this.remarkForm.position,
+        nameType: 3, // 不添加客户名称
+        filter: false
+      };
 
-        this.chart.setOption(options);
-      },
-      changeMetric(metric) {
-        this.selectedMetric = metric;
-        this.updateChart();
-      },
+      batchUpdateExternalContactNotes(data).then(response => {
+        if (response.code === 200) {
+          this.$message.success('备注成功');
+          this.remarkDialogVisible = false;
+          this.remarkForm = {
+            rules: [],
+            remarkFormat: 'time',
+            position: 1
+          };
+          this.getList();
+        }
+      });
+    },
+    /** 查看详情 */
+    handleViewDetail(row) {
+      // 可以跳转到详情页面或打开详情弹窗
+      this.$message.info('查看详情功能待实现');
+    },
+    /** 格式化时长 */
+    formatDuration(seconds) {
+      if (!seconds) return '00:00:00';
+      const hours = Math.floor(seconds / 3600);
+      const minutes = Math.floor((seconds % 3600) / 60);
+      const secs = seconds % 60;
+      return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
+    },
+    /** 更新完课时长设置 */
+    updateCompleteWatchTime(type) {
+      // 这里可以调用API保存设置
+      let watchTime = 0;
+      if (type === 'total') {
+        watchTime = this.completeWatchTime;
+      } else if (type === 'live') {
+        watchTime = this.liveCompleteWatchTime;
+      } else if (type === 'replay') {
+        watchTime = this.replayCompleteWatchTime;
+      }
+      // 更新查询参数并重新获取数据
+      this.queryParams.completeWatchTime = watchTime;
+      // this.getStatistics();
+      this.getList();
+      this.$message.success('设置成功');
     }
-  };
+  }
+};
 </script>
 
 <style scoped>
-  .app-container {
-    padding: 20px;
-  }
-
-  .title {
-    font-size: 18px;
-    font-weight: bold;
-    margin-bottom: 10px;
-  }
-  .live-container {
-    display: flex;
-    gap: 15px; /* 控制卡片之间的间距 */
-    overflow-x: auto; /* 允许横向滚动 */
-    padding-bottom: 10px;
-  }
-
-  /* 卡片样式 */
-  .live-card {
-    width: 380px;
-    height: 225px;
-    background: rgb(250,250,250);
-    border-radius: 10px;
-    padding: 15px;
-    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
-    display: flex;
-    flex-direction: column;
-    align-items: flex-start;
-  }
-
-  /* 直播状态 */
-  .status {
-    background: #d3d3d3;
-    color: #333;
-    font-size: 14px;
-    padding: 4px 8px;
-    border-radius: 5px;
-    display: inline-block;
-
-
-  }
-
-  /* 直播信息部分 */
-  .content {
-    background: rgb(250,250,250);
-    display: flex;
-    gap: 10px;
-    align-items: center;
-    width: 90%;
-    margin-top: 0px;
-  }
-
-  /* 直播封面图片 */
-  .cover-image {
-    width: 70px;
-    height: 70px;
-    border-radius: 10px;
-    object-fit: cover;
-  }
-
-  /* 直播文本信息 */
-  .info {
-    flex: 1;
-  }
-
-  .live-title {
-    font-size: 16px;
-    font-weight: bold;
-    white-space: nowrap;
-    overflow: hidden;
-    text-overflow: ellipsis;
-    max-width: 250px; /* 防止标题过长 */
-  }
-
-  .time {
-    font-size: 14px;
-    color: #666;
-  }
-
-  /* 直播数据部分 */
-  .stats {
-    display: flex;
-    justify-content: space-between;
-    width: 100%;
-    flex-shrink: 0;
-    margin-top: -40px;
-  }
-
-  .stat-item {
-    text-align: center;
-    flex: 1;
-  }
-
-  .label {
-    font-size: 14px;
-    color: #888;
-  }
-
-  .value {
-    font-size: 18px;
-    font-weight: bold;
-    color: #333;
-  }
-  /* 直播趋势 */
-  .trend-section {
-    margin-top: 30px;
-  }
-  .trend-header {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-  }
-  .trend-cards {
-    display: flex;
-    gap: 15px;
-  }
-  .trend-card {
-    width: 280px;
-    hight: 100px;
-    border: 1px solid #ddd;
-    border-radius: 8px;
-    padding: 16px;
-    transition: all 0.3s;
-    position: relative;
-  }
-
-  .trend-card.active {
-    border-color: #007bff; /* 选中时边框变蓝 */
-    box-shadow: 0px 0px 10px rgba(0, 123, 255, 0.3);
-  }
-
-  .trend-card.active::after {
-    content: "✔";
-    position: absolute;
-    bottom: 8px;
-    right: 8px;
-    color: white;
-    background: #007bff;
-    border-radius: 50%;
-    width: 20px;
-    height: 20px;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    font-size: 14px;
-    font-weight: bold;
-  }
-  .trend-value {
-    font-size: 24px;
-    font-weight: bold;
-  }
-
-  /* 折线图 */
-  .chart {
-    width: 100%;
-    height: 300px;
-    margin-top: 20px;
-  }
-  .top10-section {
-    margin-top: 20px;
-    background: #fff;
-    padding: 16px;
-    border-radius: 8px;
-    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
-  }
-  .live-name {
-    display: flex;
-    align-items: center;
-  }
-
-  .live-cover {
-    width: 80px;  /* 直播封面宽度 */
-    height: 45px; /* 直播封面高度 */
-    border-radius: 4px;
-    margin-right: 10px;
-    object-fit: cover;
-  }
-
-  .live-title {
-    font-size: 14px;
-    color: #333;
-    white-space: nowrap;
-    overflow: hidden;
-    text-overflow: ellipsis;
-  }
-  .ranking-tabs {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    padding: 10px 20px;
-  }
-
-  .ranking-container {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    margin: 10px 0;
-    border-radius: 8px;
-    overflow: hidden;
-  }
-
-  .ranking-section {
-    display: flex;
-    align-items: center;
-    width: 50%;
-    padding: 10px 15px;
-  }
-
-  .red-rank {
-    background: linear-gradient(90deg, #ff6b6b, #ff8e8e);
-    justify-content: flex-start;
-  }
-
-  .black-rank {
-    background: linear-gradient(90deg, #4a4a4a, #6b6b6b);
-    justify-content: flex-end;
-  }
-
-  .rank-title {
-    font-size: 18px;
-    font-weight: bold;
-    color: white;
-    margin: 0 15px;
-  }
-
-  .rank-filters {
-    display: flex;
-  }
-
-  button {
-    background: rgba(255, 255, 255, 0.2);
-    border: 1px solid rgba(255, 255, 255, 0.5);
-    color: white;
-    font-size: 14px;
-    padding: 5px 12px;
-    margin: 0 5px;
-    border-radius: 15px;
-    cursor: pointer;
-    transition: all 0.3s ease;
-  }
-
-  button.active {
-    background: white;
-    color: #ff4d4f;
-  }
-  .filter-container {
-    display: flex;
-    align-items: center;
-    gap: 8px; /* 控制两个组件的间距 */
-  }
-
-  .filter-label {
-    font-weight: bold;
-    white-space: nowrap; /* 防止换行 */
-  }
+.statistics-card {
+  margin-bottom: 20px;
+}
+
+.statistics-item {
+  text-align: center;
+  padding: 15px;
+  background: #f5f7fa;
+  border-radius: 4px;
+}
+
+.statistics-title {
+  font-size: 14px;
+  color: #606266;
+  margin-bottom: 10px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.statistics-value {
+  font-size: 24px;
+  font-weight: bold;
+  color: #303133;
+}
+
+.selection-toolbar {
+  display: flex;
+  align-items: center;
+  margin-bottom: 10px;
+  padding-left: 10px;
+}
+
+.selection-toolbar .el-checkbox {
+  margin-right: 10px;
+}
+
+.live-data-css {
+  padding-left: 10px;
+  padding-top: 30px;
+}
 </style>
-

+ 210 - 4
src/views/live/liveOrder/index.vue

@@ -126,7 +126,26 @@
           :loading="exportLoading"
           @click="handleExport"
           v-hasPermi="['live:liveOrder:export']"
-        >导出</el-button>
+        >导出订单</el-button>
+        <el-col :span="1.5">
+          <el-button
+            icon="el-icon-s-order"
+            size="mini"
+            type="warning"
+            @click="openDeliveryNote"
+          >批量导入物流单号
+          </el-button>
+        </el-col>
+        <el-col :span="1.5">
+          <el-button
+            v-hasPermi="['store:storeOrder:healthExportShippingOrder']"
+            icon="el-icon-tickets"
+            size="mini"
+            type="success"
+            @click="handleExportShippingOrder"
+          >导出发货单
+          </el-button>
+        </el-col>
       </el-col>
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
@@ -177,7 +196,7 @@
       <el-table-column label="商品规格" align="center" prop="productSpec" width="120" />
       <el-table-column label="商品数量" align="center" prop="totalNum" />
       <el-table-column label="销售价格" align="center" prop="totalPrice" />
-      <el-table-column label="成本价格" align="center" prop="costPrice" />
+<!--      <el-table-column label="成本价格" align="center" prop="costPrice" />-->
       <el-table-column label="收货地址" align="center" prop="userAddress" width="200" />
       <el-table-column label="对应供应商" align="center" prop="supplierName" width="120" />
       <el-table-column label="下单时间" align="center" prop="createTime" width="180">
@@ -217,16 +236,64 @@
       :title="show.title" :visible.sync="show.open">
       <liveOrderDetails ref="Details" />
     </el-drawer>
+
+    <el-dialog
+      :before-close="handleClose"
+      :visible.sync="deliveryNoteOpen"
+      center
+      title="批量发货"
+      width="35%"
+    >
+      <span slot="footer" class="dialog-footer">
+        <!-- 小程序Appid选择 -->
+        <el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
+          <el-form-item label="小程序:" prop="miniAppId">
+        <el-select
+          v-model="ruleForm.miniAppId"
+          clearable
+          placeholder="请选择发货小程序"
+          style="width: 100%"
+        >
+          <el-option
+            v-for="item in miniAppList"
+            :key="item.appId"
+            :label="item.appName"
+            :value="item.appId"
+          />
+        </el-select>
+      </el-form-item>
+        </el-form>
+
+        <el-upload ref="upload" :action="uploadUrl" :auto-upload="false" :disabled="orderUpload.isUploading" :headers="orderUpload.headers"
+                   :limit="1" :on-progress="handleFileUploadProgress"
+                   :on-success="handleFileSuccess" accept=".xlsx, .xls" drag
+        >
+        <i class="el-icon-upload"></i>
+        <div class="el-upload__text">
+          将文件拖到此处,或
+          <em>点击上传</em>
+        </div>
+        <div slot="tip" class="el-upload__tip">
+          <el-link style="font-size:12px" type="info" @click="importDeliveryNoteTemplate">下载模板</el-link>
+        </div>
+        <div slot="tip" class="el-upload__tip" style="color:red">提示:仅允许导入“xls”或“xlsx”格式文件!</div>
+      </el-upload>
+        <el-divider></el-divider>
+        <el-button @click="cancelResetDeliveryNote">取 消</el-button>
+        <el-button type="primary" @click="submitDeliveryNote('ruleForm')">确 定</el-button>
+      </span>
+    </el-dialog>
   </div>
 </template>
 
 <script>
-import { listLiveOrderZm, exportLiveOrderZm } from "@/api/live/liveOrder";
+import { listLiveOrderZm, exportLiveOrderZm,healthExportShippingOrder,importDeliveryNoteExpressTemplate } from "@/api/live/liveOrder";
 import liveOrderDetails from './liveOrderDetails.vue';
 import {getCompanyList} from "@/api/company/company";
 import Treeselect from "@riophae/vue-treeselect";
 import {treeselect} from "@/api/company/companyDept";
 import "@riophae/vue-treeselect/dist/vue-treeselect.css";
+import { getToken } from '@/utils/auth'
 import {parseTime} from "../../../utils/common";
 
 export default {
@@ -234,6 +301,34 @@ export default {
   components: {Treeselect, liveOrderDetails },
   data() {
     return {
+      upload: {
+        // 是否显示弹出层(用户导入)
+        open: false,
+        // 弹出层标题(用户导入)
+        title: '',
+        // 是否禁用上传
+        isUploading: false,
+        // 是否更新已经存在的用户数据
+        updateSupport: 0,
+        // 设置上传的请求头部
+        headers: { Authorization: 'Bearer ' + getToken() },
+        // 上传的地址
+        url: process.env.VUE_APP_BASE_API + '/live/liveOrder/importExpress'
+      },
+      orderUpload: {
+        // 是否显示弹出层(用户导入)
+        open: false,
+        // 弹出层标题(用户导入)
+        title: '',
+        // 是否禁用上传
+        isUploading: false,
+        // 是否更新已经存在的用户数据
+        updateSupport: 0,
+        // 设置上传的请求头部
+        headers: { Authorization: 'Bearer ' + getToken() },
+      },
+      deliveryNoteOpen: false,
+      miniAppList: [],
       // 字典
       orderStatusOptions: [],
       memberLevelOptions: [],
@@ -282,12 +377,26 @@ export default {
         orderEndTime: null,
         status: null,
       },
+      ruleForm:{
+        miniAppId: null,
+      },
       orderOptions: [],
       actName: "10",
       show: {
         title: "订单详情",
         open: false,
       },
+      rules: {
+        userId: [
+          { required: true, message: '会员信息不能为空' }
+        ],
+        addressId: [
+          { required: true, message: '收货信息不能为空' }
+        ],
+        miniAppId: [
+          { required: true, message: '发货小程序不能为空' }
+        ],
+      },
     };
   },
   created() {
@@ -313,8 +422,59 @@ export default {
       this.customerStatusOptions = response.data;
     });
   },
+  computed: {
+
+    uploadUrl() {
+      return process.env.VUE_APP_BASE_API +
+        '/live/liveOrder/importDeliveryNoteExpress?miniAppId=' +
+        this.ruleForm.miniAppId;
+    }
+  },
   methods: {
-    parseTime,
+    // 提交发货单
+    submitDeliveryNote(formName) {
+      this.$refs[formName].validate((valid) => {
+        if (valid) {
+          const uploadFiles = this.$refs.upload.uploadFiles
+          if (uploadFiles.length === 0) {
+            this.$message.error('请选择要上传的文件')
+            return
+          }
+          this.$refs.upload.submit()
+        } else {
+          return false
+        }
+      })
+    },
+    //发货单模板下载
+    importDeliveryNoteTemplate() {
+      importDeliveryNoteExpressTemplate().then((response) => {
+        this.download(response.msg)
+      })
+    },
+    handleFileSuccess(response, file, fileList) {
+      this.upload.open = false
+      this.upload.isUploading = false
+      this.$refs.upload.clearFiles()
+      this.importMsgOpen = true
+      this.importMsg = response.msg
+      // this.$alert(response.msg, '导入结果', {
+      //     dangerouslyUseHTMLString: true
+      //   });
+      this.getList()
+    },
+    // 文件上传中处理
+    handleFileUploadProgress(event, file, fileList) {
+      this.upload.isUploading = true
+    },
+    handleClose(done) {
+      this.$confirm('确认关闭?')
+        .then(_ => {
+          done()
+        })
+        .catch(_ => {
+        })
+    },
 
     // 下单时间范围选择变化处理
     handleOrderTimeChange(value) {
@@ -419,6 +579,52 @@ export default {
       this.queryParams.deptId = val;
       this.getList();
     },
+    handleExportShippingOrder() {
+      if (this.queryParams.status == '00') {
+        this.queryParams.status = null
+      }
+      if (this.createTimeRange != null && this.createTimeRange.length == 2) {
+        this.queryParams.createTimeRange = this.createTimeRange[0] + '--' + this.createTimeRange[1]
+      } else {
+        this.queryParams.createTimeRange = null
+      }
+      if (this.payTimeRange != null && this.payTimeRange.length == 2) {
+        this.queryParams.payTimeRange = this.payTimeRange[0] + '--' + this.payTimeRange[1]
+      } else {
+        this.queryParams.payTimeRange = null
+      }
+      if (this.deliveryImportTimeRange != null && this.deliveryImportTimeRange.length == 2) {
+        this.queryParams.deliveryImportTimeRange = this.deliveryImportTimeRange[0] + '--' + this.deliveryImportTimeRange[1]
+      } else {
+        this.queryParams.deliveryImportTimeRange = null
+      }
+      if (this.deliverySendTimeRange != null && this.deliverySendTimeRange.length == 2) {
+        this.queryParams.deliverySendTimeRange = this.deliverySendTimeRange[0] + '--' + this.deliverySendTimeRange[1]
+      } else {
+        this.queryParams.deliverySendTimeRange = null
+      }
+      const queryParams = this.addDateRange(this.queryParams, this.dateRange)
+      this.$confirm('是否确认导出所有订单明细数据项?', '警告', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(function() {
+        return healthExportShippingOrder(queryParams)
+      }).then(response => {
+        this.download(response.msg)
+      }).catch(function() {
+      })
+    },
+    //打开发货单
+    openDeliveryNote() {
+      this.deliveryNoteOpen = true
+      this.getAppList();
+    },
+    //取消重置
+    cancelResetDeliveryNote(){
+      this.deliveryNoteOpen = false;
+      this.resetForm('ruleForm')
+    },
   }
 };
 </script>

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

@@ -1248,6 +1248,13 @@
               <el-radio label="0">关</el-radio>
             </el-radio-group>
           </el-form-item>
+          <!-- 看视频休息暂停配置开关 默认打开要暂停 0-关闭 1-打开 -->
+          <el-form-item label="看课休息暂停开关(默认打开)">
+            <el-radio-group v-model="form18.isOpenRestReminder">
+              <el-radio label="1" >开</el-radio>
+              <el-radio label="0">关</el-radio>
+            </el-radio-group>
+          </el-form-item>
 
 
           <el-form-item label="禁止发送时间段">
@@ -2325,7 +2332,7 @@ export default {
       form16: {},
       form17: {},
       form18: {
-        viewCommentNum: 200
+        viewCommentNum: 200,
       },
       form19: {},
       form20: {