Explorar el Código

Merge remote-tracking branch 'origin/master'

yjwang hace 1 día
padre
commit
8c8db21ab4
Se han modificado 45 ficheros con 4372 adiciones y 1207 borrados
  1. 22 0
      .env.prod-bjyjb
  2. 22 0
      .env.prod-cqxzt
  3. 22 0
      .env.prod-kyt
  4. 3 0
      package.json
  5. 80 64
      src/api/course/userCoursePeriod.js
  6. 18 0
      src/api/course/userOperationLog.js
  7. 8 1
      src/api/store/inquiryOrder.js
  8. BIN
      src/assets/logo/bjyjb.jpg
  9. BIN
      src/assets/logo/cqxzt.png
  10. BIN
      src/assets/logo/ylrz.png
  11. 330 62
      src/views/course/courseWatchLog/deptWatchLog.vue
  12. 327 59
      src/views/course/courseWatchLog/index.vue
  13. 0 16
      src/views/course/courseWatchLog/myCourseWatchLog.vue
  14. 181 70
      src/views/course/courseWatchLog/watchLog.vue
  15. 6 23
      src/views/course/userCoursePeriod/batchRedPacket.vue
  16. 585 383
      src/views/course/userCoursePeriod/index.vue
  17. 15 26
      src/views/course/userCoursePeriod/redPacket.vue
  18. 4 6
      src/views/member/list.vue
  19. 1 0
      src/views/qw/externalContact/deptIndex.vue
  20. 1 0
      src/views/qw/externalContact/index.vue
  21. 1 0
      src/views/qw/externalContact/myExternalContact.vue
  22. 10 1
      src/views/qw/externalContactLoss/index.vue
  23. 1 1
      src/views/qw/friendWelcome/indexNew.vue
  24. 1 1
      src/views/qw/groupMsg/customerBaseMsg.vue
  25. 2 2
      src/views/qw/groupMsg/customerGroupDetails.vue
  26. 2 3
      src/views/qw/groupMsg/index.vue
  27. 2 2
      src/views/qw/groupMsgUser/groupMsg.vue
  28. 2 2
      src/views/qw/material/ImageUpload.vue
  29. 153 130
      src/views/qw/material/index.vue
  30. 64 13
      src/views/qw/sopTemp/index.vue
  31. 6 1
      src/views/qw/sopTemp/updateSopTemp.vue
  32. 37 5
      src/views/qw/sopUserLogsInfo/sopUserLogsInfoDetails.vue
  33. 128 12
      src/views/qw/welcome/welComeAdd.vue
  34. 3 3
      src/views/store/components/inquiryOrderReportDetails.vue
  35. 460 0
      src/views/store/components/userBehavior.vue
  36. 173 0
      src/views/store/components/userCourseConversionRecord.vue
  37. 476 0
      src/views/store/components/userCoursePeriod.vue
  38. 222 0
      src/views/store/components/userCoursePeriodDetails.vue
  39. 57 0
      src/views/store/components/userCourseStatic.vue
  40. 55 263
      src/views/store/components/userDetails.vue
  41. 268 0
      src/views/store/components/userDetailsTemp.vue
  42. 248 0
      src/views/store/components/userStaticAll.vue
  43. 75 24
      src/views/store/inquiryOrder/list.vue
  44. 300 33
      src/views/store/inquiryOrder/myList.vue
  45. 1 1
      src/views/store/inquiryOrderReport/index.vue

+ 22 - 0
.env.prod-bjyjb

@@ -0,0 +1,22 @@
+# 页面标题
+VUE_APP_TITLE =医健宝SCRM销售端
+# 公司名称
+VUE_APP_COMPANY_NAME = 医健宝智慧(北京)医药科技有限公司
+# ICP备案号
+VUE_APP_ICP_RECORD =
+# ICP网站访问地址
+VUE_APP_ICP_URL =
+# 网站LOG
+VUE_APP_LOG_URL =@/assets/logo/bjyjb.jpg
+
+# 生产环境配置
+ENV = 'production'
+
+# FS管理系统/开发环境
+VUE_APP_BASE_API = '/prod-api'
+
+#默认 1、会员 2、企微
+VUE_APP_COURSE_DEFAULT = 1
+
+# 路由懒加载
+VUE_CLI_BABEL_TRANSPILE_MODULES = true

+ 22 - 0
.env.prod-cqxzt

@@ -0,0 +1,22 @@
+# 页面标题
+VUE_APP_TITLE =襄之棠SCRM销售端
+# 公司名称
+VUE_APP_COMPANY_NAME =重庆襄之棠大药房连锁有限公司
+# ICP备案号
+VUE_APP_ICP_RECORD =
+# ICP网站访问地址
+VUE_APP_ICP_URL =
+# 网站LOG
+VUE_APP_LOG_URL =@/assets/logo/cqxzt.png
+
+# 生产环境配置
+ENV = 'production'
+
+# FS管理系统/开发环境
+VUE_APP_BASE_API = '/prod-api'
+
+#默认 1、会员 2、企微
+VUE_APP_COURSE_DEFAULT = 1
+
+# 路由懒加载
+VUE_CLI_BABEL_TRANSPILE_MODULES = true

+ 22 - 0
.env.prod-kyt

@@ -0,0 +1,22 @@
+# 页面标题
+VUE_APP_TITLE =互联网医院管理系统
+# 公司名称
+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/ylrz.png
+
+# 生产环境配置
+ENV = 'production'
+
+# FS管理系统/开发环境
+VUE_APP_BASE_API = '/prod-api'
+
+#默认 1、会员 2、企微
+VUE_APP_COURSE_DEFAULT = 1
+
+# 路由懒加载
+VUE_CLI_BABEL_TRANSPILE_MODULES = true

+ 3 - 0
package.json

@@ -29,6 +29,8 @@
     "build:prod-qdtst": "vue-cli-service build --mode prod-qdtst",
     "build:prod-bjczwh": "vue-cli-service build --mode prod-bjczwh",
     "build:prod-jkj": "vue-cli-service build --mode prod-jkj",
+    "build:prod-cqxzt": "vue-cli-service build --mode prod-cqxzt",
+    "build:prod-bjyjb": "vue-cli-service build --mode prod-bjyjb",
     "preview": "node build/index.js --preview",
     "lint": "eslint --ext .js,.vue src",
     "test:unit": "jest --clearCache && vue-cli-service test:unit",
@@ -95,6 +97,7 @@
     "vue-jsonp": "^2.1.0",
     "vue-meta": "^2.4.0",
     "vue-mobile-audio": "^0.1.3",
+    "vue-mobile-calendar": "^3.3.0",
     "vue-router": "3.0.2",
     "vue-splitpane": "1.0.4",
     "vue2-ace-editor": "0.0.15",

+ 80 - 64
src/api/course/userCoursePeriod.js

@@ -17,14 +17,7 @@ export function getDays(query) {
   })
 }
 
-// 自定义查询主列表分页
-export function pagePeriod(data) {
-  return request({
-    url: '/course/period/page',
-    method: 'post',
-    data: data
-  })
-}
+
 
 // 查询会员营期详细
 export function getPeriod(periodId) {
@@ -43,14 +36,7 @@ export function addPeriod(data) {
   })
 }
 
-// 新增会员营期
-export function addCourse(data) {
-  return request({
-    url: '/course/period/addCourse',
-    method: 'post',
-    data: data
-  })
-}
+
 
 // 新增会员营期
 export function updateCourseTime(data) {
@@ -68,23 +54,7 @@ export function updateCourseDate(data) {
     data: data
   })
 }
-// 新增会员营期
-export function updateListCourseData(data) {
-  return request({
-    url: '/course/period/updateListCourseData',
-    method: 'post',
-    data: data
-  })
-}
 
-// 修改会员营期
-export function updatePeriod(data) {
-  return request({
-    url: '/course/period',
-    method: 'put',
-    data: data
-  })
-}
 
 // 删除会员营期
 export function delPeriod(periodId) {
@@ -103,14 +73,6 @@ export function exportPeriod(query) {
   })
 }
 
-// 根据营期id获取公司红包金额列表
-export function getPeriodCompanyList(query) {
-  return request({
-    url: '/course/period/companyList',
-    method: 'get',
-    params: query
-  })
-}
 
 // 按照课程批量设置红包金额
 export function batchSaveRedPacket(data) {
@@ -121,14 +83,6 @@ export function batchSaveRedPacket(data) {
   })
 }
 
-// 获取设置红包金额列表展示
-export function getPeriodRedPacketList(query) {
-  return request({
-    url: '/course/period/redPacketList',
-    method: 'get',
-    params: query
-  })
-}
 
 // 按照营期批量设置红包金额
 export function batchSaveRedPacketByPeriod(data) {
@@ -139,23 +93,7 @@ export function batchSaveRedPacketByPeriod(data) {
   })
 }
 
-// 按照营期、按课程统计
-export function periodCountSelect(data) {
-  return request({
-    url: '/course/period/periodCount',
-    method: 'post',
-    data: data
-  })
-}
 
-// 获取营期选项列表
-export function getPeriodListLikeName(query) {
-  return request({
-    url: '/course/period/getPeriodListLikeName',
-    method: 'get',
-    params: query
-  })
-}
 
 // 营期课程上移下移
 export function periodCourseMove(data) {
@@ -199,3 +137,81 @@ export function periodList(data) {
     data: data
   })
 }
+
+
+// 自定义查询主列表分页
+export function pagePeriod(data) {
+  return request({
+    url: '/course/period/page',
+    method: 'post',
+    data: data
+  })
+}
+
+
+
+// 新增会员营期
+export function addCourse(data) {
+  return request({
+    url: '/course/period/addCourse',
+    method: 'post',
+    data: data
+  })
+}
+
+// 新增会员营期
+export function updateListCourseData(data) {
+  return request({
+    url: '/course/period/updateListCourseData',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改会员营期
+export function updatePeriod(data) {
+  return request({
+    url: '/course/period',
+    method: 'put',
+    data: data
+  })
+}
+
+// 根据营期id获取公司红包金额列表
+export function getPeriodCompanyList(query) {
+  return request({
+    url: '/course/period/companyList',
+    method: 'get',
+    params: query
+  })
+}
+
+
+// 获取设置红包金额列表展示
+export function getPeriodRedPacketList(query) {
+  return request({
+    url: '/course/period/redPacketList',
+    method: 'get',
+    params: query
+  })
+}
+
+
+// 按照营期、按课程统计
+export function periodCountSelect(data) {
+  return request({
+    url: '/course/period/periodCount',
+    method: 'post',
+    data: data
+  })
+}
+
+
+// 获取营期选项列表
+export function getPeriodListLikeName(query) {
+  return request({
+    url: '/course/period/getPeriodListLikeName',
+    method: 'get',
+    params: query
+  })
+}

+ 18 - 0
src/api/course/userOperationLog.js

@@ -0,0 +1,18 @@
+import request from '@/utils/request'
+
+// 查询用户操作日志对象列表
+export function listUserOperationLog(query) {
+  return request({
+    url: '/his/userOperationLog/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询用户操作日志类型
+export function getOperationType() {
+  return request({
+    url: '/his/userOperationLog/getOperationType',
+    method: 'get'
+  })
+}

+ 8 - 1
src/api/store/inquiryOrder.js

@@ -120,4 +120,11 @@ export function exportMessageFeedback(query) {
     method: 'get',
     params: query
   })
-}
+}
+
+export function getWxaCodeInquiryOrderUnLimit(orderId) {
+  return request({
+    url: '/store/inquiryOrder/getWxaCodeInquiryOrderUnLimit/'+orderId,
+    method: 'get',
+  })
+}

BIN
src/assets/logo/bjyjb.jpg


BIN
src/assets/logo/cqxzt.png


BIN
src/assets/logo/ylrz.png


+ 330 - 62
src/views/course/courseWatchLog/deptWatchLog.vue

@@ -88,41 +88,71 @@
 <!--          @keyup.enter.native="handleQuery"-->
 <!--        />-->
 <!--      </el-form-item>-->
+      <!-- 营期时间 -->
       <el-form-item label="营期时间" prop="scheduleTime">
-        <el-date-picker
+        <el-input
+          v-model="scheduleTimeText"
+          placeholder="请选择营期时间"
+          readonly
+          @click.native="showScheduleCalendar = true"
+        />
+        <calendar
           v-model="scheduleTime"
-          type="daterange"
-          size="small"
-          style="width: 240px"
-          value-format="yyyy-MM-dd"
-          range-separator="-"
-          start-placeholder="开始日期"
-          end-placeholder="结束日期"
-          @change="handleScheduleTimeChange">
-        </el-date-picker>
+          mode="during"
+          :show.sync="showScheduleCalendar"
+          @change="handleScheduleTimeChange"
+          :key="scheduleCalendarKey"
+        />
       </el-form-item>
+      <!-- 创建时间 -->
       <el-form-item label="创建时间" prop="createTime">
-        <el-date-picker v-model="createTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange"
-                        range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="createChange"></el-date-picker>
+        <el-input
+          v-model="createTimeText"
+          placeholder="请选择创建时间"
+          readonly
+          @click.native="showCreateCalendar = true"
+        />
+        <calendar
+          v-model="createTime"
+          mode="during"
+          :show.sync="showCreateCalendar"
+          @change="createChange"
+          :key="createCalendarKey"
+        />
       </el-form-item>
+      <!-- 最新更新时间 -->
       <el-form-item label="最新更新时间" prop="updateTime">
-        <el-date-picker v-model="updateTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange"
-                        range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="updateChange"></el-date-picker>
+        <el-input
+          v-model="updateTimeText"
+          placeholder="请选择更新时间"
+          readonly
+          @click.native="showUpdateCalendar = true"
+        />
+        <calendar
+          v-model="updateTime"
+          mode="during"
+          :show.sync="showUpdateCalendar"
+          @change="updateChange"
+          :key="updateCalendarKey"
+        />
       </el-form-item>
-      <el-form-item label="进线时间" prop="updateTime">
-        <el-date-picker
+      <!-- 进线时间 -->
+      <el-form-item label="进线时间" prop="qecCreateTime">
+        <el-input
+          v-model="qecCreateTimeText"
+          placeholder="请选择进线时间"
+          readonly
+          @click.native="showQecCalendar = true"
+        />
+        <calendar
           v-model="qecCreateTime"
-          size="small"
-          style="width: 220px"
-          value-format="yyyy-MM-dd"
-          type="daterange"
-          range-separator="-"
-          start-placeholder="开始日期"
-          end-placeholder="结束日期"
+          mode="during"
+          :show.sync="showQecCalendar"
           @change="qecCreateTimeChange"
-          :picker-options="pickerOptions"
-        ></el-date-picker>
+          :key="qecCalendarKey"
+        />
       </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>
@@ -196,6 +226,15 @@
       <el-table-column label="完课时间" align="center" prop="finishTime" />
       <el-table-column label="营期时间" align="center" prop="campPeriodTime" />
       <el-table-column label="进线时间" align="center" prop="qecCreateTime" />
+      <el-table-column
+        fixed="right"
+        label="操作"
+        width="100">
+        <template slot-scope="scope">
+          <el-button @click="openAnswerLogFun(scope.row)" type="text" size="small">答题记录</el-button>
+          <el-button @click="openRedLogFun(scope.row)" type="text" size="small">红包记录</el-button>
+        </template>
+      </el-table-column>
     </el-table>
 
     <pagination
@@ -206,21 +245,144 @@
       @pagination="getList"
     />
 
+    <el-drawer title="答题记录" :visible.sync="openAnswerLog" size="70%" append-to-body>
+      <el-table border v-loading="" :data="answerLogsList">
+        <el-table-column label="会员用户" align="center" prop="userName">
+          <template slot-scope="scope">
+            <div style="display: flex;white-space: nowrap">
+              <div style="margin: auto">
+                {{ scope.row.userName }}
+              </div>
+              <el-popover
+                placement="right"
+                title=""
+                trigger="hover">
+                <img slot="reference" :src="scope.row.fsAvatar" style="width: 30px;height: 30px">
+                <img :src="scope.row.fsAvatar" style="max-width: 200px;max-height: 200px">
+              </el-popover>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="课程名称" align="center" prop="courseName"/>
+        <el-table-column label="小节名称" align="center" prop="videoName"/>
+        <el-table-column label="是否全部正确" align="center" prop="isRight">
+          <template slot-scope="scope">
+            <dict-tag :options="sysCompanyOr" :value="scope.row.isRight"></dict-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="销售名称" align="center" prop="companyUserName"/>
+        <el-table-column label="企微员工名称" align="center" prop="qwUserName"/>
+        <el-table-column label="公司名称" align="center" prop="companyName"/>
+        <el-table-column label="创建时间" align="center" prop="createTime"/>
+      </el-table>
+
+      <pagination
+        v-show="answerLogTotal>0"
+        :total="answerLogTotal"
+        :page.sync="answerLogQueryParams.pageNum"
+        :limit.sync="answerLogQueryParams.pageSize"
+        @pagination="answerLogList"
+      />
+    </el-drawer>
+
+    <el-drawer title="红包记录" :visible.sync="openRedLog" size="70%" append-to-body>
+      <el-table border v-loading="" :data="redLogsList">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="记录编号" align="center" prop="logId" />
+        <el-table-column label="批次单号" align="center" prop="outBatchNo" />
+        <el-table-column label="课程名称" align="center" prop="courseId" >
+          <template slot-scope="scope">
+            <span prop="status" v-for="(item, index) in courseLists"    v-if="scope.row.courseId==item.dictValue">{{item.dictLabel}}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="小节名称" align="center" prop="title" />
+<!--        <el-table-column label="会员id" align="center" prop="userId" />-->
+        <el-table-column label="会员用户" align="center" prop="fsNickName">
+          <template slot-scope="scope">
+            <div style="display: flex;white-space: nowrap">
+              <div style="margin: auto">
+                {{scope.row.fsNickName}}
+              </div>
+              <el-popover
+                placement="right"
+                title=""
+                trigger="hover">
+                <img slot="reference" :src="scope.row.fsAvatar" style="width: 30px;height: 30px">
+                <img :src="scope.row.fsAvatar" style="max-width: 200px;max-height: 200px">
+              </el-popover>
+            </div>
+          </template>
+        </el-table-column>
+<!--        <el-table-column label="会员电话" align="center" prop="phone" />-->
+<!--        <el-table-column label="所属销售" align="center" prop="companyUserName" />-->
+<!--        <el-table-column label="所属公司" align="center" prop="companyName" />-->
+        <el-table-column label="转帐金额" align="center" prop="amount" />
+        <el-table-column label="状态" align="center" prop="status" >
+          <template slot-scope="scope">
+            <el-tag>
+              {{
+                scope.row.status === 0 ? "发送中" :
+                scope.row.status === 2 ? "待补发" :
+                "已完成"
+              }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="所属企微" align="center" prop="qwUserName" />
+        <el-table-column label="创建时间" align="center" prop="createTime" />
+      </el-table>
+
+      <pagination
+        v-show="redLogTotal>0"
+        :total="redLogTotal"
+        :page.sync="redLogQueryParams.pageNum"
+        :limit.sync="redLogQueryParams.pageSize"
+        @pagination="redLogList"
+      />
+    </el-drawer>
+
   </div>
 </template>
 
 <script>
 import { deptListCourseWatchLog, getCourseWatchLog, delCourseWatchLog, addCourseWatchLog, updateCourseWatchLog, exportCourseWatchLog } from "@/api/course/courseWatchLog";
-import { courseList,videoList } from '../../../api/course/courseRedPacketLog'
+import {courseList, myListCourseRedPacketLog, videoList} from '@/api/course/courseRedPacketLog'
+import {myListLogs} from "@/api/course/courseAnswerlogs";
 import { getCompanyUserListLikeName } from "@/api/company/companyUser";
 import {getTask} from "@/api/common";
+import Vue from 'vue'
+import Calendar from 'vue-mobile-calendar'
+Vue.use(Calendar)
+
+
 export default {
   name: "CourseWatchLog",
   data() {
     return {
+      // 日历 key 控制刷新
+      scheduleCalendarKey: 0,
+      createCalendarKey: 0,
+      updateCalendarKey: 0,
+      qecCalendarKey: 0,
+
+      createTimeText: '',
+      scheduleTimeText: '',  // 新增
+      updateTimeText: '',    // 新增
+      qecCreateTimeText: '', // 新增
+
+      scheduleTime: [],  // 改为数组
+      createTime: [],    // 改为数组
+      updateTime: [],    // 改为数组
+      qecCreateTime: [], // 改为数组
+
+      // 控制日历显隐
+      showScheduleCalendar: false,
+      showCreateCalendar: false,
+      showUpdateCalendar: false,
+      showQecCalendar: false,
+
+
       activeName:"00",
-      createTime:null,
-      updateTime:null,
       courseLists:[],
       videoList:[],
       logTypeOptions:[],
@@ -245,7 +407,26 @@ export default {
       // 是否显示弹出层
       open: false,
 
-      qecCreateTime:null,
+      //答题记录
+      openAnswerLog: false,
+      loadingAnswerLog: true,
+      answerLogsList: [],
+      answerLogTotal: 0,
+      answerLogQueryParams: {
+        pageNum: 1,
+        pageSize: 10,
+      },
+
+      //红包记录
+      openRedLog: false,
+      loadingRedLog: true,
+      redLogsList: [],
+      redLogTotal: 0,
+      redLogQueryParams: {
+        pageNum: 1,
+        pageSize: 10,
+      },
+
       pickerOptions: {
         disabledDate(time) {
           // 获取6天前的日期(加上今天就是7天)
@@ -293,7 +474,6 @@ export default {
       // 表单校验
       rules: {
       },
-      scheduleTime: null,
       // 员工选项列表
       companyUserOptionsParams: {
         name: undefined,
@@ -315,6 +495,28 @@ export default {
     });
   },
   methods: {
+    // 重置日历组件
+    resetCalendars() {
+      this.scheduleTime = [];
+      this.createTime = [];
+      this.updateTime = [];
+      this.qecCreateTime = [];
+
+      this.scheduleTimeText = '';
+      this.createTimeText = '';
+      this.updateTimeText = '';
+      this.qecCreateTimeText = '';
+
+      // 强制刷新日历组件
+      this.scheduleCalendarKey++;
+      this.createCalendarKey++;
+      this.updateCalendarKey++;
+      this.qecCalendarKey++;
+    },
+    formatDateRange(dates) {
+      if (!dates || dates.length < 2) return '';
+      return dates.map(date => date.format('YYYY-MM-DD')).join(' ~ ');
+    },
     courseChange(row){
       this.queryParams.videoId=null;
       if(row === ''){
@@ -325,35 +527,7 @@ export default {
         this.videoList=response.list
       });
     },
-    createChange() {
-      if (this.createTime != null) {
-        this.queryParams.sTime = this.createTime[0];
-        this.queryParams.eTime = this.createTime[1];
-      } else {
-        this.queryParams.sTime = null;
-        this.queryParams.eTime = null;
-      }
-    },
 
-    updateChange(){
-      if (this.updateTime != null) {
-        this.queryParams.upSTime = this.updateTime[0];
-        this.queryParams.upETime = this.updateTime[1];
-      } else {
-        this.queryParams.upSTime = null;
-        this.queryParams.upETime = null;
-      }
-    },
-
-    qecCreateTimeChange(){
-      if (this.qecCreateTime != null) {
-        this.queryParams.qecSTime = this.qecCreateTime[0];
-        this.queryParams.qecETime = this.qecCreateTime[1];
-      } else {
-        this.queryParams.qecSTime = null;
-        this.queryParams.qecETime = null;
-      }
-    },
     handleClickX(tab,event){
       this.activeName=tab.name;
       if(tab.name=="00"){
@@ -513,15 +687,109 @@ export default {
           }
         }).catch(() => {});
     },
-    handleScheduleTimeChange(val) {
-      if (val) {
-        this.queryParams.scheduleStartTime = val[0];
-        this.queryParams.scheduleEndTime = val[1];
+
+    openAnswerLogFun(row) {
+      this.openAnswerLog = true;
+      this.answerLogQueryParams.watchLogId = row.logId;
+      this.answerLogList();
+    },
+    answerLogList() {
+      this.loadingAnswerLog = true;
+      myListLogs(this.answerLogQueryParams).then(e => {
+        this.answerLogsList = e.rows;
+        this.answerLogTotal = e.total;
+        this.loadingAnswerLog = false;
+      })
+    },
+
+    openRedLogFun(row) {
+      this.openRedLog = true;
+      this.redLogQueryParams.watchLogId = row.logId;
+      this.redLogList();
+    },
+    redLogList() {
+      this.loadingRedLog = true;
+      console.info(this.redLogQueryParams)
+      myListCourseRedPacketLog(this.redLogQueryParams).then(e => {
+        this.redLogsList = e.rows;
+        this.redLogTotal = e.total;
+        this.loadingRedLog = false;
+      })
+    },
+
+    // 营期时间
+    handleScheduleTimeChange(scheduleTime) {
+      if (scheduleTime && scheduleTime.length >= 2) {
+        this.scheduleTimeText = this.formatDateRange(scheduleTime);
+        this.queryParams.scheduleStartTime = scheduleTime.map(date => date.format('YYYY-MM-DD'))[0] || null;
+        this.queryParams.scheduleEndTime = scheduleTime.map(date => date.format('YYYY-MM-DD'))[1] || null;
       } else {
+        this.scheduleTimeText = '';
         this.queryParams.scheduleStartTime = null;
         this.queryParams.scheduleEndTime = null;
       }
     },
+    // 创建时间
+    createChange(createTime) {
+      if (createTime && createTime.length >= 2) {
+        this.createTimeText = this.formatDateRange(createTime);
+        this.queryParams.sTime = createTime.map(date => date.format('YYYY-MM-DD'))[0] || null;
+        this.queryParams.eTime = createTime.map(date => date.format('YYYY-MM-DD'))[1] || null;
+      } else {
+        this.createTimeText = '';
+        this.queryParams.sTime = null;
+        this.queryParams.eTime = null;
+      }
+    },
+
+    // 更新时间
+    updateChange(updateTime) {
+      if (updateTime && updateTime.length >= 2) {
+        this.updateTimeText = this.formatDateRange(updateTime);
+        this.queryParams.upSTime = updateTime.map(date => date.format('YYYY-MM-DD'))[0] || null;
+        this.queryParams.upETime = updateTime.map(date => date.format('YYYY-MM-DD'))[1] || null;
+      } else {
+        this.updateTimeText = '';
+        this.queryParams.upSTime = null;
+        this.queryParams.upETime = null;
+      }
+    },
+
+    // 进线时间
+    qecCreateTimeChange(qecCreateTime) {
+      if (qecCreateTime && qecCreateTime.length >= 2) {
+        // 检查选择的日期范围是否超过7天(包括起始和结束日期)
+        const startDate = new Date(qecCreateTime[0]);
+        const endDate = new Date(qecCreateTime[1]);
+
+        // 设置时间为当天开始,避免时间部分影响计算
+        startDate.setHours(0, 0, 0, 0);
+        endDate.setHours(0, 0, 0, 0);
+
+        const timeDiff = Math.abs(endDate - startDate);
+        const diffDays = Math.ceil(timeDiff / (1000 * 60 * 60 * 24));
+
+        // 如果超过6天的范围(总共7天,包括起始日)
+        if (diffDays > 6) {
+          this.$message.error('进线时间选择范围不能超过7天');
+          // 清空选择
+          this.qecCreateTime = [];
+          this.qecCreateTimeText = '';
+          this.queryParams.qecSTime = null;
+          this.queryParams.qecETime = null;
+          this.qecCalendarKey++;
+          return;
+        }
+
+        this.qecCreateTimeText = this.formatDateRange(qecCreateTime);
+        this.queryParams.qecSTime = qecCreateTime.map(date => date.format('YYYY-MM-DD'))[0] || null;
+        this.queryParams.qecETime = qecCreateTime.map(date => date.format('YYYY-MM-DD'))[1] || null;
+      } else {
+        this.qecCreateTimeText = '';
+        this.queryParams.qecSTime = null;
+        this.queryParams.qecETime = null;
+      }
+    },
     /**
      * 根据名称模糊查询用户列表
      * @param query 参数

+ 327 - 59
src/views/course/courseWatchLog/index.vue

@@ -58,44 +58,69 @@
           />
         </el-select>
       </el-form-item>
+      <!-- 营期时间 -->
       <el-form-item label="营期时间" prop="scheduleTime">
-        <el-date-picker
+        <el-input
+          v-model="scheduleTimeText"
+          placeholder="请选择营期时间"
+          readonly
+          @click.native="showScheduleCalendar = true"
+        />
+        <calendar
           v-model="scheduleTime"
-          type="daterange"
-          size="small"
-          style="width: 240px"
-          value-format="yyyy-MM-dd"
-          range-separator="-"
-          start-placeholder="开始日期"
-          end-placeholder="结束日期"
-          @change="handleScheduleTimeChange">
-        </el-date-picker>
+          mode="during"
+          :show.sync="showScheduleCalendar"
+          @change="handleScheduleTimeChange"
+          :key="scheduleCalendarKey"
+        />
       </el-form-item>
+      <!-- 创建时间 -->
       <el-form-item label="创建时间" prop="createTime">
-        <el-date-picker v-model="createTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange"
-                        range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="createChange"></el-date-picker>
+        <el-input
+          v-model="createTimeText"
+          placeholder="请选择创建时间"
+          readonly
+          @click.native="showCreateCalendar = true"
+        />
+        <calendar
+          v-model="createTime"
+          mode="during"
+          :show.sync="showCreateCalendar"
+          @change="createChange"
+          :key="createCalendarKey"
+        />
       </el-form-item>
+      <!-- 最新更新时间 -->
       <el-form-item label="最新更新时间" prop="updateTime">
-        <el-date-picker v-model="updateTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange"
-                        range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="updateChange"></el-date-picker>
+        <el-input
+          v-model="updateTimeText"
+          placeholder="请选择更新时间"
+          readonly
+          @click.native="showUpdateCalendar = true"
+        />
+        <calendar
+          v-model="updateTime"
+          mode="during"
+          :show.sync="showUpdateCalendar"
+          @change="updateChange"
+          :key="updateCalendarKey"
+        />
       </el-form-item>
-<!--      <el-form-item label="进线时间" prop="updateTime">-->
-<!--        <el-date-picker v-model="qecCreateTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange"-->
-<!--                        range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="qecCreateTimeChange"></el-date-picker>-->
-<!--      </el-form-item>-->
-      <el-form-item label="进线时间" prop="updateTime">
-        <el-date-picker
+      <!-- 进线时间 -->
+      <el-form-item label="进线时间" prop="qecCreateTime">
+        <el-input
+          v-model="qecCreateTimeText"
+          placeholder="请选择进线时间"
+          readonly
+          @click.native="showQecCalendar = true"
+        />
+        <calendar
           v-model="qecCreateTime"
-          size="small"
-          style="width: 220px"
-          value-format="yyyy-MM-dd"
-          type="daterange"
-          range-separator="-"
-          start-placeholder="开始日期"
-          end-placeholder="结束日期"
+          mode="during"
+          :show.sync="showQecCalendar"
           @change="qecCreateTimeChange"
-          :picker-options="pickerOptions"
-        ></el-date-picker>
+          :key="qecCalendarKey"
+        />
       </el-form-item>
 
       <el-form-item>
@@ -189,6 +214,15 @@
       <el-table-column label="完课时间" align="center" prop="finishTime" />
       <el-table-column label="营期时间" align="center" prop="campPeriodTime" />
       <el-table-column label="进线时间" align="center" prop="qecCreateTime" />
+      <el-table-column
+        fixed="right"
+        label="操作"
+        width="100">
+        <template slot-scope="scope">
+          <el-button @click="openAnswerLogFun(scope.row)" type="text" size="small">答题记录</el-button>
+          <el-button @click="openRedLogFun(scope.row)" type="text" size="small">红包记录</el-button>
+        </template>
+      </el-table-column>
     </el-table>
 
     <pagination
@@ -199,6 +233,102 @@
       @pagination="getList"
     />
 
+    <el-drawer title="答题记录" :visible.sync="openAnswerLog" size="70%" append-to-body>
+      <el-table border v-loading="" :data="answerLogsList">
+        <el-table-column label="会员用户" align="center" prop="userName">
+          <template slot-scope="scope">
+            <div style="display: flex;white-space: nowrap">
+              <div style="margin: auto">
+                {{ scope.row.userName }}
+              </div>
+              <el-popover
+                placement="right"
+                title=""
+                trigger="hover">
+                <img slot="reference" :src="scope.row.fsAvatar" style="width: 30px;height: 30px">
+                <img :src="scope.row.fsAvatar" style="max-width: 200px;max-height: 200px">
+              </el-popover>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="课程名称" align="center" prop="courseName"/>
+        <el-table-column label="小节名称" align="center" prop="videoName"/>
+        <el-table-column label="是否全部正确" align="center" prop="isRight">
+          <template slot-scope="scope">
+            <dict-tag :options="sysCompanyOr" :value="scope.row.isRight"></dict-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="销售名称" align="center" prop="companyUserName"/>
+        <el-table-column label="企微员工名称" align="center" prop="qwUserName"/>
+        <el-table-column label="公司名称" align="center" prop="companyName"/>
+        <el-table-column label="创建时间" align="center" prop="createTime"/>
+      </el-table>
+
+      <pagination
+        v-show="answerLogTotal>0"
+        :total="answerLogTotal"
+        :page.sync="answerLogQueryParams.pageNum"
+        :limit.sync="answerLogQueryParams.pageSize"
+        @pagination="answerLogList"
+      />
+    </el-drawer>
+
+    <el-drawer title="红包记录" :visible.sync="openRedLog" size="70%" append-to-body>
+      <el-table border v-loading="" :data="redLogsList">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="记录编号" align="center" prop="logId" />
+        <el-table-column label="批次单号" align="center" prop="outBatchNo" />
+        <el-table-column label="课程名称" align="center" prop="courseId" >
+          <template slot-scope="scope">
+            <span prop="status" v-for="(item, index) in courseLists"    v-if="scope.row.courseId==item.dictValue">{{item.dictLabel}}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="小节名称" align="center" prop="title" />
+<!--        <el-table-column label="会员id" align="center" prop="userId" />-->
+        <el-table-column label="会员用户" align="center" prop="fsNickName">
+          <template slot-scope="scope">
+            <div style="display: flex;white-space: nowrap">
+              <div style="margin: auto">
+                {{scope.row.fsNickName}}
+              </div>
+              <el-popover
+                placement="right"
+                title=""
+                trigger="hover">
+                <img slot="reference" :src="scope.row.fsAvatar" style="width: 30px;height: 30px">
+                <img :src="scope.row.fsAvatar" style="max-width: 200px;max-height: 200px">
+              </el-popover>
+            </div>
+          </template>
+        </el-table-column>
+<!--        <el-table-column label="会员电话" align="center" prop="phone" />-->
+<!--        <el-table-column label="所属销售" align="center" prop="companyUserName" />-->
+<!--        <el-table-column label="所属公司" align="center" prop="companyName" />-->
+        <el-table-column label="转帐金额" align="center" prop="amount" />
+        <el-table-column label="状态" align="center" prop="status" >
+          <template slot-scope="scope">
+            <el-tag>
+              {{
+                scope.row.status === 0 ? "发送中" :
+                scope.row.status === 2 ? "待补发" :
+                "已完成"
+              }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="所属企微" align="center" prop="qwUserName" />
+        <el-table-column label="创建时间" align="center" prop="createTime" />
+      </el-table>
+
+      <pagination
+        v-show="redLogTotal>0"
+        :total="redLogTotal"
+        :page.sync="redLogQueryParams.pageNum"
+        :limit.sync="redLogQueryParams.pageSize"
+        @pagination="redLogList"
+      />
+    </el-drawer>
+
     <el-dialog title="批量添加标签" :visible.sync="tagOpen" width="800px" append-to-body>
       <div>搜索标签:
         <el-input v-model="tagChange.tagName" placeholder="请输入标签名称" clearable size="small" style="width: 200px;margin-right: 10px" />
@@ -305,23 +435,46 @@
 
 <script>
 import { listCourseWatchLog, getCourseWatchLog, delCourseWatchLog, addCourseWatchLog, updateCourseWatchLog, exportCourseWatchLog } from "@/api/course/courseWatchLog";
-import { courseList,videoList } from '@/api/course/courseRedPacketLog'
+import {courseList, myListCourseRedPacketLog, videoList} from '@/api/course/courseRedPacketLog'
+import {myListLogs} from "@/api/course/courseAnswerlogs";
 import {allListTagGroup} from "../../../api/qw/tagGroup";
 import {searchTags} from "../../../api/qw/tag";
 import {addTagByWatch, delTagByWatch} from "../../../api/qw/externalContact";
+import Vue from 'vue'
+import Calendar from 'vue-mobile-calendar'
+Vue.use(Calendar)
+
 export default {
   name: "CourseWatchLog",
   data() {
     return {
+      // 日历 key 控制刷新
+      scheduleCalendarKey: 0,
+      createCalendarKey: 0,
+      updateCalendarKey: 0,
+      qecCalendarKey: 0,
+
+      createTimeText: '',
+      scheduleTimeText: '',  // 新增
+      updateTimeText: '',    // 新增
+      qecCreateTimeText: '', // 新增
+
+      scheduleTime: [],  // 改为数组
+      createTime: [],    // 改为数组
+      updateTime: [],    // 改为数组
+      qecCreateTime: [], // 改为数组
+
+      // 控制日历显隐
+      showScheduleCalendar: false,
+      showCreateCalendar: false,
+      showUpdateCalendar: false,
+      showQecCalendar: false,
 
       resultDialogVisible: false,
       resultMessage: '',
       resultTitle:'',
 
       activeName:"2",
-      createTime:null,
-      updateTime:null,
-      qecCreateTime:null,
       pickerOptions: {
         disabledDate(time) {
           // 获取6天前的日期(加上今天就是7天)
@@ -392,6 +545,25 @@ export default {
       },
 
 
+      //答题记录
+      openAnswerLog: false,
+      loadingAnswerLog: true,
+      answerLogsList: [],
+      answerLogTotal: 0,
+      answerLogQueryParams: {
+        pageNum: 1,
+        pageSize: 10,
+      },
+
+      //红包记录
+      openRedLog: false,
+      loadingRedLog: true,
+      redLogsList: [],
+      redLogTotal: 0,
+      redLogQueryParams: {
+        pageNum: 1,
+        pageSize: 10,
+      },
 
       // 查询参数
       queryParams: {
@@ -423,7 +595,6 @@ export default {
       // 表单校验
       rules: {
       },
-      scheduleTime: null,
     };
   },
   created() {
@@ -436,6 +607,28 @@ export default {
     });
   },
   methods: {
+    // 重置日历组件
+    resetCalendars() {
+      this.scheduleTime = [];
+      this.createTime = [];
+      this.updateTime = [];
+      this.qecCreateTime = [];
+
+      this.scheduleTimeText = '';
+      this.createTimeText = '';
+      this.updateTimeText = '';
+      this.qecCreateTimeText = '';
+
+      // 强制刷新日历组件
+      this.scheduleCalendarKey++;
+      this.createCalendarKey++;
+      this.updateCalendarKey++;
+      this.qecCalendarKey++;
+    },
+    formatDateRange(dates) {
+      if (!dates || dates.length < 2) return '';
+      return dates.map(date => date.format('YYYY-MM-DD')).join(' ~ ');
+    },
     courseChange(row){
       this.queryParams.videoId=null;
       if(row === ''){
@@ -446,31 +639,78 @@ export default {
         this.videoList=response.list
       });
     },
-    createChange() {
-      if (this.createTime != null) {
-        this.queryParams.sTime = this.createTime[0];
-        this.queryParams.eTime = this.createTime[1];
+
+    // 营期时间
+    handleScheduleTimeChange(scheduleTime) {
+      if (scheduleTime && scheduleTime.length >= 2) {
+        this.scheduleTimeText = this.formatDateRange(scheduleTime);
+        this.queryParams.scheduleStartTime = scheduleTime.map(date => date.format('YYYY-MM-DD'))[0] || null;
+        this.queryParams.scheduleEndTime = scheduleTime.map(date => date.format('YYYY-MM-DD'))[1] || null;
+        console.log(this.queryParams.scheduleStartTime)
+        console.log(this.queryParams.scheduleEndTime)
+      } else {
+        this.scheduleTimeText = '';
+        this.queryParams.scheduleStartTime = null;
+        this.queryParams.scheduleEndTime = null;
+      }
+    },
+    // 创建时间
+    createChange(createTime) {
+      if (createTime && createTime.length >= 2) {
+        this.createTimeText = this.formatDateRange(createTime);
+        this.queryParams.sTime = createTime.map(date => date.format('YYYY-MM-DD'))[0] || null;
+        this.queryParams.eTime = createTime.map(date => date.format('YYYY-MM-DD'))[1] || null;
       } else {
+        this.createTimeText = '';
         this.queryParams.sTime = null;
         this.queryParams.eTime = null;
       }
     },
 
-    updateChange(){
-      if (this.updateTime != null) {
-        this.queryParams.upSTime = this.updateTime[0];
-        this.queryParams.upETime = this.updateTime[1];
+    // 更新时间
+    updateChange(updateTime) {
+      if (updateTime && updateTime.length >= 2) {
+        this.updateTimeText = this.formatDateRange(updateTime);
+        this.queryParams.upSTime = updateTime.map(date => date.format('YYYY-MM-DD'))[0] || null;
+        this.queryParams.upETime = updateTime.map(date => date.format('YYYY-MM-DD'))[1] || null;
       } else {
+        this.updateTimeText = '';
         this.queryParams.upSTime = null;
         this.queryParams.upETime = null;
       }
     },
 
-    qecCreateTimeChange(){
-      if (this.qecCreateTime != null) {
-        this.queryParams.qecSTime = this.qecCreateTime[0];
-        this.queryParams.qecETime = this.qecCreateTime[1];
+    // 进线时间
+    qecCreateTimeChange(qecCreateTime) {
+      if (qecCreateTime && qecCreateTime.length >= 2) {
+        // 检查选择的日期范围是否超过7天(包括起始和结束日期)
+        const startDate = new Date(qecCreateTime[0]);
+        const endDate = new Date(qecCreateTime[1]);
+
+        // 设置时间为当天开始,避免时间部分影响计算
+        startDate.setHours(0, 0, 0, 0);
+        endDate.setHours(0, 0, 0, 0);
+
+        const timeDiff = Math.abs(endDate - startDate);
+        const diffDays = Math.ceil(timeDiff / (1000 * 60 * 60 * 24));
+
+        // 如果超过6天的范围(总共7天,包括起始日)
+        if (diffDays > 6) {
+          this.$message.error('进线时间选择范围不能超过7天');
+          // 清空选择
+          this.qecCreateTime = [];
+          this.qecCreateTimeText = '';
+          this.queryParams.qecSTime = null;
+          this.queryParams.qecETime = null;
+          this.qecCalendarKey++;
+          return;
+        }
+
+        this.qecCreateTimeText = this.formatDateRange(qecCreateTime);
+        this.queryParams.qecSTime = qecCreateTime.map(date => date.format('YYYY-MM-DD'))[0] || null;
+        this.queryParams.qecETime = qecCreateTime.map(date => date.format('YYYY-MM-DD'))[1] || null;
       } else {
+        this.qecCreateTimeText = '';
         this.queryParams.qecSTime = null;
         this.queryParams.qecETime = null;
       }
@@ -521,10 +761,17 @@ export default {
         companyUserId: null,
         companyId: null,
         courseId: null,
+        sTime:null,
+        eTime:null,
+        upSTime:null,
+        upETime:null,
+        qecSTime:null,
+        qecETime:null,
         scheduleStartTime: null,
         scheduleEndTime: null,
       };
-      this.scheduleTime=null;
+      // 统一重置日历组件
+      this.resetCalendars();
       this.resetForm("form");
     },
     /** 搜索按钮操作 */
@@ -535,8 +782,6 @@ export default {
     /** 重置按钮操作 */
     resetQuery() {
       this.resetForm("queryForm");
-      this.createTime = null;
-      this.qecCreateTime=null;
       this.queryParams.sTime = null;
       this.queryParams.eTime = null;
       this.queryParams.upSTime = null;
@@ -545,8 +790,10 @@ export default {
       this.queryParams.qecETime = null;
       this.queryParams.scheduleStartTime = null;
       this.queryParams.scheduleEndTime = null;
-      this.scheduleTime=null;
-      this.updateTime=null;
+
+      // 统一重置日历组件
+      this.resetCalendars();
+
       this.handleQuery();
     },
     // 多选框选中数据
@@ -620,15 +867,36 @@ export default {
           this.exportLoading = false;
         }).catch(() => {});
     },
-    handleScheduleTimeChange(val) {
-      if (val) {
-        this.queryParams.scheduleStartTime = val[0];
-        this.queryParams.scheduleEndTime = val[1];
-      } else {
-        this.queryParams.scheduleStartTime = null;
-        this.queryParams.scheduleEndTime = null;
-      }
+
+    openAnswerLogFun(row) {
+      this.openAnswerLog = true;
+      this.answerLogQueryParams.watchLogId = row.logId;
+      this.answerLogList();
     },
+    answerLogList() {
+      this.loadingAnswerLog = true;
+      myListLogs(this.answerLogQueryParams).then(e => {
+        this.answerLogsList = e.rows;
+        this.answerLogTotal = e.total;
+        this.loadingAnswerLog = false;
+      })
+    },
+
+    openRedLogFun(row) {
+      this.openRedLog = true;
+      this.redLogQueryParams.watchLogId = row.logId;
+      this.redLogList();
+    },
+    redLogList() {
+      this.loadingRedLog = true;
+      console.info(this.redLogQueryParams)
+      myListCourseRedPacketLog(this.redLogQueryParams).then(e => {
+        this.redLogsList = e.rows;
+        this.redLogTotal = e.total;
+        this.loadingRedLog = false;
+      })
+    },
+
     handleSendTypeChange() {
       this.handleQuery(); // 重新查询列表
     },

+ 0 - 16
src/views/course/courseWatchLog/myCourseWatchLog.vue

@@ -83,7 +83,6 @@
           start-placeholder="开始日期"
           end-placeholder="结束日期"
           @change="qecCreateTimeChange"
-          :picker-options="pickerOptions"
         ></el-date-picker>
       </el-form-item>
       <el-form-item>
@@ -299,21 +298,6 @@ export default {
 
       createTime:null,
       qecCreateTime:null,
-      pickerOptions: {
-        disabledDate(time) {
-          // 获取6天前的日期(加上今天就是7天)
-          const sixDaysAgo = new Date();
-          sixDaysAgo.setDate(sixDaysAgo.getDate() - 6);
-          sixDaysAgo.setHours(0, 0, 0, 0);
-
-          // 获取明天的日期(不包括今天)
-          const tomorrow = new Date();
-          tomorrow.setDate(tomorrow.getDate() + 1);
-          tomorrow.setHours(0, 0, 0, 0);
-
-          return time.getTime() < sixDaysAgo.getTime() || time.getTime() >= tomorrow.getTime();
-        }
-      },
       courseLists:[],
       videoList:[],
       myQwUserList:[],

+ 181 - 70
src/views/course/courseWatchLog/watchLog.vue

@@ -79,42 +79,69 @@
           />
         </el-select>
       </el-form-item>
+      <!-- 营期时间 -->
       <el-form-item label="营期时间" prop="scheduleTime">
-        <el-date-picker
+        <el-input
+          v-model="scheduleTimeText"
+          placeholder="请选择营期时间"
+          readonly
+          @click.native="showScheduleCalendar = true"
+        />
+        <calendar
           v-model="scheduleTime"
-          type="daterange"
-          size="small"
-          style="width: 240px"
-          value-format="yyyy-MM-dd"
-          range-separator="-"
-          start-placeholder="开始日期"
-          end-placeholder="结束日期"
-          @change="handleScheduleTimeChange">
-        </el-date-picker>
+          mode="during"
+          :show.sync="showScheduleCalendar"
+          @change="handleScheduleTimeChange"
+          :key="scheduleCalendarKey"
+        />
       </el-form-item>
+      <!-- 创建时间 -->
       <el-form-item label="创建时间" prop="createTime">
-        <el-date-picker v-model="createTime" size="small" style="width: 220px" value-format="yyyy-MM-dd"
-                        type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
-                        @change="createChange"></el-date-picker>
+        <el-input
+          v-model="createTimeText"
+          placeholder="请选择创建时间"
+          readonly
+          @click.native="showCreateCalendar = true"
+        />
+        <calendar
+          v-model="createTime"
+          mode="during"
+          :show.sync="showCreateCalendar"
+          @change="createChange"
+          :key="createCalendarKey"
+        />
       </el-form-item>
+      <!-- 最新更新时间 -->
       <el-form-item label="最新更新时间" prop="updateTime">
-        <el-date-picker v-model="updateTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange"
-                        range-separator="-" start-placeholder="开始日期"
-                        end-placeholder="结束日期" @change="updateChange"></el-date-picker>
+        <el-input
+          v-model="updateTimeText"
+          placeholder="请选择更新时间"
+          readonly
+          @click.native="showUpdateCalendar = true"
+        />
+        <calendar
+          v-model="updateTime"
+          mode="during"
+          :show.sync="showUpdateCalendar"
+          @change="updateChange"
+          :key="updateCalendarKey"
+        />
       </el-form-item>
-      <el-form-item label="进线时间" prop="updateTime">
-        <el-date-picker
+      <!-- 进线时间 -->
+      <el-form-item label="进线时间" prop="qecCreateTime">
+        <el-input
+          v-model="qecCreateTimeText"
+          placeholder="请选择进线时间"
+          readonly
+          @click.native="showQecCalendar = true"
+        />
+        <calendar
           v-model="qecCreateTime"
-          size="small"
-          style="width: 220px"
-          value-format="yyyy-MM-dd"
-          type="daterange"
-          range-separator="-"
-          start-placeholder="开始日期"
-          end-placeholder="结束日期"
+          mode="during"
+          :show.sync="showQecCalendar"
           @change="qecCreateTimeChange"
-          :picker-options="pickerOptions"
-        ></el-date-picker>
+          :key="qecCalendarKey"
+        />
       </el-form-item>
 
       <el-form-item>
@@ -432,22 +459,41 @@ import {getMyQwUserList} from "@/api/qw/user";
 import {searchTags} from "../../../api/qw/tag";
 import {addTagByWatch, delTagByWatch} from "../../../api/qw/externalContact";
 import {allListTagGroup} from "../../../api/qw/tagGroup";
-
+import Vue from 'vue'
+import Calendar from 'vue-mobile-calendar'
+Vue.use(Calendar)
 
 export default {
   name: "CourseWatchLog",
   data() {
     return {
+      // 日历 key 控制刷新
+      scheduleCalendarKey: 0,
+      createCalendarKey: 0,
+      updateCalendarKey: 0,
+      qecCalendarKey: 0,
+
+      createTimeText: '',
+      scheduleTimeText: '',  // 新增
+      updateTimeText: '',    // 新增
+      qecCreateTimeText: '', // 新增
+
+      scheduleTime: [],  // 改为数组
+      createTime: [],    // 改为数组
+      updateTime: [],    // 改为数组
+      qecCreateTime: [], // 改为数组
+
+      // 控制日历显隐
+      showScheduleCalendar: false,
+      showCreateCalendar: false,
+      showUpdateCalendar: false,
+      showQecCalendar: false,
 
       resultDialogVisible: false,
       resultMessage: '',
       resultTitle:'',
 
       activeName:"2",
-      createTime: null,
-      updateTime:null,
-
-      qecCreateTime:null,
       pickerOptions: {
         disabledDate(time) {
           // 获取6天前的日期(加上今天就是7天)
@@ -475,7 +521,6 @@ export default {
       ids: [],
       // 非单个禁用
       single: true,
-      scheduleTime: null,
       // 非多个禁用
       multiple: true,
       // 显示搜索条件
@@ -589,6 +634,28 @@ export default {
     });
   },
   methods: {
+    // 重置日历组件
+    resetCalendars() {
+      this.scheduleTime = [];
+      this.createTime = [];
+      this.updateTime = [];
+      this.qecCreateTime = [];
+
+      this.scheduleTimeText = '';
+      this.createTimeText = '';
+      this.updateTimeText = '';
+      this.qecCreateTimeText = '';
+
+      // 强制刷新日历组件
+      this.scheduleCalendarKey++;
+      this.createCalendarKey++;
+      this.updateCalendarKey++;
+      this.qecCalendarKey++;
+    },
+    formatDateRange(dates) {
+      if (!dates || dates.length < 2) return '';
+      return dates.map(date => date.format('YYYY-MM-DD')).join(' ~ ');
+    },
     courseChange(row) {
       this.queryParams.videoId = null;
       if (row === '') {
@@ -608,34 +675,6 @@ export default {
       }
       this.getList();
     },
-    createChange() {
-      if (this.createTime != null) {
-        this.queryParams.sTime = this.createTime[0];
-        this.queryParams.eTime = this.createTime[1];
-      } else {
-        this.queryParams.sTime = null;
-        this.queryParams.eTime = null;
-      }
-    },
-    updateChange(){
-      if (this.updateTime != null) {
-        this.queryParams.upSTime = this.updateTime[0];
-        this.queryParams.upETime = this.updateTime[1];
-      } else {
-        this.queryParams.upSTime = null;
-        this.queryParams.upETime = null;
-      }
-    },
-
-    qecCreateTimeChange(){
-      if (this.qecCreateTime != null) {
-        this.queryParams.qecSTime = this.qecCreateTime[0];
-        this.queryParams.qecETime = this.qecCreateTime[1];
-      } else {
-        this.queryParams.qecSTime = null;
-        this.queryParams.qecETime = null;
-      }
-    },
 
     handleClickX(tab) {
       this.activeName=tab.name;
@@ -647,15 +686,81 @@ export default {
       this.getList()
     },
 
-    handleScheduleTimeChange(val) {
-      if (val) {
-        this.queryParams.scheduleStartTime = val[0];
-        this.queryParams.scheduleEndTime = val[1];
+    // 营期时间
+    handleScheduleTimeChange(scheduleTime) {
+      if (scheduleTime && scheduleTime.length >= 2) {
+        this.scheduleTimeText = this.formatDateRange(scheduleTime);
+        this.queryParams.scheduleStartTime = scheduleTime.map(date => date.format('YYYY-MM-DD'))[0] || null;
+        this.queryParams.scheduleEndTime = scheduleTime.map(date => date.format('YYYY-MM-DD'))[1] || null;
+        console.log(this.queryParams.scheduleStartTime)
+        console.log(this.queryParams.scheduleEndTime)
       } else {
+        this.scheduleTimeText = '';
         this.queryParams.scheduleStartTime = null;
         this.queryParams.scheduleEndTime = null;
       }
     },
+    // 创建时间
+    createChange(createTime) {
+      if (createTime && createTime.length >= 2) {
+        this.createTimeText = this.formatDateRange(createTime);
+        this.queryParams.sTime = createTime.map(date => date.format('YYYY-MM-DD'))[0] || null;
+        this.queryParams.eTime = createTime.map(date => date.format('YYYY-MM-DD'))[1] || null;
+      } else {
+        this.createTimeText = '';
+        this.queryParams.sTime = null;
+        this.queryParams.eTime = null;
+      }
+    },
+
+    // 更新时间
+    updateChange(updateTime) {
+      if (updateTime && updateTime.length >= 2) {
+        this.updateTimeText = this.formatDateRange(updateTime);
+        this.queryParams.upSTime = updateTime.map(date => date.format('YYYY-MM-DD'))[0] || null;
+        this.queryParams.upETime = updateTime.map(date => date.format('YYYY-MM-DD'))[1] || null;
+      } else {
+        this.updateTimeText = '';
+        this.queryParams.upSTime = null;
+        this.queryParams.upETime = null;
+      }
+    },
+
+    // 进线时间
+    qecCreateTimeChange(qecCreateTime) {
+      if (qecCreateTime && qecCreateTime.length >= 2) {
+        // 检查选择的日期范围是否超过7天(包括起始和结束日期)
+        const startDate = new Date(qecCreateTime[0]);
+        const endDate = new Date(qecCreateTime[1]);
+
+        // 设置时间为当天开始,避免时间部分影响计算
+        startDate.setHours(0, 0, 0, 0);
+        endDate.setHours(0, 0, 0, 0);
+
+        const timeDiff = Math.abs(endDate - startDate);
+        const diffDays = Math.ceil(timeDiff / (1000 * 60 * 60 * 24));
+
+        // 如果超过6天的范围(总共7天,包括起始日)
+        if (diffDays > 6) {
+          this.$message.error('进线时间选择范围不能超过7天');
+          // 清空选择
+          this.qecCreateTime = [];
+          this.qecCreateTimeText = '';
+          this.queryParams.qecSTime = null;
+          this.queryParams.qecETime = null;
+          this.qecCalendarKey++;
+          return;
+        }
+
+        this.qecCreateTimeText = this.formatDateRange(qecCreateTime);
+        this.queryParams.qecSTime = qecCreateTime.map(date => date.format('YYYY-MM-DD'))[0] || null;
+        this.queryParams.qecETime = qecCreateTime.map(date => date.format('YYYY-MM-DD'))[1] || null;
+      } else {
+        this.qecCreateTimeText = '';
+        this.queryParams.qecSTime = null;
+        this.queryParams.qecETime = null;
+      }
+    },
     /** 查询短链课程看课记录列表 */
     getList() {
       this.loading = true;
@@ -690,10 +795,17 @@ export default {
         companyUserId: null,
         companyId: null,
         courseId: null,
+        sTime:null,
+        eTime:null,
+        upSTime:null,
+        upETime:null,
+        qecSTime:null,
+        qecETime:null,
         scheduleStartTime: null,
         scheduleEndTime: null,
       };
-      this.scheduleTime=null;
+      // 统一重置日历组件
+      this.resetCalendars();
       this.resetForm("form");
     },
     /** 搜索按钮操作 */
@@ -704,8 +816,6 @@ export default {
     /** 重置按钮操作 */
     resetQuery() {
       this.resetForm("queryForm");
-      this.createTime = null;
-      this.qecCreateTime=null;
       this.queryParams.sTime = null;
       this.queryParams.eTime = null;
       this.queryParams.upSTime = null;
@@ -714,8 +824,9 @@ export default {
       this.queryParams.qecETime = null;
       this.queryParams.scheduleStartTime = null;
       this.queryParams.scheduleEndTime = null;
-      this.scheduleTime=null;
-      this.updateTime=null;
+      // 统一重置日历组件
+      this.resetCalendars();
+
       this.handleQuery();
     },
     // 多选框选中数据

+ 6 - 23
src/views/course/userCoursePeriod/batchRedPacket.vue

@@ -1,10 +1,9 @@
 <template>
   <el-dialog
     title="批量设置红包"
-    :visible.sync="dialogVisible"
+    :visible.sync="visible"
     width="800px"
     append-to-body
-    @close="handleClose"
   >
     <el-table
       v-loading="loading"
@@ -24,15 +23,14 @@
         align="center"
       />
       <el-table-column
-        label="金额(0.1-0.3元)"
+        label="金额"
         align="center"
         width="200"
       >
         <template slot-scope="scope">
           <el-input-number
             v-model="scope.row.amount"
-            :min="0.1"
-            :max="0.3"
+            :min="0.01"
             :precision="2"
             :step="0.01"
             size="small"
@@ -67,22 +65,14 @@ export default {
   data() {
     return {
       loading: false,
-      tableData: [],
-      dialogVisible: false
+      tableData: []
     }
   },
   watch: {
     visible(val) {
-      this.dialogVisible = val;
       if (val) {
         this.initTableData()
       }
-    },
-    dialogVisible(val) {
-      if (!val) {
-        // 当弹窗关闭时,通知父组件
-        this.$emit('update:visible', false);
-      }
     }
   },
   methods: {
@@ -90,18 +80,11 @@ export default {
     initTableData() {
       this.tableData = this.selectedData.map(item => ({
         ...item,
-        amount: 0.1
+        amount: 0.01
       }))
     },
     // 保存
     handleSave() {
-      // 验证金额范围
-      const invalidItems = this.tableData.filter(item => item.amount < 0.1 || item.amount > 0.3);
-      if (invalidItems.length > 0) {
-        this.$message.error('红包金额需要在0.1元至0.3元之间');
-        return;
-      }
-
       this.$confirm(`是否确定?确定后营期下的所有公司红包金额都将设置成对应值`, '提示', {
         confirmButtonText: '确定',
         cancelButtonText: '取消',
@@ -130,7 +113,7 @@ export default {
     },
     // 关闭
     handleClose() {
-      this.dialogVisible = false;
+      this.$emit('update:visible', false)
     }
   }
 }

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 585 - 383
src/views/course/userCoursePeriod/index.vue


+ 15 - 26
src/views/course/userCoursePeriod/redPacket.vue

@@ -11,7 +11,6 @@
               size="mini"
               type="text"
               @click="handleInputAmount(scope.row)"
-              v-hasPermi="['course:period:setCourseRedPacket']"
             >设置红包</el-button>
           </template>
         </el-table-column>
@@ -27,12 +26,11 @@
         <el-table-column label="课程" prop="courseName" align="center" />
         <el-table-column label="小节" prop="videoName" align="center" />
         <el-table-column label="营期日期" prop="dayDate" align="center"/>
-        <el-table-column label="红包金额(0.0-0.3元)" width="200px" align="center">
+        <el-table-column label="红包金额" width="200px" align="center">
           <template slot-scope="scope">
             <el-input-number
               v-model="scope.row.amount"
-              :min="0.0"
-              :max="0.3"
+              :min="0"
               :precision="2"
               :step="0.01"
               size="small"
@@ -52,7 +50,7 @@
 </template>
 
 <script>
-import { getPeriodCompanyList, batchSaveRedPacket, getPeriodRedPacketList } from "@/api/course/userCoursePeriod";
+import { getPeriodCompanyList, getDays, batchSaveRedPacket, getPeriodRedPacketList } from "@/api/course/userCoursePeriod";
 import redPacket from "@/views/course/userCoursePeriod/redPacket.vue";
 
 export default {
@@ -129,38 +127,29 @@ export default {
         periodId: this.periodId,
         companyId: this.currentCompany.companyId
       }).then(response => {
-        console.log("-----------",JSON.stringify(response.data))
         this.redPacketList = (response.data || []).map(item => ({
           ...item,
-          // amount: item.amount || 0.1
-          amount:item.amount ?? 0.1
+          amount: item.amount || 0
         }));
       });
     },
     // 保存红包金额
     handleSave() {
-      // 筛选出有金额的项目
-      const validAmountItems = this.redPacketList.filter(item => item.amount >= 0);
-      if (validAmountItems.length === 0) {
-        this.$message.warning('请至少设置一个红包金额');
-        return;
-      }
+      const saveData = this.redPacketList
+        .filter(item => item.amount > 0)
+        .map(item => ({
+          companyId: this.currentCompany.companyId,
+          redPacketMoney: item.amount,
+          videoId: item.videoId,
+          periodId: this.periodId,
+          dataType: 2
+        }));
 
-      // 验证金额范围
-      const invalidItems = validAmountItems.filter(item => item.amount < 0.0 || item.amount > 0.3);
-      if (invalidItems.length > 0) {
-        this.$message.error('红包金额需要在0.0元至0.3元之间');
+      if (saveData.length === 0) {
+        this.$message.warning('请至少设置一个红包金额');
         return;
       }
 
-      const saveData = validAmountItems.map(item => ({
-        companyId: this.currentCompany.companyId,
-        redPacketMoney: item.amount,
-        videoId: item.videoId,
-        periodId: this.periodId,
-        dataType: 2
-      }));
-
       batchSaveRedPacket(saveData).then(response => {
         if (response.code === 200) {
           this.$message.success('保存成功');

+ 4 - 6
src/views/member/list.vue

@@ -319,8 +319,7 @@
         :with-header="false"
         size="75%"
           :title="show.title" :visible.sync="show.open">
-      <!--         <userDetails  ref="userDetails" />-->
-      <userDetailsByNew  ref="userDetailsByNew" />
+      <userDetails  ref="userDetails" />
     </el-drawer>
 
   </div>
@@ -331,14 +330,13 @@ import { listUser, getUser, addUser, updateUser, delUser, exportUser, auditUser
 import {transferUser} from "@/api/users/user";
 import {getUserList} from "@/api/company/companyUser";
 import userDetails from '@/views/store/components/userDetails.vue';
-import userDetailsByNew from './userDetails.vue';
 export default {
   name: "FsUser",
-  components: {userDetails,userDetailsByNew},
+  components: {userDetails},
   data() {
     return {
       show:{
-        title:"会员详情",
+        
         open:false,
       },
       cusTransfer: {
@@ -427,7 +425,7 @@ export default {
     handledetails(row){
             this.show.open=true;
             setTimeout(() => {
-                 this.$refs.userDetailsByNew.getDetails(row.userId);
+                 this.$refs.userDetails.getDetails(row.userId);
             }, 1);
      },
     /** 提交转移按钮 (如果使用对话框) */

+ 1 - 0
src/views/qw/externalContact/deptIndex.vue

@@ -309,6 +309,7 @@
       <el-table-column label="客户称呼"  prop="stageStatus" width="110px"/>
       <el-table-column label="销售企微昵称" align="center" prop="qwUserName" width="120px"/>
       <el-table-column label="企微部门" align="center" prop="departmentName" width="120px"/>
+      <el-table-column label="注册时间" align="center" prop="registerTime" width="100px" />
       <el-table-column label="用户类别" align="center" prop="type">
         <template slot-scope="scope">
           <dict-tag :options="typeOptions" :value="scope.row.type"/>

+ 1 - 0
src/views/qw/externalContact/index.vue

@@ -349,6 +349,7 @@
       <el-table-column label="添加时间" align="center" prop="createTime" width="100px" />
       <el-table-column label="流失时间" align="center" prop="lossTime" width="100px" />
       <el-table-column label="删除时间" align="center" prop="delTime" width="100px" />
+      <el-table-column label="注册时间" align="center" prop="registerTime" width="100px" />
       <el-table-column label="备注电话号码" align="center" prop="remarkMobiles" width="150px">
         <template slot-scope="scope">
           <div v-for="i in JSON.parse(scope.row.remarkMobiles)" :key="i">{{i}}</div>

+ 1 - 0
src/views/qw/externalContact/myExternalContact.vue

@@ -339,6 +339,7 @@
       <el-table-column label="添加时间" align="center" prop="createTime" width="100px" />
       <el-table-column label="流失时间" align="center" prop="lossTime" width="100px" />
       <el-table-column label="删除时间" align="center" prop="delTime" width="100px" />
+      <el-table-column label="注册时间" align="center" prop="registerTime" width="100px" />
       <el-table-column label="备注电话号码" align="center" prop="remarkMobiles" width="150px">
         <template slot-scope="scope">
           <div v-for="i in JSON.parse(scope.row.remarkMobiles)" :key="i">{{i}}</div>

+ 10 - 1
src/views/qw/externalContactLoss/index.vue

@@ -1,6 +1,15 @@
 <template>
   <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="80px">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="120px">
+      <el-form-item label="企微员工账号" prop="userId">
+        <el-input
+          v-model="queryParams.userId"
+          placeholder="请输入企微员工账号"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
       <el-form-item label="企微公司" prop="corpId">
           <el-select v-model="queryParams.corpId" placeholder="企微公司"  size="small" @change="updateCorpId()">
             <el-option

+ 1 - 1
src/views/qw/friendWelcome/indexNew.vue

@@ -1307,7 +1307,7 @@ export default {
     handleAvatarSuccessVideo(res, file, content) {
       if (res.code == 200) {
         // 使用 $set 确保响应式更新
-        this.$set(content, 'videoUrl', "https://" + res.url);
+        this.$set(content, 'videoUrl', res.url);
       } else {
         this.msgError(res.msg);
       }

+ 1 - 1
src/views/qw/groupMsg/customerBaseMsg.vue

@@ -222,7 +222,7 @@
     </el-dialog>
 
     <!-- 选择群发账号弹窗   -->
-    <el-dialog :title="listUser.title" :visible.sync="listUser.open" style="width: 1600px;height: 100%" append-to-body>
+    <el-dialog :title="listUser.title" :visible.sync="listUser.open" width="1600px" append-to-body>
       <qwUserList ref="QwUserList" @selectUserList="selectUserList"></qwUserList>
     </el-dialog>
 

+ 2 - 2
src/views/qw/groupMsg/customerGroupDetails.vue

@@ -80,7 +80,8 @@
             </el-form>
 
             <el-table v-loading="loading" :data="groupMsgUserDetailsList">
-              <el-table-column label="成员" align="center" prop="nickName" />
+              <el-table-column label="销售" align="center" prop="nickName" />
+              <el-table-column label="成员企微昵称" align="center" prop="qwUserName" />
               <el-table-column label="成员发送状态" align="center" prop="status">
                 <template slot-scope="scope">
                   <dict-tag :options="groupMsgStatusOptions" :value="scope.row.status"/>
@@ -289,7 +290,6 @@ export default {
         params = this.groupMsgUserDetailsList// 如果 `groupMsgUserDetailsList` 是一个对象
       }
 
-      console.log("params",params)
       refreshResultsMsgUser(params).then(res=>{
         this.loading=false;
         if (res.code==200){

+ 2 - 3
src/views/qw/groupMsg/index.vue

@@ -450,8 +450,8 @@
                     fit="contain"
                     @click="openImageViewer(formDetails.miniprogramPicUrl)"/>
                 </el-descriptions-item>
-                <el-descriptions-item label="小程序appid:" v-if="formDetails.msgType=='3'">{{formDetails.miniprogramAppid}}</el-descriptions-item>
-                <el-descriptions-item label="小程序page路径:" v-if="formDetails.msgType=='3'">{{formDetails.miniprogramPage}}</el-descriptions-item>
+<!--                <el-descriptions-item label="小程序appid:" v-if="formDetails.msgType=='3'">{{formDetails.miniprogramAppid}}</el-descriptions-item>-->
+<!--                <el-descriptions-item label="小程序page路径:" v-if="formDetails.msgType=='3'">{{formDetails.miniprogramPage}}</el-descriptions-item>-->
                 <el-descriptions-item label="企微临时文件mediaId:" v-if="formDetails.msgType=='4'">{{formDetails.fileMediaId}}</el-descriptions-item>
                 <el-descriptions-item label="文件:" v-if="formDetails.msgType=='4'">
                   <el-link v-if="formDetails.fileUrl" type="primary" :href="formDetails.fileUrl" download>
@@ -1137,7 +1137,6 @@ export default {
     //选择群发的企业成员账号
     handlelistUser(){
 
-      console.log("imageArr",this.imageArr)
 
       this.listUser.title="选择企业成员"
       this.listUser.open=true;

+ 2 - 2
src/views/qw/groupMsgUser/groupMsg.vue

@@ -266,8 +266,8 @@
                 fit="contain"
                 @click="openImageViewer(form.miniprogramPicUrl)"/>
             </el-descriptions-item>
-            <el-descriptions-item label="小程序appid:" v-if="form.msgType=='3'">{{form.miniprogramAppid}}</el-descriptions-item>
-            <el-descriptions-item label="小程序page路径:" v-if="form.msgType=='3'">{{form.miniprogramPage}}</el-descriptions-item>
+<!--            <el-descriptions-item label="小程序appid:" v-if="form.msgType=='3'">{{form.miniprogramAppid}}</el-descriptions-item>-->
+<!--            <el-descriptions-item label="小程序page路径:" v-if="form.msgType=='3'">{{form.miniprogramPage}}</el-descriptions-item>-->
             <el-descriptions-item label="企微临时文件mediaId:" v-if="form.msgType=='4'">{{form.fileMediaId}}</el-descriptions-item>
             <el-descriptions-item label="文件:" v-if="form.msgType=='4'">
               <el-link v-if="form.fileUrl" type="primary" :href="form.fileUrl" download>

+ 2 - 2
src/views/qw/material/ImageUpload.vue

@@ -46,8 +46,8 @@ export default {
     },
     // 大小限制(MB)
     fileSize: {
-       type: Number,
-      default: 10,
+        type: Number,
+        default: 10,
     },
     // 文件类型, 例如['png', 'jpg', 'jpeg']
     fileType: {

+ 153 - 130
src/views/qw/material/index.vue

@@ -31,19 +31,19 @@
           @keyup.enter.native="handleQuery"
         />
       </el-form-item>
-      <el-form-item label="发送次数" prop="sendCount">
-        <el-input
-          v-model="queryParams.sendCount"
-          placeholder="请输入发送次数"
-          clearable
-          size="small"
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
+<!--      <el-form-item label="发送次数" prop="sendCount">-->
+<!--        <el-input-->
+<!--          v-model="queryParams.sendCount"-->
+<!--          placeholder="请输入发送次数"-->
+<!--          clearable-->
+<!--          size="small"-->
+<!--          @keyup.enter.native="handleQuery"-->
+<!--        />-->
+<!--      </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-button type="primary"  size="mini" @click="handleAdd()">添加素材</el-button>
+        <el-button type="primary"  size="mini" @click="handleAdd()" v-hasPermi="['qw:material:add']">添加素材</el-button>
       </el-form-item>
     </el-form>
 
@@ -102,14 +102,14 @@
                     </video>
                     <div v-else-if="scope.row.materialType == 'file'" class="file-link-container" style="width: 100%">
                       <el-link type="primary" :href="downloadUrl(scope.row.materialUrl)" download>
-                        下载文件
+                         {{scope.row.materialUrl}}
                       </el-link>
                     </div>
                   </template>
                 </el-table-column>
                 <el-table-column label="标题" align="center"  prop="title"/>
                 <el-table-column label="创建人" align="center"  prop="createName"/>
-                <el-table-column label="发送次数" align="center"  prop="sendCount"/>
+<!--                <el-table-column label="发送次数" align="center"  prop="sendCount"/>-->
                 <el-table-column label="隶属分组" align="center"  prop="materialGroupName"></el-table-column>
                 <el-table-column label="创建时间" align="center"  prop="createTime"/>
                 <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
@@ -119,14 +119,14 @@
                       type="text"
                       icon="el-icon-edit"
                       @click="handleUpdate(scope.row)"
-                      v-hasPermi="['qw:tag:edit']"
+                      v-hasPermi="['qw:material:edit']"
                     >修改</el-button>
                     <el-button
                       size="mini"
                       type="text"
                       icon="el-icon-delete"
                       @click="handleDelete(scope.row)"
-                      v-hasPermi="['qw:tag:remove']"
+                      v-hasPermi="['qw:material:remove']"
                     >删除</el-button>
                   </template>
                 </el-table-column>
@@ -146,7 +146,7 @@
                   </el-table-column>
                   <el-table-column label="标题" align="center"  prop="title"/>
                   <el-table-column label="创建人" align="center"  prop="createName"/>
-                  <el-table-column label="发送次数" align="center"  prop="sendCount"/>
+<!--                  <el-table-column label="发送次数" align="center"  prop="sendCount"/>-->
                   <el-table-column label="隶属分组" align="center"  prop="materialGroupName"></el-table-column>
                   <el-table-column label="创建时间" align="center"  prop="createTime"/>
                   <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
@@ -156,14 +156,14 @@
                         type="text"
                         icon="el-icon-edit"
                         @click="handleUpdate(scope.row)"
-                        v-hasPermi="['qw:tag:edit']"
+                        v-hasPermi="['qw:material:edit']"
                       >修改</el-button>
                       <el-button
                         size="mini"
                         type="text"
                         icon="el-icon-delete"
                         @click="handleDelete(scope.row)"
-                        v-hasPermi="['qw:tag:remove']"
+                        v-hasPermi="['qw:material:remove']"
                       >删除</el-button>
                     </template>
                   </el-table-column>
@@ -199,7 +199,7 @@
                           type="text"
                           icon="el-icon-edit"
                           @click="handleUpdate(item)"
-                          v-hasPermi="['qw:tag:edit']"
+                          v-hasPermi="['qw:material:edit']"
                         >修改</el-button>
                         <el-button
                           class="button"
@@ -207,101 +207,102 @@
                           type="text"
                           icon="el-icon-delete"
                           @click="handleDelete(item)"
-                          v-hasPermi="['qw:tag:remove']"
+                          v-hasPermi="['qw:material:remove']"
                         >删除</el-button>
                       </div>
                     </div>
                   </el-card>
                 </div>
             </el-tab-pane>
-            <el-tab-pane label="图文" name="4">
-              <div style="display: flex;  flex-wrap: wrap; gap: 10px; ">
-                <el-card
-                  v-for="(item, index) in imagetextMaterialsList"
-                  :key="index">
-                  <el-image
-                    v-if="item.titleUrl"
-                    style="width: 200px; height: 180px"
-                    :src="item.titleUrl"
-                    fit="contain"
-                    @click="openImageViewer(item.titleUrl)"
-                  />
-                  <div v-else style="width: 200px; height: 180px; display: flex; justify-content: center; align-items: center; border: 1px solid #dcdcdc;">
-                    暂无标题图片
-                  </div>
-                  <div style="margin-top: 3%">
-                    <div style="  display: flex;flex-direction: column;width: 200px">
-                      <span>标题:{{ item.title }}</span>
-                      <div style="display: flex; flex-wrap: wrap; word-break: break-all;margin:3% 0">
-                        <span>链接:</span>
-                        <el-link :href="item.materialUrl" target="_blank">{{ item.materialUrl }}</el-link>
-                      </div>
-                      <span>创建人:{{item.createName}}</span>
-                      <time class="time" style="white-space: nowrap">时间:{{ item.createTime }}</time>
-                    </div>
-                    <div class="bottom clearfix">
-                      <el-button
-                        class="button"
-                        size="mini"
-                        type="text"
-                        icon="el-icon-edit"
-                        @click="handleUpdate(item)"
-                        v-hasPermi="['qw:tag:edit']"
-                      >修改</el-button>
-                      <el-button
-                        class="button"
-                        size="mini"
-                        type="text"
-                        icon="el-icon-delete"
-                        @click="handleDelete(item)"
-                        v-hasPermi="['qw:tag:remove']"
-                      >删除</el-button>
-                    </div>
-                  </div>
-                </el-card>
-              </div>
-            </el-tab-pane>
-            <el-tab-pane label="语音" name="5">
-              <div style="display: flex;  flex-wrap: wrap; gap: 10px; ">
-                <el-card
-                  v-for="(item, index) in voiceMaterialsList"
-                  :key="index">
-                  <!--                    style="width: 250px;height: 300px">-->
-                  <!--                    <div class="card-header">-->
-                  <!--                      <el-checkbox-->
-                  <!--                        :label="item.id"-->
-                  <!--                        @change="handleSelectionChange($event, item)"-->
-                  <!--                      ></el-checkbox>-->
-                  <!--                    </div>-->
-                  <audio  controls :src="item.materialUrl" style="width: 300px;height: 50px"/>
-                  <div>
-                    <div style="  display: flex;flex-direction: column;width: 200px">
-                      <span>标题:{{ item.title }}</span>
-                      <span>创建人:{{item.createName}}</span>
-                      <time class="time" style="white-space: nowrap">时间:{{ item.createTime }}</time>
-                    </div>
-                    <div class="bottom clearfix">
-                      <el-button
-                        class="button"
-                        size="mini"
-                        type="text"
-                        icon="el-icon-edit"
-                        @click="handleUpdate(item)"
-                        v-hasPermi="['qw:tag:edit']"
-                      >修改</el-button>
-                      <el-button
-                        class="button"
-                        size="mini"
-                        type="text"
-                        icon="el-icon-delete"
-                        @click="handleDelete(item)"
-                        v-hasPermi="['qw:tag:remove']"
-                      >删除</el-button>
-                    </div>
-                  </div>
-                </el-card>
-              </div>
-            </el-tab-pane>
+
+<!--            <el-tab-pane label="图文" name="4">-->
+<!--              <div style="display: flex;  flex-wrap: wrap; gap: 10px; ">-->
+<!--                <el-card-->
+<!--                  v-for="(item, index) in imagetextMaterialsList"-->
+<!--                  :key="index">-->
+<!--                  <el-image-->
+<!--                    v-if="item.titleUrl"-->
+<!--                    style="width: 200px; height: 180px"-->
+<!--                    :src="item.titleUrl"-->
+<!--                    fit="contain"-->
+<!--                    @click="openImageViewer(item.titleUrl)"-->
+<!--                  />-->
+<!--                  <div v-else style="width: 200px; height: 180px; display: flex; justify-content: center; align-items: center; border: 1px solid #dcdcdc;">-->
+<!--                    暂无标题图片-->
+<!--                  </div>-->
+<!--                  <div style="margin-top: 3%">-->
+<!--                    <div style="  display: flex;flex-direction: column;width: 200px">-->
+<!--                      <span>标题:{{ item.title }}</span>-->
+<!--                      <div style="display: flex; flex-wrap: wrap; word-break: break-all;margin:3% 0">-->
+<!--                        <span>链接:</span>-->
+<!--                        <el-link :href="item.materialUrl" target="_blank">{{ item.materialUrl }}</el-link>-->
+<!--                      </div>-->
+<!--                      <span>创建人:{{item.createName}}</span>-->
+<!--                      <time class="time" style="white-space: nowrap">时间:{{ item.createTime }}</time>-->
+<!--                    </div>-->
+<!--                    <div class="bottom clearfix">-->
+<!--                      <el-button-->
+<!--                        class="button"-->
+<!--                        size="mini"-->
+<!--                        type="text"-->
+<!--                        icon="el-icon-edit"-->
+<!--                        @click="handleUpdate(item)"-->
+<!--                        v-hasPermi="['qw:material:edit']"-->
+<!--                      >修改</el-button>-->
+<!--                      <el-button-->
+<!--                        class="button"-->
+<!--                        size="mini"-->
+<!--                        type="text"-->
+<!--                        icon="el-icon-delete"-->
+<!--                        @click="handleDelete(item)"-->
+<!--                        v-hasPermi="['qw:material:remove']"-->
+<!--                      >删除</el-button>-->
+<!--                    </div>-->
+<!--                  </div>-->
+<!--                </el-card>-->
+<!--              </div>-->
+<!--            </el-tab-pane>-->
+<!--            <el-tab-pane label="语音" name="5">-->
+<!--              <div style="display: flex;  flex-wrap: wrap; gap: 10px; ">-->
+<!--                <el-card-->
+<!--                  v-for="(item, index) in voiceMaterialsList"-->
+<!--                  :key="index">-->
+<!--                  &lt;!&ndash;                    style="width: 250px;height: 300px">&ndash;&gt;-->
+<!--                  &lt;!&ndash;                    <div class="card-header">&ndash;&gt;-->
+<!--                  &lt;!&ndash;                      <el-checkbox&ndash;&gt;-->
+<!--                  &lt;!&ndash;                        :label="item.id"&ndash;&gt;-->
+<!--                  &lt;!&ndash;                        @change="handleSelectionChange($event, item)"&ndash;&gt;-->
+<!--                  &lt;!&ndash;                      ></el-checkbox>&ndash;&gt;-->
+<!--                  &lt;!&ndash;                    </div>&ndash;&gt;-->
+<!--                  <audio  controls :src="item.materialUrl" style="width: 300px;height: 50px"/>-->
+<!--                  <div>-->
+<!--                    <div style="  display: flex;flex-direction: column;width: 200px">-->
+<!--                      <span>标题:{{ item.title }}</span>-->
+<!--                      <span>创建人:{{item.createName}}</span>-->
+<!--                      <time class="time" style="white-space: nowrap">时间:{{ item.createTime }}</time>-->
+<!--                    </div>-->
+<!--                    <div class="bottom clearfix">-->
+<!--                      <el-button-->
+<!--                        class="button"-->
+<!--                        size="mini"-->
+<!--                        type="text"-->
+<!--                        icon="el-icon-edit"-->
+<!--                        @click="handleUpdate(item)"-->
+<!--                        v-hasPermi="['qw:material:edit']"-->
+<!--                      >修改</el-button>-->
+<!--                      <el-button-->
+<!--                        class="button"-->
+<!--                        size="mini"-->
+<!--                        type="text"-->
+<!--                        icon="el-icon-delete"-->
+<!--                        @click="handleDelete(item)"-->
+<!--                        v-hasPermi="['qw:material:remove']"-->
+<!--                      >删除</el-button>-->
+<!--                    </div>-->
+<!--                  </div>-->
+<!--                </el-card>-->
+<!--              </div>-->
+<!--            </el-tab-pane>-->
             <el-tab-pane label="视频" name="6">
               <div style="display: flex;  flex-wrap: wrap; gap: 10px; ">
                 <el-card
@@ -331,7 +332,7 @@
                         type="text"
                         icon="el-icon-edit"
                         @click="handleUpdate(item)"
-                        v-hasPermi="['qw:tag:edit']"
+                        v-hasPermi="['qw:material:edit']"
                       >修改</el-button>
                       <el-button
                         class="button"
@@ -339,7 +340,7 @@
                         type="text"
                         icon="el-icon-delete"
                         @click="handleDelete(item)"
-                        v-hasPermi="['qw:tag:remove']"
+                        v-hasPermi="['qw:material:remove']"
                       >删除</el-button>
                     </div>
                   </div>
@@ -359,7 +360,7 @@
                   <!--                      ></el-checkbox>-->
                   <!--                    </div>-->
                   <el-link type="primary" :href="downloadUrl(item.materialUrl)" download>
-                    下载文件
+                    {{item.materialUrl}}
                   </el-link>
                   <div>
                     <div style="  display: flex;flex-direction: column;width: 200px">
@@ -374,7 +375,7 @@
                         type="text"
                         icon="el-icon-edit"
                         @click="handleUpdate(item)"
-                        v-hasPermi="['qw:tag:edit']"
+                        v-hasPermi="['qw:material:edit']"
                       >修改</el-button>
                       <el-button
                         class="button"
@@ -382,7 +383,7 @@
                         type="text"
                         icon="el-icon-delete"
                         @click="handleDelete(item)"
-                        v-hasPermi="['qw:tag:remove']"
+                        v-hasPermi="['qw:material:remove']"
                       >删除</el-button>
                     </div>
                   </div>
@@ -444,7 +445,7 @@
         </div>
         <div v-else-if="form.materialType=='image'">
           <el-form-item label="上传图片" prop="materialUrl">
-            <ImageUpload v-model="form.materialUrl" type="image" :num="10" :width="150" :height="150" />
+            <ImageUpload v-model="form.materialUrl" type="image" :num="1" :width="150" :height="150" />
           </el-form-item>
         </div>
         <div v-else-if="form.materialType=='imagetext'">
@@ -486,7 +487,7 @@
             </el-upload>
             <video v-if="form.materialUrl"
               :src="form.materialUrl"
-              controls style="width: 200px;height: 100px">
+              controls style="width: 300px;height: 200px">
             </video>
           </el-form-item>
         </div>
@@ -502,7 +503,7 @@
               <i class="el-icon-plus avatar-uploader-icon"></i>
             </el-upload>
             <el-link v-if="form.materialUrl" type="primary" :href="downloadUrl(form.materialUrl)" download>
-              下载文件
+              {{ form.materialUrl }}
             </el-link>
           </el-form-item>
         </div>
@@ -530,7 +531,7 @@ export default {
       dialogVisible: false,
       dialogImageUrl:null,
       //上传地址
-      uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
+      uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS2",
       myQwCompanyList:[],
       // 遮罩层
       loading: true,
@@ -581,7 +582,6 @@ export default {
         title: null,
         createName: null,
         sendCount: null,
-        corpId: null,
         groupType:"1",
       },
 
@@ -695,14 +695,14 @@ export default {
       }
     },
     beforeAvatarUploadVoice(file){
-      const isLt1M = file.size / 1024 / 1024 < 1;
+      const isLt1M = file.size / 1024 / 1024 < 2;
       if (!isLt1M) {
         this.$message.error('上传大小不能超过 2MB!');
       }
       return isLt1M;
     },
     beforeAvatarUploadVideo(file){
-      const isLt1M = file.size / 1024 / 1024 < 1;
+      const isLt1M = file.size / 1024 / 1024 < 10;
       if (!isLt1M) {
         this.$message.error('上传大小不能超过 10MB!');
       }
@@ -710,7 +710,7 @@ export default {
     },
 
     beforeAvatarUploadFile(file){
-      const isLt1M = file.size / 1024 / 1024 < 1;
+      const isLt1M = file.size / 1024 / 1024 < 20;
       if (!isLt1M) {
         this.$message.error('上传大小不能超过 20MB!');
       }
@@ -719,27 +719,28 @@ export default {
 
     //选择小板块方法
     handleClick(row){
-      switch (row.index) {
-        case '0':
+
+      switch (row.name) {
+        case '1':
           this.resetParam();
           this.getList();
           break;
-        case '1':
+        case '2':
           this.textMaterials();
           break;
-        case '2':
+        case '3':
           this.imageMaterials();
           break;
-        case '3':
+        case '4':
           this.imagetextMaterials()
           break;
-        case '4':
+        case '5':
           this.voiceMaterials();
           break;
-        case '5':
+        case '6':
           this.videoMaterials()
           break;
-        case '6':
+        case '7':
           this.fileMaterials()
           break;
         default:
@@ -865,16 +866,38 @@ export default {
         this.form.corpId=this.queryParams.corpId;
         if (valid) {
           if (this.form.materialId != null) {
+
+            this.open = false;
+            let loadingRock = this.$loading({
+              lock: true,
+              text: '正在执行中请稍后~!',
+              spinner: 'el-icon-loading',
+              background: 'rgba(0, 0, 0, 0.7)'
+            });
+
             updateMaterial(this.form).then(response => {
               this.msgSuccess("修改成功");
-              this.open = false;
               this.getList();
+            }).finally(res=>{
+              loadingRock.close();
             });
+
           } else {
+
+            this.open = false;
+            let loadingRock = this.$loading({
+              lock: true,
+              text: '正在执行中请稍后~~',
+              spinner: 'el-icon-loading',
+              background: 'rgba(0, 0, 0, 0.7)'
+            });
+
             addMaterial(this.form).then(response => {
               this.msgSuccess("新增成功");
-              this.open = false;
               this.getList();
+              loadingRock.close();
+            }).finally(res=>{
+              loadingRock.close();
             });
           }
         }

+ 64 - 13
src/views/qw/sopTemp/index.vue

@@ -248,7 +248,16 @@
         <el-form-item label="排序" prop="sort">
           <el-input-number v-model="form.sort" :min="0" label="排序"></el-input-number>
         </el-form-item>
-
+        <el-form-item label="是否开启官方群发" v-if="form.sendType == 11 && (form.id === null || form.id === undefined)">
+          <el-radio-group v-model="form.openOfficial">
+            <el-radio
+              v-for="dict in openOfficialOptions"
+              :key="dict.dictValue"
+              :label="dict.dictValue"
+            >{{ dict.dictLabel }}
+            </el-radio>
+          </el-radio-group>
+        </el-form-item>
         <el-form-item label="内容" prop="modeContent">
           <el-input v-model="form.modeContent" placeholder="请输入文字内容"  type="textarea" :rows="3"/>
         </el-form-item>
@@ -266,16 +275,34 @@
           <el-input-number v-model="form.num" :min="1" label="每天催课次数" @change="sendNumChange"></el-input-number>
         </el-form-item>
         <el-form-item label="催课时间" v-if="form.sendType == 11 && !form.id">
-          <el-time-picker
-            v-for="item in form.timeList"
-            class="custom-input"
-            v-model="item.value"
-            value-format="HH:mm"
-            format="HH:mm"
-            :picker-options="{ selectableRange: startTimeRange }"
-            placeholder="时间"
-            style="width: 200px;height: 20px;margin-left: 10px;margin-top: 10px">
-          </el-time-picker>
+<!--          <el-time-picker-->
+<!--            v-for="item in form.timeList"-->
+<!--            class="custom-input"-->
+<!--            v-model="item.value"-->
+<!--            value-format="HH:mm"-->
+<!--            format="HH:mm"-->
+<!--            :picker-options="{ selectableRange: startTimeRange }"-->
+<!--            placeholder="时间"-->
+<!--            style="width: 200px;height: 20px;margin-left: 10px;margin-top: 10px">-->
+<!--          </el-time-picker>-->
+          <div v-for="(item, index) in form.timeList" :key="index" style="margin-bottom: 10px;">
+            <el-time-picker
+              class="custom-input"
+              v-model="item.value"
+              value-format="HH:mm"
+              format="HH:mm"
+              :picker-options="{ selectableRange: startTimeRange }"
+              placeholder="时间"
+              style="width: 150px; height: 20px; margin-left: 10px; margin-top: 10px">
+            </el-time-picker>
+
+            <el-input
+              v-model="item.desc"
+              placeholder="催课内容"
+              type="textarea" :rows="2"
+              style="width: 500px; margin-left: 10px; margin-top: 10px;">
+            </el-input>
+          </div>
         </el-form-item>
       </el-form>
       <div slot="footer" class="dialog-footer" style="display: flex;justify-content: flex-end;">
@@ -417,6 +444,7 @@ export default {
       command: 0,
       // 状态字典
       statusOptions: [],
+      openOfficialOptions: [],
 
       shareOptions: {
         title: '分享模板',
@@ -487,6 +515,10 @@ export default {
       this.statusOptions = response.data;
     });
 
+    this.getDicts("sys_company_or").then(response => {
+      this.openOfficialOptions = response.data;
+    });
+
     getSelectableRange().then(e => {
       this.startTimeRange = e.data;
     })
@@ -547,9 +579,10 @@ export default {
         gap: 1,
         sendType: this.sendType,
         sort: 0,
+        openOfficial: "1",
         time: "",
         num: 1,
-        timeList: [{value: ""}],
+        timeList: [{value: "",desc:""}],
         status: "1",
       };
       this.resetForm("form");
@@ -675,10 +708,28 @@ export default {
       delete this.form.rules
       this.$refs["form"].validate(valid => {
         if (valid) {
+
+          if (this.command != 2 && this.form.id == null && this.form.sendType == 11){
+
+            const hasEmptyFields = this.form.timeList.some(item => {
+              return !item.value || !item.desc;
+            });
+
+            if (hasEmptyFields) {
+              this.$message.error("请填写【催课时间】和【催课内容】!");
+              return; // 阻止提交
+            }
+
+          }
+
+
+
           let f = JSON.parse(JSON.stringify(this.form));
           if (f.timeList && f.timeList.length > 0) {
+            f.timeDesc = f.timeList.map(item => item.desc);
             f.timeList = f.timeList.map(item => item.value);
           }
+
           const loading = this.$loading({
             lock: true,
             text: 'Loading',
@@ -763,7 +814,7 @@ export default {
     sendNumChange(val, old) {
       if (val > old) {
         for (let i = 0; i < val - old; i++) {
-          this.form.timeList.push({value: ""});
+          this.form.timeList.push({value: "",desc:""});
         }
       } else {
         let len = old - val;

+ 6 - 1
src/views/qw/sopTemp/updateSopTemp.vue

@@ -1196,9 +1196,11 @@ export default {
 
     },
     addSetList(contentIndex, content) {
-
       if (this.form.sendType == 1) {
         for (let i = 0; i < content.length; i++) {
+          if (!content[i].setting) {
+            content[i].setting = []; // 初始化 setting 数组如果不存在
+          }
 
           if (content[i].setting.length > 9) {
             return this.$message.error("因为企微接口限制,企微模板一条消息只能设置最多~9个内容!!")
@@ -1215,6 +1217,9 @@ export default {
 
         }
       } else {
+        if (!content[contentIndex].setting) {
+          content[contentIndex].setting = []; // 初始化 setting 数组如果不存在
+        }
 
         const newSetting = {
           contentType: '1',

+ 37 - 5
src/views/qw/sopUserLogsInfo/sopUserLogsInfoDetails.vue

@@ -48,12 +48,26 @@
           @keyup.enter.native="handleQuery"
         />
       </el-form-item>
+<!--      <el-form-item label="进线时间" prop="entryTime" v-if="queryParams.filterMode == 1">-->
+<!--        <el-date-picker clearable size="small"-->
+<!--                        v-model="queryParams.entryTime"-->
+<!--                        type="date"-->
+<!--                        value-format="yyyy-MM-dd"-->
+<!--                        placeholder="选择营期时间">-->
+<!--        </el-date-picker>-->
+<!--      </el-form-item>-->
+
       <el-form-item label="进线时间" prop="entryTime" v-if="queryParams.filterMode == 1">
-        <el-date-picker clearable size="small"
-                        v-model="queryParams.entryTime"
-                        type="date"
-                        value-format="yyyy-MM-dd"
-                        placeholder="选择营期时间">
+        <el-date-picker
+          v-model="scheduleEntryTime"
+          type="datetimerange"
+          size="small"
+          style="width: 350px"
+          value-format="yyyy-MM-dd HH:mm:ss"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          @change="handleScheduleTimeChange">
         </el-date-picker>
       </el-form-item>
       <el-form-item label="标签" prop="tagIds">
@@ -525,6 +539,9 @@ export default {
       open: false,
       updateOpen:false,
       // 查询参数
+
+      scheduleEntryTime:null,
+
       queryParams: {
         pageNum: 1,
         pageSize: 10,
@@ -542,6 +559,8 @@ export default {
         externalUserName: null,
         createTime: null,
         entryTime: null,
+        inComingSTime: null,
+        inComingETime: null,
       },
 
       tagGroupList: [],
@@ -1099,6 +1118,16 @@ export default {
       this.resetForm("msgForm");
     },
 
+    handleScheduleTimeChange(val) {
+      if (val) {
+        this.queryParams.inComingSTime = val[0];
+        this.queryParams.inComingETime = val[1];
+      } else {
+        this.queryParams.inComingSTime = null;
+        this.queryParams.inComingETime = null;
+      }
+    },
+
     /** 搜索按钮操作 */
     handleQuery() {
 
@@ -1127,6 +1156,9 @@ export default {
     /** 重置按钮操作 */
     resetQuery() {
       this.selectTags=[];
+      this.scheduleEntryTime=null;
+      this.queryParams.inComingSTime=null;
+      this.queryParams.inComingETime=null;
       this.resetForm("queryForm");
       this.handleQuery();
     },

+ 128 - 12
src/views/qw/welcome/welComeAdd.vue

@@ -40,18 +40,43 @@
           </div>
 
           <div v-if="form.msgType=='3'">
-            <el-form-item label="小程序标题:" prop="miniprogramTitle">
-              <el-input v-model="form.miniprogramTitle" placeholder="请输入小程序消息标题,最长为21字"  />
-            </el-form-item>
-            <el-form-item label="上传小程序封面:" prop="miniprogramPicUrl">
-              <ImageUpload v-model="form.miniprogramPicUrl"  type="image" :num="10" :width="150" :height="150" />
-            </el-form-item>
-            <el-form-item label="小程序appid:" prop="miniprogramAppid">
-              <el-input v-model="form.miniprogramAppid" placeholder="请输入小程序appid,必须是关联到企业的小程序应用" />
-            </el-form-item>
-            <el-form-item label="小程序page路径:" prop="miniprogramPage">
-              <el-input v-model="form.miniprogramPage" placeholder="请输入小程序page路径" />
+
+            <el-form-item label="选择课程"  prop="videoId" >
+              <el-select  v-model="form.courseId" placeholder="请选择课程" style=" margin-right: 10px;" size="mini"  @change="courseChange(form)">
+                <el-option
+                  v-for="dict in courseList"
+                  :key="dict.dictValue"
+                  :label="dict.dictLabel"
+                  :value="parseInt(dict.dictValue)"
+                />
+              </el-select>
+              <el-select  v-model="form.videoId" placeholder="请选择小节" size="mini" style=" margin-right: 10px;" @change="videoIdChange(form)" >
+                <el-option
+                  v-for="dict in videoList"
+                  :key="dict.dictValue"
+                  :label="dict.dictLabel"
+                  :value="parseInt(dict.dictValue)"
+                />
+              </el-select>
             </el-form-item>
+
+            <div v-if="form.videoId!=null">
+              <el-form-item label="小程序标题:" prop="miniprogramTitle">
+                <el-input v-model="form.miniprogramTitle" :rows="2" maxlength="64" placeholder="请输入小程序消息标题,最长为64字节" @input="checkByteLength(form)" />
+              </el-form-item>
+              <el-form-item label="小程序封面:" prop="miniprogramPicUrl">
+                <img :src="form.miniprogramPicUrl" width="150px" height="150px">
+              </el-form-item>
+              <div v-if="form.videoId!=null">
+                <el-form-item label="小程序链接:"  label-width="100px" >
+                  <el-tag type="warning" v-model="form.miniprogramPage='待生成'">选择的课程小节 即为卡片小程序链接地址</el-tag>
+                </el-form-item>
+              </div>
+              <el-form-item label="appid" prop="miniprogramAppid" v-show="false" >
+                <el-input v-model="form.miniprogramAppid" disabled />
+              </el-form-item>
+            </div>
+
           </div>
 
           <div v-if="form.msgType=='4'">
@@ -97,7 +122,7 @@
               </el-radio-group>
             </el-form-item>
 
-          <el-form-item label="是否定时发送" prop="isTimerSend">
+          <el-form-item label="是否定时发送" prop="isTimerSend" v-show="form.msgType!='3'">
             <el-radio-group v-model="form.isTimerSend" size="medium">
               <el-radio v-for="dict in allowSelectOptions" :label="dict.dictValue">{{dict.dictLabel}}</el-radio>
             </el-radio-group>
@@ -221,6 +246,7 @@
 <script>
 import { listWelcome, getWelcome, delWelcome, addWelcome, updateWelcome, exportWelcome } from "@/api/qw/welcome";
 import ImageUpload from "@/views/qw/material/ImageUpload";
+import {courseList, videoList} from "../../../api/qw/sop";
 
 export default {
   name: "Welcome",
@@ -228,6 +254,10 @@ export default {
 
   data() {
     return {
+
+      courseList:[],
+      videoList:[],
+
       //上传地址
       uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
       //放大图片
@@ -277,7 +307,11 @@ export default {
         ],
         linkUrl:[
           {required:true,message:"图文链接地址不能为空",trigger:"blur"}
+        ],
+        videoId: [
+          { required: true, message: '请选择小节', trigger: 'blur' }
         ]
+
       }
     };
   },
@@ -285,9 +319,14 @@ export default {
 
     //文本类型字典
     this.getDicts("sys_qw_welcome_type").then(response => {
+      console.log("response",response)
       this.groupMsgTextTypeOptions = response.data;
     });
 
+    courseList().then(response => {
+      this.courseList = response.list;
+    });
+
     // //全部客户还是部分客户
     // this.getDicts("sys_qw_groupMsg_type").then(response => {
     //   this.groupMsgTypeOptions = response.data;
@@ -315,6 +354,83 @@ export default {
   },
   methods: {
 
+
+    courseChange(form){
+
+      // 清空 videoId 选择
+      this.$set(form, 'videoId', null);
+      // 清空 videoList
+      this.videoList = [];
+
+      if (form.courseId != null) {
+        // 查找选中的课程对应的 label 和 dictImgUrl
+        const selectedCourse = this.courseList.find(course => parseInt(course.dictValue) === form.courseId);
+
+        if (selectedCourse) {
+          // 设置 linkTitle 和 linkImageUrl
+          this.$set(form, 'miniprogramPicUrl', selectedCourse.dictImgUrl);
+        }
+
+
+        // 获取新的 videoList
+        videoList(form.courseId).then(response => {
+          this.videoList = response.list;
+        });
+
+      }
+    },
+    videoIdChange(form){
+      //选择了课程小节则 默认绑上
+      if (form.videoId != null) {
+        // 根据 videoId 获取相关信息(假设有相关的 API 调用)
+        let  selectedVideo = this.videoList.find(course => parseInt(course.dictValue) === form.videoId);
+        console.log("selectedVideo",selectedVideo)
+        if (selectedVideo && form.msgType==='3' ) {
+          this.$set(form, 'miniprogramTitle', this.truncateTextByByteLength(selectedVideo.dictLabel,64));
+          this.$set(form, 'expiresDays', 30);
+        }
+
+      }
+
+    },
+
+     // 检查字节长度
+    checkByteLength(form) {
+      const text = form.miniprogramTitle;
+      const byteLength = this.getByteLength(text); // 获取当前字节数
+
+      // 如果字节数超过64,截断输入内容
+      if (byteLength > 64) {
+        this.$set(form, 'miniprogramTitle', this.truncateTextByByteLength(text,64));
+      }
+    },
+
+    // 计算字符串的字节数
+    getByteLength(text) {
+      return new Blob([text]).size; // 使用 Blob 计算字节数
+    },
+
+    // 根据字节数截断字符串
+    truncateTextByByteLength(text, maxByteLength) {
+      let byteLength = 0;
+      let result = "";
+
+      for (let i = 0; i < text.length; i++) {
+        const char = text[i];
+        const charByteLength = this.getByteLength(char); // 获取当前字符的字节数
+
+        // 如果加上当前字符的字节数后不超过限制,则添加到结果中
+        if (byteLength + charByteLength <= maxByteLength) {
+          result += char;
+          byteLength += charByteLength;
+        } else {
+          break; // 超过限制时停止
+        }
+      }
+
+      return result;
+    },
+
     /** 表单验证 */
     async validateChildForm() {
       return new Promise((resolve, reject) => {

+ 3 - 3
src/views/store/components/inquiryOrderReportDetails.vue

@@ -24,10 +24,10 @@
           <el-descriptions-item label="首次处理时间"><span v-if="item!=null">{{item.updateTime}}</span></el-descriptions-item>
           <el-descriptions-item label="咨询备注"><span v-if="item!=null">{{item.companyUserRemark}}</span></el-descriptions-item>
           <el-descriptions-item label="疾病类型" ><dict-tag :options="diseaseTypeOptions" :value="item.diseaseType"/></el-descriptions-item>
-        </el-descriptions>      
+        </el-descriptions>
   </div>
   <div class="contentx" v-if="item!=null">
-        <div class="desct" > 
+        <div class="desct" >
           <span v-if="patientInfo">
             {{ patientInfo }}
           </span>
@@ -88,7 +88,7 @@
 </template>
 
 <script>
-import { listBySearch,listInquiryOrderReport, getInquiryOrderReport, delInquiryOrderReport, addInquiryOrderReport, updateInquiryOrderReport, exportInquiryOrderReport ,getPatientMobile} from "@/api/store/inquiryOrderReport";
+import { listBySearch, getInquiryOrderReport,getPatientMobile} from "@/api/store/inquiryOrderReport";
 
   export default {
     name: "inquiryOrderReport",

+ 460 - 0
src/views/store/components/userBehavior.vue

@@ -0,0 +1,460 @@
+<template>
+  <div class="behavior-track-container">
+    <!-- 筛选区域 -->
+    <div class="filter-section">
+      <div class="filter-item">
+        <label>操作类型:</label>
+        <el-select
+          v-model="queryParams.operationType"
+          class="type-select"
+          @change="handleTypeChange"
+          placeholder="请选择行为类型"
+          clearable
+        >
+          <el-option
+            v-for="item in typeList"
+            :key="item"
+            :label="item"
+            :value="item"
+          ></el-option>
+        </el-select>
+      </div>
+    </div>
+    <div v-if="steps.length === 0" class="no-data">
+      <p>暂无数据</p>
+    </div>
+    <!-- 按日期分组展示(分页后的数据) -->
+    <div v-if="steps.length != 0" v-for="(records, date) in groupedPaginatedSteps" :key="date" class="date-group">
+      <!-- 日期标题(如果是今天,显示“今天”,否则显示具体日期) -->
+      <div class="date-title">
+        {{ isToday(date) ? '今天' : date }}
+      </div>
+      <div class="date-divider"></div>
+
+      <!-- 当前日期下的记录 -->
+      <el-steps
+        direction="vertical"
+        :space="120"
+        process-status="process"
+        finish-status="success"
+        class="custom-steps"
+      >
+        <el-step
+          v-for="(step, index) in records"
+          :key="index"
+          :icon="index === 0 ? 'el-icon-s-help' : 'el-icon-bangzhu'"
+        >
+          <template #title>
+            <div class="step-title">
+              {{ step.operationType }}
+              <span class="step-time">{{ step.createTime }}</span> <!-- 每个步骤的时间 -->
+            </div>
+          </template>
+          <template #description>
+            <!-- 答题 -->
+            <div v-if="step.operationType == '答题'" class="step-content">
+              <!-- 第一行 -->
+              <div class="step-row first-row">
+                <div v-if="step.paramVo" class="detail-item">
+                  <span class="detail-label"><i class="el-icon-notebook-2"></i> 课节名称:</span>
+                  <span class="detail-value">{{ step.paramVo.courseName }}</span>
+                </div>
+              </div>
+
+              <!-- 第二行 -->
+              <div v-if="step.details" class="step-row second-row">
+                <div class="detail-item">
+                  <span class="detail-label"><i class="el-icon-chat-dot-round"></i> 备注:</span>
+                  <span class="detail-value">{{ step.details }}</span>
+                </div>
+              </div>
+            </div>
+
+            <!-- 发放奖励 -->
+            <div v-else-if="step.operationType == '发送奖励'" class="step-content">
+              <!-- 第一行 -->
+              <div class="step-row first-row">
+                <div v-if="step.paramVo" class="detail-item">
+                  <span class="detail-label"><i class="el-icon-gift"></i> 课程名称:</span>
+                  <span class="detail-value">{{ step.paramVo.courseName }}</span>
+                </div>
+                <div v-if="step.paramVo" class="detail-item">
+                  <span class="detail-label"><i class="el-icon-coin"></i> 小节名称:</span>
+                  <span class="detail-value">{{ step.paramVo.title }}</span>
+                </div>
+                <div v-if="step.fsCourseRedPacketLog" class="detail-item">
+                  <span class="detail-label"><i class="el-icon-coin"></i> 发放金额:</span>
+                  <span class="detail-value">{{ step.fsCourseRedPacketLog.amount }} 元</span>
+                </div>
+              </div>
+
+              <!-- 第二行 -->
+              <div v-if="step.details" class="step-row second-row">
+                <div class="detail-item">
+                  <span class="detail-label"><i class="el-icon-chat-dot-round"></i> 备注:</span>
+                  <span class="detail-value">{{ step.details }}</span>
+                </div>
+              </div>
+            </div>
+
+            <!-- 其他类型 -->
+            <div v-else class="step-content">
+              <!-- 第一行 -->
+              <div class="step-row first-row">
+                <div v-if="step.paramVo" class="detail-item">
+                  <span class="detail-label"><i class="el-icon-office-building"></i> 训练营:</span>
+                  <span class="detail-value">{{ step.paramVo.trainingCampName }}</span>
+                </div>
+                <div v-if="step.paramVo" class="detail-item">
+                  <span class="detail-label"><i class="el-icon-collection-tag"></i> 营期名称:</span>
+                  <span class="detail-value">{{ step.paramVo.periodName }}</span>
+                </div>
+                <div v-if="step.paramVo" class="detail-item">
+                  <span class="detail-label"><i class="el-icon-notebook-2"></i> 课节名称:</span>
+                  <span class="detail-value">{{ step.paramVo.courseName }}</span>
+                </div>
+              </div>
+
+              <!-- 第二行 -->
+              <div v-if="step.details" class="step-row second-row">
+                <div class="detail-item">
+                  <span class="detail-label"><i class="el-icon-chat-dot-round"></i> 备注:</span>
+                  <span class="detail-value">{{ step.details }}</span>
+                </div>
+              </div>
+            </div>
+          </template>
+
+        </el-step>
+      </el-steps>
+    </div>
+
+    <!-- 分页 -->
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </div>
+
+
+</template>
+
+<script>
+import { listUserOperationLog,getOperationType} from "@/api/course/userOperationLog";
+
+export default {
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      typeList: [],
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        operationType:null,
+        userId:null,
+      },
+      total: 0,
+      steps: [
+      ]
+    };
+  },
+  created() {
+    getOperationType().then(response => {
+      this.typeList = response.data;
+    });
+  },
+  computed: {
+    // 删除这个属性,直接在 getList 中使用后端的操作类型
+    groupedPaginatedSteps() {
+      const groups = {};
+      this.steps.forEach((step) => {
+        const date = step.createTime ?
+          (step.createTime instanceof Date ? step.createTime.toISOString().substr(0, 10) : new Date(step.createTime).toISOString().substr(0, 10))
+          : null;
+        if (!groups[date]) {
+          groups[date] = [];
+        }
+        groups[date].push(step);
+      });
+      return groups;
+    },
+
+  },
+  methods: {
+    getList() {
+      this.loading = true;
+      listUserOperationLog(this.queryParams).then(response => {
+        this.steps = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 判断是否为今天
+    isToday(date) {
+      const today = new Date();
+      const formattedDate = `${today.getFullYear()}-${(today.getMonth() + 1).toString().padStart(2, '0')}-${today.getDate().toString().padStart(2, '0')}`;
+      return date === formattedDate;
+    },
+    getDetails(orderId) {
+      this.queryParams.userId=orderId;
+      this.getList();
+
+    },
+    handleTypeChange() {
+      this.queryParams.pageNum = 1;  // 重置分页为第一页
+      this.getList();  // 重新获取数据
+    }
+  },
+};
+</script>
+
+<style scoped>
+.behavior-track-container {
+  width: 100%;
+  padding: 24px;
+  font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", Arial, sans-serif;
+  background-color: #f9fbfd;
+  border-radius: 12px;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
+}
+
+.filter-section {
+  margin-bottom: 24px;
+  background: #ffffff;
+  border-radius: 8px;
+  padding: 16px 20px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+  display: flex;
+  align-items: center;
+}
+
+.filter-item {
+  display: flex;
+  align-items: center;
+}
+
+.filter-item label {
+  font-weight: 600;
+  color: #333;
+  margin-right: 12px;
+  font-size: 14px;
+}
+
+.type-select {
+  width: 240px;
+}
+
+.custom-steps {
+  padding: 0 16px;
+}
+
+.step-title {
+  font-size: 16px;
+  font-weight: 600;
+  color: #333;
+}
+
+.step-detail p {
+  margin: 8px 0;
+  line-height: 1.6;
+  font-size: 14px;
+  color: #555;
+}
+
+/* 步骤条样式优化 - 移除图标边框 */
+.custom-steps >>> .el-step__head {
+  padding-right: 16px;
+}
+
+.custom-steps >>> .el-step__icon {
+  width: 24px;
+  height: 24px;
+  font-size: 18px;
+}
+
+.custom-steps >>> .el-step__icon.is-icon {
+  color: #52c41a;
+}
+
+.custom-steps >>> .el-step__line {
+  top: 36px;
+  left: 11px;
+  background-color: #e8e8e8;
+}
+
+/* 分页样式优化 */
+.pagination-wrapper {
+  margin-top: 32px;
+  text-align: center;
+  padding: 16px 0;
+}
+
+.custom-pagination >>> .el-pager li {
+  border-radius: 4px;
+  margin: 0 4px;
+}
+
+.custom-pagination >>> .el-pager li.active {
+  background-color: #52c41a;
+  color: #fff;
+}
+
+.custom-pagination >>> .el-pagination__jump {
+  margin-left: 12px;
+}
+
+.custom-pagination >>> .el-input__inner {
+  border-radius: 4px;
+}
+
+/* 新增和修改的样式 */
+.step-content {
+  margin-top: 8px;
+  padding: 16px;
+  background: #ffffff;
+  border-radius: 8px;
+  border: 1px solid #ebeef5;
+  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.06);
+  transition: all 0.3s ease;
+}
+
+.step-content:hover {
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
+  transform: translateY(-1px);
+}
+
+.detail-label {
+  display: inline-flex;
+  align-items: center;
+  width: 100px;
+  color: #606266;
+  font-weight: 500;
+  flex-shrink: 0;
+}
+
+.detail-label i {
+  margin-right: 6px;
+  font-size: 14px;
+  color: #909399;
+}
+
+.detail-value {
+  flex: 1;
+  color: #303133;
+  word-break: break-word;
+  padding-left: 4px;
+}
+
+.step-row {
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: space-between;
+  margin-bottom: 8px;
+}
+
+.first-row .detail-item {
+  flex: 1 1 30%; /* 平均分布 */
+  display: flex;
+  align-items: center;
+  margin-right: 16px;
+}
+
+.second-row .detail-item {
+  display: flex;
+  align-items: center;
+}
+
+.time-item {
+  margin-left: auto; /* 时间靠右 */
+}
+
+.detail-label {
+  font-weight: 500;
+  color: #606266;
+  margin-right: 6px;
+}
+
+.detail-value {
+  color: #303133;
+  word-break: break-word;
+}
+
+@media (max-width: 768px) {
+  .first-row,
+  .second-row {
+    flex-direction: column;
+  }
+  .time-item {
+    margin-left: 0;
+  }
+}
+/* 完全重置步骤描述区域的样式 */
+.custom-steps >>> .el-step__description {
+  width: 100% !important;
+  max-width: 100% !important;
+  padding: 0 !important;
+  margin: 0 !important;
+}
+
+/* 确保步骤内容使用弹性布局 */
+.step-content {
+  display: flex;
+  flex-direction: column;
+  width: 100%;
+}
+
+/* 调整时间项 */
+.time-item {
+  align-self: flex-end; /* 替代 margin-left: auto */
+  margin-top: 8px; /* 如果需要与上方的间距 */
+}
+/* 日期标题样式 */
+.date-title {
+  font-size: 16px;
+  font-weight: bold;
+  color: #333;
+  margin-top: 16px;
+}
+
+.date-divider {
+  height: 1px;
+  background: #e8e8e8;
+  margin: 4px 0 12px;
+}
+
+/* 在步骤条标题旁边显示时间 */
+.step-title {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.step-time {
+  font-size: 12px;
+  color: #999;
+  margin-left: 10px;
+}
+
+/* 第二行的备注 */
+.second-row {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.second-row .detail-item {
+  display: flex;
+  align-items: center;
+}
+
+.no-data {
+  text-align: center;
+  color: #999;
+  font-size: 18px;
+  padding: 20px;
+}
+</style>

+ 173 - 0
src/views/store/components/userCourseConversionRecord.vue

@@ -0,0 +1,173 @@
+<template>
+
+  <div style="background-color: #f0f2f5; padding-bottom: 20px; min-height: 100%; " >
+    <div class="contentx" v-if="item!=null">
+      <div class="desct"> 优惠劵领取信息</div>
+      <el-tabs type="card" v-model="actName" @tab-click="handleClickX">
+        <el-tab-pane label="全部" name="10"></el-tab-pane>
+        <el-tab-pane v-for="(item,index) in couponStatusOptions" :label="item.dictLabel" :name="item.dictValue"></el-tab-pane>
+      </el-tabs>
+      <el-table v-loading="loading" :data="userCouponList">
+        <el-table-column label="优惠劵标题" align="center" prop="title" />
+        <el-table-column label="券号" align="center" prop="couponCode" />
+        <el-table-column label="会员昵称" align="center" prop="nickName" />
+        <el-table-column label="会员电话" align="center" prop="phone" />
+        <el-table-column label="关联订单ID" align="center" prop="businessId" />
+        <el-table-column label="订单类型" align="center" prop="businessType">
+          <template slot-scope="scope">
+            <dict-tag :options="businessTypeOptions" :value="scope.row.businessType"/>
+          </template>
+        </el-table-column>
+        <el-table-column label="状态" align="center" prop="status">
+          <template slot-scope="scope">
+            <dict-tag :options="couponStatusOptions" :value="scope.row.status"/>
+          </template>
+        </el-table-column>
+        <el-table-column label="领取时间" align="center" prop="createTime" width="180"/>
+        <el-table-column label="使用时间" align="center" prop="useTime" width="180"/>
+      </el-table>
+      <pagination
+        v-show="total>0"
+        :total="total"
+        :page.sync="queryParams.pageNum"
+        :limit.sync="queryParams.pageSize"
+        @pagination="getList"
+      />
+    </div>
+
+    <div class="contentx" v-if="item!=null" >
+      <div class="desct">
+        用户药品订单
+      </div>
+      <userStorerDetails  ref="userDetails" />
+    </div>
+  </div>
+</template>
+
+
+
+<script>
+import { getPatientByUserId} from "@/api/store/patient";
+import { getUser ,getUserAddr} from "@/api/store/user";
+import { getListUserCoupon } from "@/api/store/userCoupon";
+import userStorerDetails from "./userStorerDetails.vue";
+import userPatietDetails from "./userPatietDetails.vue";
+import userInquiryOrderDetails from "./userInquiryOrderDetails.vue";
+import userAddDetails from "./userAddDetails.vue";
+export default {
+  name: "storedet",
+  props:["data"],
+  components: { userStorerDetails ,userInquiryOrderDetails,userPatietDetails,userAddDetails},
+  data() {
+    return {
+      patientInfo: process.env.VUE_APP_PATIENT_INFO,
+      addr:[],
+      patient:[],
+      userOptions: [],
+      statusOptions: [],
+      sexOptions: [],
+      pOptions: [],
+      item:null,
+      total: 0,
+      loading: true,
+      // 会员优惠券表格数据
+      userCouponList: [],
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        userId: null,
+        couponId: null,
+      },
+      actName:"10",
+      businessTypeOptions:[],
+      couponStatusOptions:[],
+    }
+  },
+  created() {
+    this.getDicts("sys_user_status").then(response => {
+      this.userOptions = response.data;
+    });
+    this.getDicts("sys_company_status").then(response => {
+      this.statusOptions = response.data;
+    });
+    this.getDicts("sys_patient_status").then(response => {
+      this.pOptions = response.data;
+    });
+    this.getDicts("sys_patient_sex").then(response => {
+      this.sexOptions = response.data;
+    });
+    this.getDicts("sys_coupon_business_type").then(response => {
+      this.businessTypeOptions = response.data;
+    });
+
+    this.getDicts("sys_coupon_status").then(response => {
+      this.couponStatusOptions = response.data;
+    });
+  },
+  methods: {
+    handleClickX(tab, event) {
+      if(tab.name=="10"){
+        this.queryParams.status=null;
+      }else{
+        this.queryParams.status=tab.name;
+      }
+      this.queryParams.pageNum = 1;
+      this.getList();
+
+    },
+    getList() {
+      this.loading = true;
+      getListUserCoupon(this.queryParams).then(response => {
+        this.userCouponList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+
+    getDetails(orderId) {
+      this.item=null;
+      getUser(orderId).then(response => {
+        this.item = response.data;
+        setTimeout(() => {
+          this.$refs.userDetails.getUserDetails(orderId);
+        }, 1);
+
+      });
+      this.patient=null;
+      getPatientByUserId(orderId).then(response => {
+        this.patient = response.data;
+      });
+      getUserAddr(orderId).then(response => {
+        this.addr = response.data;
+      });
+      this.queryParams.userId=orderId;
+      this.getList();
+
+    },
+  }
+}
+</script>
+<style>
+
+.contentx{
+  height: 100%;
+  background-color: #fff;
+  padding: 0px 20px 20px;
+
+
+  margin: 20px;
+}
+.el-descriptions-item__label.is-bordered-label{
+  font-weight: normal;
+}
+.el-descriptions-item__content {
+  max-width: 150px;
+  min-width: 100px;
+}
+.desct{
+  padding-top: 20px;
+  padding-bottom: 20px;
+  color: #524b4a;
+  font-weight: bold;
+}
+</style>

+ 476 - 0
src/views/store/components/userCoursePeriod.vue

@@ -0,0 +1,476 @@
+<template>
+  <div class="app-container">
+    <!-- 没有数据提示 -->
+    <div v-if="campList.length === 0 && !leftLoading" class="no-data">
+      <span>—— 没有数据 ——</span>
+    </div>
+
+    <el-container v-else>
+      <!-- 左侧区域 -->
+      <el-aside width="360px" class="left-aside">
+        <!-- 顶部区域 -->
+        <!-- 训练营列表 -->
+        <div class="camp-list" ref="campList" @scroll="handleScroll" v-loading="leftLoading">
+          <div
+            v-for="(item, index) in campList"
+            :key="index"
+            class="camp-item"
+            :class="{ 'active': activeCampIndex === index }"
+            @click="selectCamp(index)"
+          >
+            <div class="camp-content">
+              <div class="camp-title">
+                <i class="el-icon-s-flag camp-icon"></i>
+                {{ item.trainingCampName }}
+              </div>
+              <div class="camp-info">
+                <span>序号:{{ item.orderNumber }}</span>
+                <span>最新营期开课:{{ item.recentDate || '-' }}</span>
+              </div>
+            </div>
+          </div>
+          <!-- 底部加载更多提示 -->
+          <div v-if="loadingMore" class="loading-more">
+            <i class="el-icon-loading"></i>
+            <span>加载中...</span>
+          </div>
+
+          <!-- 所有数据加载完毕提示 -->
+          <div v-if="campList.length > 0 && !loadingMore" class="no-more-data">
+            <span>—— 已加载全部训练营 ——</span>
+          </div>
+        </div>
+      </el-aside>
+
+      <!-- 右侧区域 -->
+      <el-main><userCourseStatic  ref="userCourseStatic" /></el-main>
+    </el-container>
+
+  </div>
+</template>
+
+<script>
+import { listCamp} from "@/api/course/userCourseCamp";
+import userCourseStatic from './userCourseStatic.vue';
+export default {
+  name: "userCoursePeriod",
+  components: {
+    userCourseStatic
+  },
+  data() {
+    return {
+      // 加载更多状态
+      loadingMore: false,
+      // 激活的训练营索引
+      activeCampIndex: null,
+      // 训练营列表
+      campList: [],
+      // 遮罩层
+      loading: true,
+      updateDateOpen: false,
+      // 左侧遮罩层
+      leftLoading: true,
+      // 左侧查询参数
+      leftQueryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        hasNextPage: false,
+        scs: 'order_number(desc),training_camp_id(desc)',
+        trainingCampName: null,
+        userId:null,
+      },
+    };
+  },
+  created() {
+    // this.getLeftList();
+  },
+  methods: {
+    getDetails(orderId) {
+      this.userId=orderId;
+      this.leftQueryParams.userId=orderId;
+      this.getLeftList();
+    },
+    /** 查询左侧列表 */
+    getLeftList() {
+      this.leftLoading = true;
+      // 重置页码和加载更多状态
+      this.leftQueryParams.pageNum = 1;
+      this.loadingMore = false;
+
+      // 训练营数据
+      listCamp(this.leftQueryParams).then(response => {
+        if (response && response.code === 200) {
+          this.campList = response.data.list || [];
+          this.leftQueryParams.hasNextPage = response.data.hasNextPage;
+          this.activeCampIndex = this.campList.length > 0 ? 0 : null;
+          this.selectCamp(this.activeCampIndex);
+          // 如果当前显示的列表高度不足以触发滚动,但还有更多数据,自动加载下一页
+          this.$nextTick(() => {
+            const scrollEl = this.$refs.campList;
+            if (scrollEl && this.leftQueryParams.hasNextPage && scrollEl.scrollHeight <= scrollEl.clientHeight) {
+              this.loadMoreCamps();
+            }
+          });
+        } else {
+          this.$message.error(response.msg || '获取训练营列表失败');
+          this.campList = [];
+          this.leftQueryParams.hasNextPage = false;
+        }
+        this.leftLoading = false;
+      }).catch(error => {
+        console.error('获取训练营列表失败:', error);
+        this.$message.error('获取训练营列表失败');
+        this.campList = [];
+        this.leftQueryParams.hasNextPage = false;
+        this.leftLoading = false;
+      });
+    },
+    /** 选中训练营 */
+    selectCamp(index) {
+      if(index == null || index == undefined) return;
+      this.activeCampIndex = index;
+      // 加载对应的训练营营期数据
+      const selectedCamp = this.campList[index];
+      console.log(this.userId)
+      this.$refs.userCourseStatic.getDetails(selectedCamp,this.userId);
+      // this.queryParams.trainingCampId = selectedCamp.trainingCampId;
+      // this.getList();
+    },
+    /** 处理滚动事件,实现滚动到底部加载更多 */
+    handleScroll() {
+      // 如果正在节流中或者正在加载中,则不处理
+      if (this.scrollThrottle || this.loadingMore) return;
+
+      // 设置节流,200ms内不再处理滚动事件
+      this.scrollThrottle = true;
+      setTimeout(() => {
+        this.scrollThrottle = false;
+      }, 200);
+
+      const scrollEl = this.$refs.campList;
+      if (!scrollEl) return;
+
+      // 判断是否滚动到底部:滚动高度 + 可视高度 >= 总高度 - 30(添加30px的容差,提前触发加载)
+      const isBottom = scrollEl.scrollTop + scrollEl.clientHeight >= scrollEl.scrollHeight - 30;
+
+      // 如果滚动到底部,且有下一页数据,且当前不在加载中,则加载更多
+      if (isBottom && this.leftQueryParams.hasNextPage && !this.leftLoading && !this.loadingMore) {
+        this.loadMoreCamps();
+      }
+    },
+    /** 加载更多训练营数据 */
+    loadMoreCamps() {
+      // 已在加载中,防止重复加载
+      if (this.leftLoading || this.loadingMore) return;
+
+      // 设置加载状态
+      this.loadingMore = true;
+
+      // 页码加1
+      this.leftQueryParams.pageNum += 1;
+
+      // 加载下一页数据
+      listCamp(this.leftQueryParams).then(response => {
+        if (response && response.code === 200) {
+          // 将新数据追加到列表中
+          const newList = response.data.list || [];
+          if (newList.length > 0) {
+            this.campList = [...this.campList, ...newList];
+          }
+
+          // 更新是否有下一页的标志
+          this.leftQueryParams.hasNextPage = response.data.hasNextPage;
+
+          // 如果当前显示的列表高度不足以触发滚动,但还有更多数据,自动加载下一页
+          this.$nextTick(() => {
+            const scrollEl = this.$refs.campList;
+            if (scrollEl && this.leftQueryParams.hasNextPage && scrollEl.scrollHeight <= scrollEl.clientHeight) {
+              // 延迟一点再加载下一页,避免过快加载
+              setTimeout(() => {
+                this.loadMoreCamps();
+              }, 300);
+            }
+          });
+        } else {
+          this.$message.error(response.msg || '加载更多训练营失败');
+        }
+        this.loadingMore = false;
+      }).catch(error => {
+        console.error('加载更多训练营失败:', error);
+        this.$message.error('加载更多训练营失败');
+        this.loadingMore = false;
+      });
+    },
+
+  },
+};
+</script>
+
+<style scoped>
+.left-aside {
+  background-color: #fff;
+  border-right: 1px solid #EBEEF5;
+  padding: 0;
+  display: flex;
+  flex-direction: column;
+  height: 800px;
+}
+
+.left-header {
+  padding: 10px;
+  border-bottom: 1px solid #EBEEF5;
+}
+
+.left-header-top {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 10px;
+}
+
+.search-btn {
+  width: 50%;
+  height: 36px;
+  background-color: #409EFF;
+  color: white;
+  border: none;
+}
+
+.search-input-wrapper {
+  margin-bottom: 10px;
+}
+
+.sort-wrapper {
+  display: flex;
+  align-items: center;
+  margin-bottom: 10px;
+}
+
+.sort-label {
+  width: 70px;
+  font-size: 14px;
+  font-weight: 600;
+  color: #909399;
+}
+
+.sort-select {
+  margin-left: 10px;
+  width: 280px;
+}
+
+.color-wrapper {
+  display: flex;
+  align-items: center;
+  margin-bottom: 10px;
+}
+
+.color-label {
+  width: 70px;
+  color: #606266;
+}
+
+.color-hint {
+  margin-left: 10px;
+  color: #606266;
+  font-size: 12px;
+}
+
+.color-help {
+  margin-left: 5px;
+  color: #909399;
+  cursor: pointer;
+}
+
+.hint-text {
+  color: #606266;
+  font-size: 12px;
+  margin-top: 5px;
+}
+
+.camp-list {
+  flex: 1;
+  overflow-y: auto;
+  padding: 3px;
+}
+
+.camp-item {
+  margin-bottom: 5px;
+  padding: 15px;
+  background-color: #ffffff;
+  position: relative;
+  cursor: pointer;
+  display: flex;
+  justify-content: space-between;
+  border: 1px solid #eaedf2;
+}
+
+.camp-item:last-child {
+  margin-bottom: 0;
+}
+
+.camp-item:hover {
+  background-color: #f5f9ff;
+}
+
+.camp-item.active {
+  background-color: #eaf4ff;
+  border-left: 1px solid #75b8fc;
+}
+
+.camp-content {
+  flex: 1;
+  padding-right: 10px;
+}
+
+.camp-title {
+  font-weight: bold;
+  font-size: 16px;
+  margin-bottom: 8px;
+  color: #333;
+  display: flex;
+  align-items: center;
+}
+
+.camp-icon {
+  font-size: 16px;
+  margin-right: 6px;
+  color: #409EFF;
+}
+
+.camp-info {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 8px;
+  font-size: 12px;
+  color: #c4c1c1;
+  line-height: 1.5;
+}
+
+.camp-stats {
+  display: flex;
+  justify-content: space-between;
+  font-size: 12px;
+  color: #666;
+  background-color: #f5f9ff;
+  padding: 6px 10px;
+  border-radius: 4px;
+  line-height: 1.5;
+}
+
+.stat-item {
+  display: flex;
+  align-items: center;
+}
+
+.stat-item i {
+  margin-right: 4px;
+  font-size: 14px;
+  color: #409EFF;
+}
+
+.camp-actions {
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: flex-end;
+  gap: 8px;
+  border-left: 1px dashed #eaedf2;
+  padding-left: 12px;
+  min-width: 50px;
+}
+
+.action-btn {
+  padding: 2px 5px;
+  font-size: 12px;
+  border-radius: 4px;
+  transition: all 0.2s;
+}
+
+.action-btn:hover {
+  background-color: rgba(255, 255, 255, 0.8);
+}
+
+.delete-btn {
+  color: #f56c6c;
+}
+
+.delete-btn:hover {
+  background-color: rgba(245, 108, 108, 0.1);
+}
+
+.copy-btn {
+  color: #409EFF;
+}
+
+.copy-btn:hover {
+  background-color: rgba(64, 158, 255, 0.1);
+}
+
+.warning-icon {
+  color: #E6A23C;
+  font-size: 16px;
+  margin-left: 5px;
+}
+
+.el-main {
+  padding: 10px;
+}
+
+/* 添加训练营表单样式 */
+.drawer-footer {
+  /* position: absolute; */
+  bottom: 0;
+  left: 0;
+  right: 0;
+  padding: 20px;
+  background: #fff;
+  text-align: right;
+  border-top: 1px solid #e8e8e8;
+}
+
+.el-input-number {
+  width: 100%;
+}
+
+/* 加载更多样式 */
+.loading-more {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 12px 0;
+  color: #909399;
+  font-size: 14px;
+}
+
+.loading-more i {
+  margin-right: 5px;
+  font-size: 16px;
+}
+
+/* 无更多数据提示 */
+.no-more-data {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 12px 0;
+  color: #c0c4cc;
+  font-size: 13px;
+}
+
+.no-more-data span {
+  position: relative;
+  display: flex;
+  align-items: center;
+}
+/* 添加没有数据提示的样式 */
+.no-data {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  height: 100vh;  /* 让提示居中,覆盖整个页面 */
+  font-size: 18px;
+  color: #c0c4cc;
+  background-color: #f5f5f5;
+}
+
+</style>

+ 222 - 0
src/views/store/components/userCoursePeriodDetails.vue

@@ -0,0 +1,222 @@
+<template>
+  <div class="statistics-container">
+    <!-- 营期课程选择 -->
+    <div class="fixed-header">
+      <el-form :inline="true" :model="queryParams" class="demo-form-inline">
+        <el-form-item label="营期课程">
+          <el-select
+            v-model="queryParams.periodId"
+            placeholder="请选择营期课程"
+            style="width: 400px"
+            clearable
+          >
+            <el-option
+              v-for="item in courseOptions"
+              :key="item.periodId"
+              :label="item.periodName"
+              :value="item.periodId"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="handleQuery">查询</el-button>
+        </el-form-item>
+      </el-form>
+
+      <!-- 统计数据展示 -->
+      <el-row :gutter="20" class="statistics-row">
+        <el-col :span="4">
+          <div class="statistics-item">
+            <div class="statistics-title">到课数</div>
+            <div class="statistics-value">{{ statistics.courseCompleteNum || 0 }}次</div>
+          </div>
+        </el-col>
+        <el-col :span="4">
+          <div class="statistics-item">
+            <div class="statistics-title">总学习时长</div>
+            <div class="statistics-value">{{ statistics.courseWatchNum || 0 }}分钟</div>
+          </div>
+        </el-col>
+        <el-col :span="4">
+          <div class="statistics-item">
+            <div class="statistics-title">正确答题数</div>
+            <div class="statistics-value">{{ statistics.correctAnswerNum || 0 }}次</div>
+          </div>
+        </el-col>
+        <el-col :span="4">
+          <div class="statistics-item">
+            <div class="statistics-title">获得红包数</div>
+            <div class="statistics-value">{{ statistics.redPacketCount || 0 }}次</div>
+          </div>
+        </el-col>
+        <el-col :span="4">
+          <div class="statistics-item">
+            <div class="statistics-title">红包总金额</div>
+            <div class="statistics-value">{{ statistics.redPacketAmount || 0 }}元</div>
+          </div>
+        </el-col>
+      </el-row>
+    </div>
+
+  </div>
+</template>
+
+<script>
+import {getDays, periodCourseStatisticCount,periodList} from "@/api/course/userCoursePeriod";
+
+export default {
+  name: "userCoursePeriodDetails",
+  props: {
+    periodId: {
+      type: [String, Number],
+      default: ''
+    },
+    active: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    return {
+      // 遮罩层
+      loading: false,
+      // 总条数
+      total: 0,
+      // 课程选项
+      courseOptions: [],
+      // 统计数据
+      statistics: {
+        courseCompleteNum: 0,
+        courseWatchNum: 0,
+        redPacketCount: 0,
+        correctAnswerNum: 0,
+        redPacketAmount: 0
+      },
+      // 列表数据
+      list: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        // videoId: '',
+        periodId: '',
+        trainingCampId:null,
+        userId:null,
+      },
+      // 是否已初始化
+      initialized: false
+    };
+  },
+  created() {
+  },
+  methods: {
+    getDetails(camp,userid) {
+      this.queryParams.trainingCampId = camp.trainingCampId;
+      this.camp = camp;
+      this.userId=userid;
+      this.queryParams.userId=userid;
+      this.user=null;
+      this.courseOptions = []
+      this.queryParams.periodId = null;
+      this.getCourseOptions();
+      this.calculateTotalStatistics();
+    },
+    /** 查询按钮操作 */
+    handleQuery() {
+      this.calculateTotalStatistics();
+    },
+    /** 获取课程选项 */
+    getCourseOptions() {
+
+      this.loading = true;
+      periodList(this.queryParams).then(r => {
+        if (r.code === 200) {
+          this.courseOptions = r.data;
+          this.loading = false;
+        } else {
+          this.$message.error(r.msg || '获取数据失败');
+        }
+        this.loading = false;
+      }).catch(() => {
+        this.loading = false;
+      });
+    },
+
+    /** 计算总统计数据 */
+    calculateTotalStatistics() {
+      console.log("this.queryParams:",this.queryParams)
+      periodCourseStatisticCount(this.queryParams).then(response => {
+        if (response.code === 200) {
+          // 设置列表数据
+          this.statistics.courseCompleteNum = response.data.courseCompleteNum || 0;
+          this.statistics.courseWatchNum = response.data.courseWatchNum || 0;
+          this.statistics.courseWatchTimes = response.data.courseWatchTimes || 0;
+          this.statistics.redPacketCount = response.data.redPacketCount || 0;
+          this.statistics.correctAnswerNum = response.data.correctAnswerNum || 0;
+          this.statistics.redPacketAmount = response.data.redPacketAmount || 0;
+
+        } else {
+          this.$message.error(response.msg || '获取数据失败');
+        }
+        this.loading = false;
+      }).catch(error => {
+        console.error('获取数据失败:', error);
+        this.$message.error('获取数据失败');
+        this.loading = false;
+      });
+
+    }
+  }
+};
+</script>
+
+<style scoped>
+.statistics-container {
+  height: 100%;
+  overflow: hidden;
+  position: relative;
+}
+
+.fixed-header {
+  position: sticky;
+  top: 0;
+  z-index: 10;
+  background-color: #fff;
+  padding: 10px 0;
+  border-bottom: 1px solid #EBEEF5;
+}
+/* 覆盖原有的pagination-container样式 */
+:deep(.pagination-container) {
+  height: auto !important;
+  margin-bottom: 0 !important;
+  margin-top: 0 !important;
+  padding: 0 !important;
+}
+
+.statistics-row {
+  margin: 20px 0;
+}
+
+.statistics-item {
+  background-color: #f5f7fa;
+  border-radius: 4px;
+  padding: 15px;
+  text-align: center;
+  height: 100px;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+}
+
+.statistics-title {
+  font-size: 14px;
+  color: #606266;
+  margin-bottom: 10px;
+}
+
+.statistics-value {
+  font-size: 20px;
+  font-weight: bold;
+  color: #303133;
+}
+</style>

+ 57 - 0
src/views/store/components/userCourseStatic.vue

@@ -0,0 +1,57 @@
+<template>
+  <el-tabs v-model="activeName" type="card" @tab-click="handleClick">
+    <el-tab-pane label="数据总览" name="first"><userStaticAll  ref="userStaticAll" /></el-tab-pane>
+    <el-tab-pane label="课节详情" name="second"><userCoursePeriodDetails  ref="userCoursePeriodDetails" /></el-tab-pane>
+    <el-tab-pane label="转化记录" name="third"><userCourseConversionRecord  ref="userCourseConversionRecord" /></el-tab-pane>
+
+  </el-tabs>
+</template>
+<script>
+import userStaticAll from './userStaticAll.vue';
+import userCoursePeriodDetails from './userCoursePeriodDetails.vue';
+import userCourseConversionRecord from './userCourseConversionRecord.vue';
+
+export default {
+  name: "userCourseStatic",
+  components: {
+    userStaticAll,userCoursePeriodDetails,userCourseConversionRecord
+  },
+  data() {
+
+    return {
+      camp:{},
+      userId:null,
+      activeName: 'first'
+    };
+  },
+  methods: {
+    handleClick(tab, event) {
+      if (tab.name === "first"){
+        setTimeout(() => {
+          this.$refs.userStaticAll.getDetails(this.camp,this.userId);
+        }, 1);
+      }else if (tab.name === "second"){
+        setTimeout(() => {
+          this.$refs.userCoursePeriodDetails.getDetails(this.camp,this.userId);
+        }, 1);
+      }else {
+        setTimeout(() => {
+          this.$refs.userCourseConversionRecord.getDetails(this.userId);
+        }, 1);
+      }
+
+    },
+
+    getDetails(camp,userid) {
+      this.camp = camp;
+      this.userId=userid;
+      this.activeName = 'first';
+      // 默认加载“数据总览”接口
+      this.$nextTick(() => {
+        this.$refs.userStaticAll.getDetails(this.camp, this.userId);
+      });
+
+    },
+  }
+};
+</script>

+ 55 - 263
src/views/store/components/userDetails.vue

@@ -1,274 +1,66 @@
 <template>
-
-<div style="background-color: #f0f2f5; padding-bottom: 20px; min-height: 100%; " >
-<!--
-  <div style="padding: 20px; background-color: #fff;">
-    会员详情
-  </div>
--->
-
-  <div class="contentx" v-if="item!=null" >
-    <div class="desct">
-      基本信息
+  <div>
+    <div style="background-color: #f0f2f5; padding-bottom: 20px; min-height: 100%; " >
+      <div style="padding: 20px; background-color: #fff;">
+        会员详情
+      </div>
     </div>
-        <el-descriptions title="" :column="3" border>
-
-          <el-descriptions-item label="会员id" >
-                <span v-if="item!=null">{{item.userId}}</span>
-            </el-descriptions-item>
-            <el-descriptions-item label="用户昵称" >
-                <span v-if="item!=null">{{item.nickName}}</span>
-            </el-descriptions-item>
-            <el-descriptions-item label="用户头像" >
-                 <el-image v-if="item.avatar!=null"
-                          style="width: 50px;"
-                          :src="item.avatar">
-                      </el-image>
-            </el-descriptions-item>
-            <el-descriptions-item label="手机号码" >
-                <span v-if="item!=null">{{item.phone}}</span>
-            </el-descriptions-item>
-            <el-descriptions-item label="用户积分" >
-                <span v-if="item!=null">{{item.integral}}</span>
-            </el-descriptions-item>
-            <el-descriptions-item label="状态" >
-             <span v-if="item!=null">
-                   <dict-tag :options="userOptions" :value="item.status"/>
-             </span>
-            </el-descriptions-item>
-
-            <el-descriptions-item label="上级昵称" >
-                <span v-if="item!=null">{{item.tuiName}}</span>
-            </el-descriptions-item>
-            <el-descriptions-item label="上级手机号码" >
-                <span v-if="item!=null">{{item.tuiPhone}}</span>
-            </el-descriptions-item>
-            <el-descriptions-item label="推广员关联时间" >
-                <span v-if="item!=null">{{item.tuiTime}}</span>
-            </el-descriptions-item>
-            <el-descriptions-item label="下级人数" >
-                <span v-if="item!=null">{{item.tuiUserCount}}</span>
-            </el-descriptions-item>
-            <el-descriptions-item label="最后一次登录ip" >
-                <span v-if="item!=null">{{item.lastIp}}</span>
-            </el-descriptions-item>
-            <el-descriptions-item label="余额" >
-                <span v-if="item!=null">{{item.balance}}</span>
-            </el-descriptions-item>
-            <el-descriptions-item label="创建时间" >
-                <span v-if="item!=null">{{item.createTime}}</span>
-            </el-descriptions-item>
-
-            <el-descriptions-item label="更新时间" >
-                <span v-if="item!=null">{{item.updateTime}}</span>
-            </el-descriptions-item>
-        </el-descriptions>
-</div>
-
-    <div class="contentx" v-if="item!=null">
-            <div class="desct"> 优惠劵领取信息</div>
-            <el-tabs type="card" v-model="actName" @tab-click="handleClickX">
-              <el-tab-pane label="全部" name="10"></el-tab-pane>
-              <el-tab-pane v-for="(item,index) in couponStatusOptions" :label="item.dictLabel" :name="item.dictValue"></el-tab-pane>
-            </el-tabs>
-            <el-table v-loading="loading" :data="userCouponList">
-              <el-table-column label="优惠劵标题" align="center" prop="title" />
-              <el-table-column label="券号" align="center" prop="couponCode" />
-              <el-table-column label="会员昵称" align="center" prop="nickName" />
-              <el-table-column label="会员电话" align="center" prop="phone" />
-              <el-table-column label="关联订单ID" align="center" prop="businessId" />
-              <el-table-column label="订单类型" align="center" prop="businessType">
-                <template slot-scope="scope">
-                  <dict-tag :options="businessTypeOptions" :value="scope.row.businessType"/>
-                </template>
-              </el-table-column>
-              <el-table-column label="状态" align="center" prop="status">
-                <template slot-scope="scope">
-                  <dict-tag :options="couponStatusOptions" :value="scope.row.status"/>
-                </template>
-              </el-table-column>
-              <el-table-column label="领取时间" align="center" prop="createTime" width="180"/>
-              <el-table-column label="使用时间" align="center" prop="useTime" width="180"/>
-            </el-table>
-            <pagination
-              v-show="total>0"
-              :total="total"
-              :page.sync="queryParams.pageNum"
-              :limit.sync="queryParams.pageSize"
-              @pagination="getList"
-            />
-        </div>
-
-
-
-  <div class="contentx" v-if="item!=null">
-        <div class="desct">
-          <span v-if="patientInfo">
-            {{ patientInfo }}
-          </span>
-          <span v-else>
-            患者信息
-          </span>
-        </div>
-    <userPatietDetails  ref="userPatietDetail" />
-
+    <template>
+      <el-tabs v-model="activeName"  :tab-position="tabPosition" style="height: 200px;margin: 40px">
+        <el-tab-pane label="基本信息" name="basic"><userDetailsTemp  ref="userDetailsTemp" /></el-tab-pane>
+        <el-tab-pane label="行为轨迹" name="behavior"><userBehavior  ref="userBehavior" /></el-tab-pane>
+        <el-tab-pane label="训练营"  name="course"><userCoursePeriod  ref="userCoursePeriod" /></el-tab-pane>
+      </el-tabs>
+    </template>
   </div>
-
-  <div class="contentx" v-if="item!=null" >
- <div class="desct">
-      用户地址
- </div>
- <userAddDetails  ref="userAddDetail" />
-
-   </div>
-  <div class="contentx" v-if="item!=null" >
- <div class="desct">
-      用户药品订单
- </div>
-    <userStorerDetails  ref="userDetails" />
-   </div>
-<div class="contentx" v-if="item!=null" >
- <div class="desct">
-      用户问诊订单
- </div>
-    <userInquiryOrderDetails  ref="InquiryDetails" />
-   </div>
- </div>
-</template>
-
-
-
+</template>  
 <script>
-import { getPatientByUserId} from "@/api/store/patient";
-import {  getUser,getUserAddr} from "@/api/store/user";
-import { getListUserCoupon } from "@/api/store/userCoupon";
-import userStorerDetails from "../components/userStorerDetails.vue";
-import userPatietDetails from "../components/userPatietDetails.vue";
-import userInquiryOrderDetails from "../components/userInquiryOrderDetails.vue";
-import userAddDetails from "../components/userAddDetails.vue";
-  export default {
-    name: "storedet",
-    props:["data"],
-     components: { userStorerDetails ,userInquiryOrderDetails,userPatietDetails,userAddDetails},
-    data() {
-      return {
-        patientInfo: process.env.VUE_APP_PATIENT_INFO,
-        addr:[],
-        patient:[],
-        userOptions: [],
-        statusOptions: [],
-        sexOptions: [],
-        pOptions: [],
-        item:null,
-        total: 0,
-        loading: true,
-       // 会员优惠券表格数据
-        userCouponList: [],
-        queryParams: {
-         pageNum: 1,
-         pageSize: 10,
-         userId: null,
-         couponId: null,
-        },
-        actName:"10",
-        businessTypeOptions:[],
-        couponStatusOptions:[],
-      }
-    },
-    created() {
-      this.getDicts("sys_user_status").then(response => {
-        this.userOptions = response.data;
-      });
-      this.getDicts("sys_company_status").then(response => {
-        this.statusOptions = response.data;
-      });
-      this.getDicts("sys_patient_status").then(response => {
-       this.pOptions = response.data;
-      });
-      this.getDicts("sys_patient_sex").then(response => {
-         this.sexOptions = response.data;
-       });
-       this.getDicts("sys_coupon_business_type").then(response => {
-         this.businessTypeOptions = response.data;
-       });
-
-       this.getDicts("sys_coupon_status").then(response => {
-         this.couponStatusOptions = response.data;
-       });
-    },
-    methods: {
-      handleClickX(tab, event) {
-       if(tab.name=="10"){
-         this.queryParams.status=null;
-       }else{
-         this.queryParams.status=tab.name;
-       }
-          this.queryParams.pageNum = 1;
-          this.getList();
-
-      },
-      getList() {
-        this.loading = true;
-        getListUserCoupon(this.queryParams).then(response => {
-          this.userCouponList = response.rows;
-          this.total = response.total;
-          this.loading = false;
-        });
-      },
-
-      getDetails(orderId) {
-          this.item=null;
-          getUser(orderId).then(response => {
-              this.item = response.data;
-              setTimeout(() => {
-                   this.$refs.userDetails.getUserDetails(orderId);
-              }, 1);
-              setTimeout(() => {
-                   this.$refs.InquiryDetails.getInquiryDetails(orderId,1);
-              }, 1);
-              setTimeout(() => {
-                   this.$refs.userPatietDetail.getPatList(orderId);
-              }, 1);
-              setTimeout(() => {
-                   this.$refs.userAddDetail.getAddList(orderId);
-              }, 1);
-
-          });
-          this.patient=null;
-          getPatientByUserId(orderId).then(response => {
-              this.patient = response.data;
-          });
-          getUserAddr(orderId).then(response => {
-              this.addr = response.data;
-          });
-           this.queryParams.userId=orderId;
-          this.getList();
-
+import userDetailsTemp from './userDetailsTemp.vue';
+import userBehavior from './userBehavior.vue';
+import userCoursePeriod from './userCoursePeriod.vue';
+export default {
+  name: "userDetailsByNew",
+  props:["data"],
+  components: { userDetailsTemp,userBehavior,userCoursePeriod},
+  data() {
+    return {
+      activeName: 'basic',
+      // 左侧遮罩层
+      leftLoading: true,
+      // 左侧查询参数
+      leftQueryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        hasNextPage: false,
+        scs: 'order_number(desc),training_camp_id(desc)',
+        trainingCampName: null,
+        userId:null,
       },
+      tabPosition: 'top',
     }
+  },
+  created() {
+
+  },
+  methods: {
+    handleClick(tab, event) {
+      console.log(tab, event);
+    },
+    getDetails(userId) {
+      this. activeName='basic';
+      setTimeout(() => {
+        this.$refs.userDetailsTemp.getDetails(userId);
+      }, 1);
+      setTimeout(() => {
+        this.$refs.userBehavior.getDetails(userId);
+      }, 1);
+      setTimeout(() => {
+        this.$refs.userCoursePeriod.getDetails(userId);
+      }, 1);
+    },
   }
+}
 </script>
 <style>
 
-  .contentx{
-      height: 100%;
-      background-color: #fff;
-      padding: 0px 20px 20px;
-
-
-      margin: 20px;
-  }
-  .el-descriptions-item__label.is-bordered-label{
-    font-weight: normal;
-  }
-  .el-descriptions-item__content {
-    max-width: 150px;
-    min-width: 100px;
-  }
-  .desct{
-      padding-top: 20px;
-      padding-bottom: 20px;
-      color: #524b4a;
-      font-weight: bold;
-    }
 </style>

+ 268 - 0
src/views/store/components/userDetailsTemp.vue

@@ -0,0 +1,268 @@
+<template>
+    <div style="background-color: #f0f2f5; padding-bottom: 20px; min-height: 100%; " >
+      <div class="contentx" v-if="item!=null" >
+        <div class="desct">
+          基本信息
+        </div>
+            <el-descriptions title="" :column="3" border>
+    
+              <el-descriptions-item label="会员id" >
+                    <span v-if="item!=null">{{item.userId}}</span>
+                </el-descriptions-item>
+                <el-descriptions-item label="用户昵称" >
+                    <span v-if="item!=null">{{item.nickName}}</span>
+                </el-descriptions-item>
+                <el-descriptions-item label="用户头像" >
+                     <el-image v-if="item.avatar!=null"
+                              style="width: 50px;"
+                              :src="item.avatar">
+                          </el-image>
+                </el-descriptions-item>
+                <el-descriptions-item label="手机号码" >
+                    <span v-if="item!=null">{{item.phone}}</span>
+                </el-descriptions-item>
+                <el-descriptions-item label="用户积分" >
+                    <span v-if="item!=null">{{item.integral}}</span>
+                </el-descriptions-item>
+                <el-descriptions-item label="状态" >
+                 <span v-if="item!=null">
+                       <dict-tag :options="userOptions" :value="item.status"/>
+                 </span>
+                </el-descriptions-item>
+    
+                <el-descriptions-item label="上级昵称" >
+                    <span v-if="item!=null">{{item.tuiName}}</span>
+                </el-descriptions-item>
+                <el-descriptions-item label="上级手机号码" >
+                    <span v-if="item!=null">{{item.tuiPhone}}</span>
+                </el-descriptions-item>
+                <el-descriptions-item label="推广员关联时间" >
+                    <span v-if="item!=null">{{item.tuiTime}}</span>
+                </el-descriptions-item>
+                <el-descriptions-item label="下级人数" >
+                    <span v-if="item!=null">{{item.tuiUserCount}}</span>
+                </el-descriptions-item>
+                <el-descriptions-item label="最后一次登录ip" >
+                    <span v-if="item!=null">{{item.lastIp}}</span>
+                </el-descriptions-item>
+                <el-descriptions-item label="余额" >
+                    <span v-if="item!=null">{{item.balance}}</span>
+                </el-descriptions-item>
+                <el-descriptions-item label="创建时间" >
+                    <span v-if="item!=null">{{item.createTime}}</span>
+                </el-descriptions-item>
+    
+                <el-descriptions-item label="更新时间" >
+                    <span v-if="item!=null">{{item.updateTime}}</span>
+                </el-descriptions-item>
+            </el-descriptions>
+    </div>
+    
+        <div class="contentx" v-if="item!=null">
+                <div class="desct"> 优惠劵领取信息</div>
+                <el-tabs type="card" v-model="actName" @tab-click="handleClickX">
+                  <el-tab-pane label="全部" name="10"></el-tab-pane>
+                  <el-tab-pane v-for="(item,index) in couponStatusOptions" :label="item.dictLabel" :name="item.dictValue"></el-tab-pane>
+                </el-tabs>
+                <el-table v-loading="loading" :data="userCouponList">
+                  <el-table-column label="优惠劵标题" align="center" prop="title" />
+                  <el-table-column label="券号" align="center" prop="couponCode" />
+                  <el-table-column label="会员昵称" align="center" prop="nickName" />
+                  <el-table-column label="会员电话" align="center" prop="phone" />
+                  <el-table-column label="关联订单ID" align="center" prop="businessId" />
+                  <el-table-column label="订单类型" align="center" prop="businessType">
+                    <template slot-scope="scope">
+                      <dict-tag :options="businessTypeOptions" :value="scope.row.businessType"/>
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="状态" align="center" prop="status">
+                    <template slot-scope="scope">
+                      <dict-tag :options="couponStatusOptions" :value="scope.row.status"/>
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="领取时间" align="center" prop="createTime" width="180"/>
+                  <el-table-column label="使用时间" align="center" prop="useTime" width="180"/>
+                </el-table>
+                <pagination
+                  v-show="total>0"
+                  :total="total"
+                  :page.sync="queryParams.pageNum"
+                  :limit.sync="queryParams.pageSize"
+                  @pagination="getList"
+                />
+            </div>
+    
+    
+    
+      <div class="contentx" v-if="item!=null">
+            <div class="desct">
+              <span v-if="patientInfo">
+                {{ patientInfo }}
+              </span>
+              <span v-else>
+                患者信息
+              </span>
+            </div>
+        <userPatietDetails  ref="userPatietDetail" />
+    
+      </div>
+    
+      <div class="contentx" v-if="item!=null" >
+     <div class="desct">
+          用户地址
+     </div>
+     <userAddDetails  ref="userAddDetail" />
+    
+       </div>
+      <div class="contentx" v-if="item!=null" >
+     <div class="desct">
+          用户药品订单
+     </div>
+        <userStorerDetails  ref="userDetails" />
+       </div>
+    <div class="contentx" v-if="item!=null" >
+     <div class="desct">
+          用户问诊订单
+     </div>
+        <userInquiryOrderDetails  ref="InquiryDetails" />
+       </div>
+     </div>
+    </template>
+    
+    
+    
+    <script>
+    import { listPatient, getPatient, delPatient, addPatient, updatePatient, exportPatient ,getPatientByUserId} from "@/api/store/patient";
+    import { listUser, getUser, delUser, addUser, updateUser, exportUser ,getUserAddr} from "@/api/store/user";
+    import { getListUserCoupon } from "@/api/store/userCoupon";
+    import userStorerDetails from "../components/userStorerDetails.vue";
+    import userPatietDetails from "../components/userPatietDetails.vue";
+    import userInquiryOrderDetails from "../components/userInquiryOrderDetails.vue";
+    import userAddDetails from "../components/userAddDetails.vue";
+      export default {
+        name: "storedet",
+        props:["data"],
+         components: { userStorerDetails ,userInquiryOrderDetails,userPatietDetails,userAddDetails},
+        data() {
+          return {
+            patientInfo: process.env.VUE_APP_PATIENT_INFO,
+            addr:[],
+            patient:[],
+            userOptions: [],
+            statusOptions: [],
+            sexOptions: [],
+            pOptions: [],
+            item:null,
+            total: 0,
+            loading: true,
+           // 会员优惠券表格数据
+            userCouponList: [],
+            queryParams: {
+             pageNum: 1,
+             pageSize: 10,
+             userId: null,
+             couponId: null,
+            },
+            actName:"10",
+            businessTypeOptions:[],
+            couponStatusOptions:[],
+          }
+        },
+        created() {
+          this.getDicts("sys_user_status").then(response => {
+            this.userOptions = response.data;
+          });
+          this.getDicts("sys_company_status").then(response => {
+            this.statusOptions = response.data;
+          });
+          this.getDicts("sys_patient_status").then(response => {
+           this.pOptions = response.data;
+          });
+          this.getDicts("sys_patient_sex").then(response => {
+             this.sexOptions = response.data;
+           });
+           this.getDicts("sys_coupon_business_type").then(response => {
+             this.businessTypeOptions = response.data;
+           });
+    
+           this.getDicts("sys_coupon_status").then(response => {
+             this.couponStatusOptions = response.data;
+           });
+        },
+        methods: {
+          handleClickX(tab, event) {
+           if(tab.name=="10"){
+             this.queryParams.status=null;
+           }else{
+             this.queryParams.status=tab.name;
+           }
+              this.queryParams.pageNum = 1;
+              this.getList();
+    
+          },
+          getList() {
+            this.loading = true;
+            getListUserCoupon(this.queryParams).then(response => {
+              this.userCouponList = response.rows;
+              this.total = response.total;
+              this.loading = false;
+            });
+          },
+    
+          getDetails(orderId) {
+              this.item=null;
+              getUser(orderId).then(response => {
+                  this.item = response.data;
+                  setTimeout(() => {
+                       this.$refs.userDetails.getUserDetails(orderId);
+                  }, 1);
+                  setTimeout(() => {
+                       this.$refs.InquiryDetails.getInquiryDetails(orderId,1);
+                  }, 1);
+                  setTimeout(() => {
+                       this.$refs.userPatietDetail.getPatList(orderId);
+                  }, 1);
+                  setTimeout(() => {
+                       this.$refs.userAddDetail.getAddList(orderId);
+                  }, 1);
+    
+              });
+              this.patient=null;
+              getPatientByUserId(orderId).then(response => {
+                  this.patient = response.data;
+              });
+              getUserAddr(orderId).then(response => {
+                  this.addr = response.data;
+              });
+               this.queryParams.userId=orderId;
+              this.getList();
+    
+          },
+        }
+      }
+    </script>
+    <style>
+    
+      .contentx{
+          height: 100%;
+          background-color: #fff;
+          padding: 0px 20px 20px;
+    
+    
+          margin: 20px;
+      }
+      .el-descriptions-item__label.is-bordered-label{
+        font-weight: normal;
+      }
+      .el-descriptions-item__content {
+        max-width: 150px;
+        min-width: 100px;
+      }
+      .desct{
+          padding-top: 20px;
+          padding-bottom: 20px;
+          color: #524b4a;
+          font-weight: bold;
+        }
+    </style>
+    

+ 248 - 0
src/views/store/components/userStaticAll.vue

@@ -0,0 +1,248 @@
+<template>
+  <div class="statistics-container">
+    <!-- 营期课程选择 -->
+    <div class="fixed-header">
+      <!-- 统计数据展示 -->
+      <el-row :gutter="20" class="statistics-row">
+        <el-col :span="4">
+          <div class="statistics-item">
+            <div class="statistics-title">到课数</div>
+            <div class="statistics-value">{{ statistics.courseCompleteNum || 0 }}次</div>
+          </div>
+        </el-col>
+        <el-col :span="4">
+          <div class="statistics-item">
+            <div class="statistics-title">总学习时长</div>
+            <div class="statistics-value">{{ statistics.courseWatchNum || 0 }}分钟</div>
+          </div>
+        </el-col>
+        <el-col :span="4">
+          <div class="statistics-item">
+            <div class="statistics-title">正确答题数</div>
+            <div class="statistics-value">{{ statistics.correctAnswerNum || 0 }}次</div>
+          </div>
+        </el-col>
+        <el-col :span="4">
+          <div class="statistics-item">
+            <div class="statistics-title">获得红包数</div>
+            <div class="statistics-value">{{ statistics.redPacketCount || 0 }}次</div>
+          </div>
+        </el-col>
+        <el-col :span="4">
+          <div class="statistics-item">
+            <div class="statistics-title">红包总金额</div>
+            <div class="statistics-value">{{ statistics.redPacketAmount || 0 }}元</div>
+          </div>
+        </el-col>
+      </el-row>
+    </div>
+
+    <!-- 列表统计展示 -->
+    <div class="table-wrapper">
+      <el-table v-loading="loading" :data="list">
+        <el-table-column type="index" label="序号" width="50" align="center" fixed/>
+        <el-table-column prop="courseName" label="课程名称" align="center" min-width="100" fixed/>
+        <el-table-column prop="videoName" label="课节名称" align="center" min-width="100" fixed/>
+        <el-table-column label="记录类型" align="center" prop="logType">
+          <template slot-scope="scope">
+            <el-tag prop="type" v-for="(item, index) in typeOptions" v-if="scope.row.logType==item.dictValue">{{item.dictLabel}}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="duration" label="播放时长" align="center" min-width="100"/>
+        <el-table-column prop="finishTime" label="完课时间" align="center" min-width="120"/>
+      </el-table>
+
+      <!-- 分页 -->
+      <div class="custom-pagination-container">
+        <pagination
+          v-show="total > 0"
+          :total="total"
+          :page.sync="queryParams.pageNum"
+          :limit.sync="queryParams.pageSize"
+          @pagination="getCountList"
+        />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import {periodCourseStatisticCount} from "@/api/course/userCoursePeriod";
+import { listBytrainingCampId } from "@/api/course/qw/courseWatchLog";
+
+
+export default {
+  name: "userStaticAll",
+  props: {
+    periodId: {
+      type: [String, Number],
+      default: ''
+    },
+    active: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    return {
+      typeOptions: [],
+      // 遮罩层
+      loading: false,
+      // 总条数
+      total: 0,
+      // 统计数据
+      statistics: {
+        courseCompleteNum: 0,
+        courseWatchNum: 0,
+        redPacketCount: 0,
+        correctAnswerNum: 0,
+        redPacketAmount: 0
+      },
+      // 列表数据
+      list: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        // videoId: '',
+        periodId: '',
+        trainingCampId:null,
+        userId:null,
+      },
+    };
+  },
+  created() {
+    this.getDicts("sys_course_watch_log_type").then((response) => {
+      this.typeOptions = response.data;
+    });
+  },
+  methods: {
+    getDetails(camp,userid) {
+    this.queryParams.trainingCampId = camp.trainingCampId;
+      this.camp = camp;
+      this.userId=userid;
+      this.queryParams.userId=userid;
+      this.user=null;
+      if (this.queryParams.trainingCampId){
+        this.getCountList();
+        this.calculateTotalStatistics();
+      }
+
+    },
+    /** 获取列表数据 */
+    getCountList() {
+      this.loading = true;
+      if (!this.queryParams.trainingCampId || !this.queryParams.userId){
+        return
+      }
+      listBytrainingCampId(this.queryParams).then(response => {
+        if (response.code === 200) {
+          // 设置列表数据
+          this.list = response.rows;
+          this.total = response.total || 0;
+
+
+        } else {
+          this.$message.error(response.msg || '获取数据失败');
+        }
+        this.loading = false;
+      }).catch(error => {
+        console.error('获取数据失败:', error);
+        this.$message.error('获取数据失败');
+        this.loading = false;
+      });
+    },
+    /** 计算总统计数据 */
+    calculateTotalStatistics() {
+
+      console.log("this.queryParams:",this.queryParams)
+      periodCourseStatisticCount(this.queryParams).then(response => {
+        if (response.code === 200) {
+          // 设置列表数据
+          this.statistics.courseCompleteNum = response.data.courseCompleteNum || 0;
+          this.statistics.courseWatchNum = response.data.courseWatchNum || 0;
+          this.statistics.courseWatchTimes = response.data.courseWatchTimes || 0;
+          this.statistics.redPacketCount = response.data.redPacketCount || 0;
+          this.statistics.correctAnswerNum = response.data.correctAnswerNum || 0;
+          this.statistics.redPacketAmount = response.data.redPacketAmount || 0;
+
+        } else {
+          this.$message.error(response.msg || '获取数据失败');
+        }
+        this.loading = false;
+      }).catch(error => {
+        console.error('获取数据失败:', error);
+        this.$message.error('获取数据失败');
+        this.loading = false;
+      });
+
+    }
+  }
+};
+</script>
+
+<style scoped>
+.statistics-container {
+  height: 100%;
+  overflow: hidden;
+  position: relative;
+}
+
+.fixed-header {
+  position: sticky;
+  top: 0;
+  z-index: 10;
+  background-color: #fff;
+  padding: 10px 0;
+  border-bottom: 1px solid #EBEEF5;
+}
+
+.table-wrapper {
+  height: calc(100% - 220px);
+  overflow: visible;
+  position: relative;
+}
+
+.custom-pagination-container {
+  padding: 10px 0 12px 0;
+  text-align: right;
+  background-color: #fff;
+  position: relative;
+  z-index: 1;
+}
+
+/* 覆盖原有的pagination-container样式 */
+:deep(.pagination-container) {
+  height: auto !important;
+  margin-bottom: 0 !important;
+  margin-top: 0 !important;
+  padding: 0 !important;
+}
+
+.statistics-row {
+  margin: 20px 0;
+}
+
+.statistics-item {
+  background-color: #f5f7fa;
+  border-radius: 4px;
+  padding: 15px;
+  text-align: center;
+  height: 100px;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+}
+
+.statistics-title {
+  font-size: 14px;
+  color: #606266;
+  margin-bottom: 10px;
+}
+
+.statistics-value {
+  font-size: 20px;
+  font-weight: bold;
+  color: #303133;
+}
+</style>

+ 75 - 24
src/views/store/inquiryOrder/list.vue

@@ -93,7 +93,7 @@
       <el-form-item label="支付时间" prop="payTimeRange">
         <el-date-picker style="width:205.4px" clearable size="small" v-model="payTimeRange" type="daterange" value-format="yyyy-MM-dd"  start-placeholder="开始日期"  end-placeholder="结束日期"> </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>
@@ -190,15 +190,24 @@
 
       </el-table-column>
       <el-table-column label="科室名称" align="center" prop="deptName" />
-      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="100px">
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="150px">
         <template slot-scope="scope">
-       <el-button
-                  size="mini"
-                  type="text"
-                  @click="handledetails(scope.row)"
-                   icon="el-icon-share"
-                >详情
-      </el-button>
+         <el-button
+              size="mini"
+              type="text"
+              @click="handledetails(scope.row)"
+               icon="el-icon-share"
+            >详情
+        </el-button>
+        <el-button
+          v-if="scope.row.isPay==0"
+          size="mini"
+          type="text"
+          @click="handlePayOrder(scope.row)"
+          icon="el-icon-coin"
+          v-hasPermi="['store:inquiryOrder:wxaCodeInquiryOrder']"
+        >生成付款二维码
+        </el-button>
         </template>
       </el-table-column>
     </el-table>
@@ -218,12 +227,20 @@
      <inquiryOrderDetails  ref="Details" />
    </el-drawer>
 
+    <el-dialog :title="payInquiryOrder.title" v-if="payInquiryOrder.open"  :visible.sync="payInquiryOrder.open" width="450px"  append-to-body>
+      <div style="padding-bottom:15px;" >
+        <img :src="codeImage" width="400px">
+      </div>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="downloadImage(codeImage, payInquiryOrder.name+'.png')">下载二维码</el-button>
+      </div>
+    </el-dialog>
 
   </div>
 </template>
 
 <script>
-import { listinquiryOrder, getinquiryOrder, delinquiryOrder, addinquiryOrder, updateinquiryOrder, exportinquiryOrder,exportMessageFeedback } from "@/api/store/inquiryOrder";
+import { listinquiryOrder, getinquiryOrder, delinquiryOrder, addinquiryOrder, updateinquiryOrder, exportinquiryOrder,exportMessageFeedback,getWxaCodeInquiryOrderUnLimit } from "@/api/store/inquiryOrder";
 import inquiryOrderDetails from '../components/inquiryOrderDetails.vue';
 import { treeselect } from "@/api/company/companyDept";
 import Treeselect from "@riophae/vue-treeselect";
@@ -272,6 +289,12 @@ export default {
       title: "",
       // 是否显示弹出层
       open: false,
+      codeImage:null,
+      payInquiryOrder:{
+        open:false,
+        title:"付款二维码",
+        name:null,
+      },
       // 查询参数
       queryParams: {
         pageNum: 1,
@@ -355,20 +378,48 @@ export default {
       this.queryParams.deptId = data.id;
       this.getList();
     },
-  handleClickX(tab, event) {
-   if(tab.name=="10"){
-     this.queryParams.status=null;
-   }else{
-     this.queryParams.status=tab.name;
-   }
-    this.handleQuery();
-  },
-        handledetails(row){
-            this.show.open=true;
-            setTimeout(() => {
-                 this.$refs.Details.getDetails(row.orderId);
-            }, 1);
-        },
+    handleClickX(tab, event) {
+     if(tab.name=="10"){
+       this.queryParams.status=null;
+     }else{
+       this.queryParams.status=tab.name;
+     }
+      this.handleQuery();
+    },
+    handledetails(row){
+        this.show.open=true;
+        setTimeout(() => {
+             this.$refs.Details.getDetails(row.orderId);
+        }, 1);
+    },
+
+    handlePayOrder(row){
+
+      let loadingRock = this.$loading({
+        lock: true,
+        text: '生成二维码中~~请不要刷新页面!!',
+        spinner: 'el-icon-loading',
+        background: 'rgba(0, 0, 0, 0.7)'
+      });
+
+      getWxaCodeInquiryOrderUnLimit(row.orderId).then(response => {
+        this.codeImage='data:image/jpeg;base64,'+response.data
+        this.payInquiryOrder.open=true;
+        this.payInquiryOrder.name=row.title;
+        loadingRock.close();
+      }).finally(res=>{
+        loadingRock.close();
+      })
+    },
+
+    downloadImage(imageSrc, fileName) {
+      const link = document.createElement('a');
+      link.href = imageSrc;
+      link.download = fileName || '付款二维码.png';
+      document.body.appendChild(link);
+      link.click();
+      document.body.removeChild(link);
+    },
     /** 查询问诊订单列表 */
     getList() {
       this.loading = true;

+ 300 - 33
src/views/store/inquiryOrder/myList.vue

@@ -98,7 +98,14 @@
     </el-form>
 
     <el-row :gutter="10" class="mb8">
-
+      <el-col :span="1.5">
+          <el-button
+            type="primary"
+            icon="el-icon-plus"
+            size="mini"
+            @click="handleAdd"
+          >创建订单</el-button>
+        </el-col>
       <el-col :span="1.5">
         <el-button
           type="warning"
@@ -176,15 +183,24 @@
 
       </el-table-column>
       <el-table-column label="科室名称" align="center" prop="deptName" />
-      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="100px">
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="150px">
         <template slot-scope="scope">
-       <el-button
-                  size="mini"
-                  type="text"
-                  @click="handledetails(scope.row)"
-                   icon="el-icon-share"
-                >详情
-      </el-button>
+           <el-button
+                      size="mini"
+                      type="text"
+                      @click="handledetails(scope.row)"
+                       icon="el-icon-share"
+                    >详情
+          </el-button>
+          <el-button
+            v-if="scope.row.isPay==0"
+            size="mini"
+            type="text"
+            @click="handlePayOrder(scope.row)"
+            icon="el-icon-coin"
+            v-hasPermi="['store:inquiryOrder:wxaCodeInquiryOrder']"
+          >生成付款二维码
+          </el-button>
         </template>
       </el-table-column>
     </el-table>
@@ -204,6 +220,135 @@
      <inquiryOrderDetails  ref="Details" />
    </el-drawer>
 
+  <el-dialog :title="title" v-if="open" :visible.sync="open" width="1000px" append-to-body :close-on-click-modal ="false">
+    <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+      <el-form-item label="会员信息" prop="userId">
+        <el-row>
+          <el-col >
+            <el-input placeholder="请输入会员手机号" style="width:240px;cursor:pointer" v-model="phone"/>
+            <el-button plain style="margin-left:10px;"    @click="searchUser()">查询</el-button>
+            <el-button plain style="margin-left:10px;" icon="el-icon-plus"  type="primary" @click="handleAddUser()">添加会员</el-button>
+          </el-col>
+        </el-row>
+          <el-table border style="margin-top:5px;"  v-loading="userloading" :data="users">
+            <el-table-column label="ID" align="center" prop="userId" />
+            <el-table-column label="会员头像" align="center" width="80">
+              <template slot-scope="scope">
+                <el-popover placement="right" title="" trigger="hover">
+                  <img slot="reference" :src="scope.row.avatar" width="50" >
+                  <img :src="scope.row.avatar" style="max-width: 120px;">
+                </el-popover>
+              </template>
+            </el-table-column>
+            <el-table-column label="昵称" align="center" prop="nickName" />
+            <el-table-column label="手机号" align="center" prop="phone" />
+            <el-table-column label="状态" align="center" prop="status" >
+              <template slot-scope="scope">
+                <el-tag prop="status" v-for="(item, index) in userStatusOptions"  v-if="scope.row.status==item.dictValue">{{item.dictLabel}}</el-tag>
+              </template>
+            </el-table-column>
+          </el-table>
+    </el-form-item>
+
+      <el-form-item label="就诊人信息" prop="patientId">
+        <el-row>
+          <el-col>
+            <el-button plain  type="primary" icon="el-icon-plus"  @click="handleAddUserPatient()">添加就诊人</el-button>
+          </el-col>
+        </el-row>
+        <el-radio-group v-model="form.patientId" style="width:100%">
+        <el-table border  style="margin-top:5px;"  v-loading="patientLoading" :data="patientList">
+          <el-table-column label="ID" align="center"  >
+            <template slot-scope="scope">
+              <el-radio :label="scope.row.patientId"></el-radio>
+            </template>
+          </el-table-column>
+          <el-table-column label="患者姓名" align="center" prop="patientName" />
+          <el-table-column label="所属会员" align="center" width="150px">
+           <template slot-scope="scope">
+             <div v-if="scope.row.nickName!=null"> {{scope.row.nickName}}-{{scope.row.phone}}</div>
+           </template>
+          </el-table-column>
+          <el-table-column label="身份证号" align="center" prop="idCard" width="170px"/>
+          <el-table-column label="出生年月" align="center" prop="birthday" width="180">
+            <template slot-scope="scope">
+              <span>{{ parseTime(scope.row.birthday, '{y}-{m}-{d}') }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="性别" align="center" prop="sex" >
+            <template slot-scope="scope">
+                 <dict-tag :options="sexOptions" :value="scope.row.sex"/>
+            </template>
+          </el-table-column>
+          <el-table-column label="体重G" align="center" prop="weight" />
+          <el-table-column label="手机号" align="center" prop="mobile" />
+          <el-table-column label="状态" align="center" prop="status" >
+             <template slot-scope="scope">
+                  <dict-tag :options="userStatusOptions" :value="scope.row.status"/>
+             </template>
+          </el-table-column>
+          <el-table-column label="与本人关系" align="center" prop="relation" />
+          <el-table-column label="肝功能是否异常" align="center" prop="liverUnusual" />
+          <el-table-column label="肾功能是否异常" align="center" prop="renalUnusual" />
+          <el-table-column label="过敏史" align="center" prop="historyAllergic" />
+          <el-table-column label="家族病史" align="center" prop="familyMedHistory" />
+          <el-table-column label="个人病史" align="center" prop="selfMedHistory" />
+        </el-table>
+        </el-radio-group>
+      </el-form-item>
+
+      <el-form-item label="销售备注" prop="companyUserRemark">
+        <el-input  type="textarea" rows="2" v-model="form.companyUserRemark" placeholder="" />
+      </el-form-item>
+      <el-form-item label="订单类型" prop="orderType">
+        <el-select v-model="form.orderType" placeholder="请选择" clearable size="small">
+                <el-option
+                  v-for="dict in inquiryOrderOptions"
+                  :key="dict.dictValue"
+                  :label="dict.dictLabel"
+                  :value="dict.dictValue"
+                />
+              </el-select>
+      </el-form-item>
+      <el-form-item label="问诊子类型" prop="inquirySubType">
+         <el-select v-model="form.inquirySubType" placeholder="请选择" clearable size="small">
+                 <el-option
+                   v-for="dict in inquirySubTypeOptions"
+                   :key="dict.dictValue"
+                   :label="dict.dictLabel"
+                   :value="dict.dictValue"
+                 />
+               </el-select>
+      </el-form-item>
+      <el-form-item label="咨询的问题" prop="title">
+        <el-input  type="textarea" rows="2" v-model="form.title" placeholder="" />
+      </el-form-item>
+      <el-form-item label="检测报告或者患处照片" prop="reportImages">
+        <ImageUpload v-model="form.reportImages"  type="image" :num="1" :width="150" :height="150" />
+      </el-form-item>
+
+    </el-form>
+    <div slot="footer" class="dialog-footer">
+      <el-button type="primary" @click="submitForm">确 定</el-button>
+      <el-button @click="cancel">取 消</el-button>
+    </div>
+  </el-dialog>
+    <el-dialog :title="userPatient.title" v-if="userPatient.open"  :visible.sync="userPatient.open" width="800px" append-to-body>
+        <add-patient ref="addPatient"   @addUserPatient="addUserPatient" />
+    </el-dialog>
+    <el-dialog :title="user.title" v-if="user.open"  :visible.sync="user.open" width="500px" append-to-body>
+       <add-user @addUser="addUser" />
+    </el-dialog>
+
+    <el-dialog :title="payInquiryOrder.title" v-if="payInquiryOrder.open"  :visible.sync="payInquiryOrder.open" width="450px"  append-to-body>
+      <div style="padding-bottom:15px;" >
+        <img :src="codeImage" width="400px">
+      </div>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="downloadImage(codeImage, payInquiryOrder.name+'.png')">下载二维码</el-button>
+      </div>
+    </el-dialog>
+
 
   </div>
 </template>
@@ -212,10 +357,15 @@
 import { listinquiryOrder,myExport,getinquiryOrder,myList, delinquiryOrder, addinquiryOrder, updateinquiryOrder, exportinquiryOrder } from "@/api/store/inquiryOrder";
 import inquiryOrderDetails from '../components/inquiryOrderDetails.vue';
 import { getTask } from "@/api/common";
-
+import { userList } from "@/api/store/user";
+import addUser from "../components/addUser";
+import addPatient from "../components/addPatient";
+import { listPatient } from "@/api/store/patient";
+import {getWxaCodeInquiryOrderUnLimit} from "../../../api/store/inquiryOrder";
+import ImageUpload from "@/views/qw/sop/ImageUpload";
 export default {
   name: "ofast",
-  components: { inquiryOrderDetails },
+  components: { inquiryOrderDetails,addUser,addPatient,ImageUpload },
   data() {
     return {
       show:{
@@ -228,6 +378,7 @@ export default {
        startTime:null,
       // 遮罩层
       loading: true,
+
       // 导出遮罩层
       exportLoading: false,
       // 选中数组
@@ -242,6 +393,7 @@ export default {
       total: 0,
       // 问诊订单表格数据
       inquiryOrderList: [],
+      userStatusOptions:[],
       // 弹出层标题
       title: "",
       // 是否显示弹出层
@@ -276,12 +428,48 @@ export default {
         patientName:null,
         sendName:null,
         phone:null,
+        reportImages:null,
       },
+      phone:null,
       // 表单参数
       form: {},
+      user:{
+        open:false,
+        title:"创建会员"
+      },
+      userPatient:{
+        open:false,
+        title:"创建病人"
+      },
+
+      codeImage:null,
+      payInquiryOrder:{
+        open:false,
+        title:"付款二维码",
+        name:null,
+      },
+      patientList:[],
+      userloading :false,
+      users:[],
       // 表单校验
       rules: {
+        userId: [
+          { required: true, message: "会员信息不能为空" }
+        ],
+        addressId: [
+          { required: true, message: "收货信息不能为空" }
+        ],
+        packageId: [
+          { required: true, message: "套餐包不能为空" }
+        ],
+        payType: [
+          { required: true, message: "支付方式不能为空" }
+        ],
+        patientId: [
+          { required: true, message: "就诊人不能为空" }
+        ],
       },
+      patientLoading:false,
       inquiryStatusOptions:[],
       inquiryTypeOptions:[],
       inquiryPayOptions:[],
@@ -300,6 +488,9 @@ export default {
       this.getDicts("sys_inquiry_pay").then(response => {
           this.inquiryPayOptions = response.data;
         });
+      this.getDicts("sys_company_status").then(response => {
+        this.userStatusOptions = response.data;
+      });
       this.getDicts("sys_inquiry_order_type").then(response => {
           this.inquiryOrderOptions = response.data;
         });
@@ -312,20 +503,48 @@ export default {
     this.getList();
   },
   methods: {
-  handleClickX(tab, event) {
-   if(tab.name=="10"){
-     this.queryParams.status=null;
-   }else{
-     this.queryParams.status=tab.name;
-   }
-    this.handleQuery();
-  },
-        handledetails(row){
-            this.show.open=true;
-            setTimeout(() => {
-                 this.$refs.Details.getDetails(row.orderId);
-            }, 1);
-        },
+    handleClickX(tab, event) {
+     if(tab.name=="10"){
+       this.queryParams.status=null;
+     }else{
+       this.queryParams.status=tab.name;
+     }
+      this.handleQuery();
+    },
+    handledetails(row){
+        this.show.open=true;
+        setTimeout(() => {
+             this.$refs.Details.getDetails(row.orderId);
+        }, 1);
+    },
+
+    handlePayOrder(row){
+
+      let loadingRock = this.$loading({
+        lock: true,
+        text: '生成二维码中~~请不要刷新页面!!',
+        spinner: 'el-icon-loading',
+        background: 'rgba(0, 0, 0, 0.7)'
+      });
+
+      getWxaCodeInquiryOrderUnLimit(row.orderId).then(response => {
+        this.codeImage='data:image/jpeg;base64,'+response.data
+        this.payInquiryOrder.open=true;
+        this.payInquiryOrder.name=row.title;
+        loadingRock.close();
+      }).finally(res=>{
+        loadingRock.close();
+      })
+    },
+
+    downloadImage(imageSrc, fileName) {
+      const link = document.createElement('a');
+      link.href = imageSrc;
+      link.download = fileName || '付款二维码.png';
+      document.body.appendChild(link);
+      link.click();
+      document.body.removeChild(link);
+    },
     /** 查询问诊订单列表 */
     getList() {
       this.loading = true;
@@ -347,11 +566,67 @@ export default {
         this.loading = false;
       });
     },
+    handleAddUserPatient(){
+      if(this.form.userId==null){
+        this.msgError("请选择会员");
+        return;
+      }
+      this.userPatient.open=true;
+      setTimeout(() => {
+        this.$refs.addPatient.init(this.form.userId);
+      }, 500);
+    },
+    addUserPatient(){
+      this.userPatient.open=false;
+      //获取地址
+      this.getPatentList(this.form.userId);
+    },
     // 取消按钮
     cancel() {
       this.open = false;
       this.reset();
     },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加套餐订单";
+    },
+    addUser(phone){
+      this.user.open=false;
+      this.phone=phone;
+      this.searchUser();
+    },
+    getPatentList(userId){
+      var data={userId:userId}
+      this.patientLoading = true;
+      this.patientList=[];
+      listPatient(data).then(response => {
+        this.patientList = response.rows;
+        this.patientLoading = false;
+      });
+    },
+    searchUser(){
+      if(this.phone==null||this.phone==""){
+        return;
+      }
+      var data={phone:this.phone}
+      this.userloading = true;
+      this.users=[];
+      this.address=[];
+      userList(data).then(response => {
+        this.users = response.rows;
+        this.userloading = false;
+        if(this.users!=null&&this.users.length==1){
+          this.form.userId=this.users[0].userId;
+
+          this.getPatentList(this.form.userId)
+        }
+      });
+    },
+    handleAddUser(){
+      this.user.open=true;
+    },
     // 表单重置
     reset() {
       this.form = {
@@ -435,19 +710,11 @@ export default {
     submitForm() {
       this.$refs["form"].validate(valid => {
         if (valid) {
-          if (this.form.orderId != null) {
-            updateinquiryOrder(this.form).then(response => {
-              this.msgSuccess("修改成功");
-              this.open = false;
-              this.getList();
-            });
-          } else {
             addinquiryOrder(this.form).then(response => {
               this.msgSuccess("新增成功");
               this.open = false;
               this.getList();
             });
-          }
         }
       });
     },

+ 1 - 1
src/views/store/inquiryOrderReport/index.vue

@@ -484,7 +484,7 @@ export default {
       }
 
     },
-   
+
     selectOK(rows){
 
       if(this.form.inquiryResult==null){

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio