Browse Source

会员详情

ct 2 days ago
parent
commit
3cccec2fb5

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

@@ -199,3 +199,197 @@ export function periodList(data) {
     data: data
     data: data
   })
   })
 }
 }
+
+import request from '@/utils/request'
+
+// 查询会员营期列表
+export function listPeriod(query) {
+  return request({
+    url: '/course/period/list',
+    method: 'get',
+    params: query
+  })
+}
+// 查询会员营期列表
+export function getDays(query) {
+  return request({
+    url: '/course/period/getDays',
+    method: 'get',
+    params: query
+  })
+}
+
+// 自定义查询主列表分页
+export function pagePeriod(data) {
+  return request({
+    url: '/course/period/page',
+    method: 'post',
+    data: data
+  })
+}
+
+// 查询会员营期详细
+export function getPeriod(periodId) {
+  return request({
+    url: '/course/period/' + periodId,
+    method: 'get'
+  })
+}
+
+// 新增会员营期
+export function addPeriod(data) {
+  return request({
+    url: '/course/period',
+    method: 'post',
+    data: data
+  })
+}
+
+// 新增会员营期
+export function addCourse(data) {
+  return request({
+    url: '/course/period/addCourse',
+    method: 'post',
+    data: data
+  })
+}
+
+// 新增会员营期
+export function updateCourseTime(data) {
+  return request({
+    url: '/course/period/updateCourseTime',
+    method: 'post',
+    data: data
+  })
+}
+// 新增会员营期
+export function updateCourseDate(data) {
+  return request({
+    url: '/course/period/updateCourseDate',
+    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
+  })
+}
+
+// 删除会员营期
+export function delPeriod(periodId) {
+  return request({
+    url: '/course/period/' + periodId,
+    method: 'delete'
+  })
+}
+
+// 导出会员营期
+export function exportPeriod(query) {
+  return request({
+    url: '/course/period/export',
+    method: 'get',
+    params: query
+  })
+}
+
+// 根据营期id获取公司红包金额列表
+export function getPeriodCompanyList(query) {
+  return request({
+    url: '/course/period/companyList',
+    method: 'get',
+    params: query
+  })
+}
+
+// 按照课程批量设置红包金额
+export function batchSaveRedPacket(data) {
+  return request({
+    url: '/course/period/batchRedPacket',
+    method: 'post',
+    data: data
+  })
+}
+
+// 获取设置红包金额列表展示
+export function getPeriodRedPacketList(query) {
+  return request({
+    url: '/course/period/redPacketList',
+    method: 'get',
+    params: query
+  })
+}
+
+// 按照营期批量设置红包金额
+export function batchSaveRedPacketByPeriod(data) {
+  return request({
+    url: '/course/period/batchRedPacket/byPeriod',
+    method: 'post',
+    data: data
+  })
+}
+
+// 按照营期、按课程统计
+export function periodCountSelect(data) {
+  return request({
+    url: '/course/period/periodCount',
+    method: 'post',
+    data: data
+  })
+}
+
+export function periodCourseStatisticCount(data) {
+  return request({
+    url: '/course/period/periodCourseStatisticCount',
+    method: 'post',
+    data: data
+  })
+}
+
+// 获取营期选项列表
+export function getPeriodListLikeName(query) {
+  return request({
+    url: '/course/period/getPeriodListLikeName',
+    method: 'get',
+    params: query
+  })
+}
+
+// 营期课程上移下移
+export function periodCourseMove(data) {
+  return request({
+    url: '/course/period/courseMove',
+    method: 'put',
+    params: data
+  })
+}
+
+// 结束营期
+export function closePeriod(query) {
+  return request({
+    url: '/course/period/closePeriod',
+    method: 'post',
+    params: query
+  })
+}
+
+// 查询该会员下该训练营下操作过的所有营期
+export function periodList(data) {
+  return request({
+    url: '/course/period/periodlist',
+    method: 'post',
+    data: data
+  })
+}

+ 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'
+  })
+}

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

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

File diff suppressed because it is too large
+ 585 - 383
src/views/course/userCoursePeriod/index.vue


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

@@ -11,7 +11,6 @@
               size="mini"
               size="mini"
               type="text"
               type="text"
               @click="handleInputAmount(scope.row)"
               @click="handleInputAmount(scope.row)"
-              v-hasPermi="['course:period:setCourseRedPacket']"
             >设置红包</el-button>
             >设置红包</el-button>
           </template>
           </template>
         </el-table-column>
         </el-table-column>
@@ -27,12 +26,11 @@
         <el-table-column label="课程" prop="courseName" align="center" />
         <el-table-column label="课程" prop="courseName" align="center" />
         <el-table-column label="小节" prop="videoName" align="center" />
         <el-table-column label="小节" prop="videoName" align="center" />
         <el-table-column label="营期日期" prop="dayDate" 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">
           <template slot-scope="scope">
             <el-input-number
             <el-input-number
               v-model="scope.row.amount"
               v-model="scope.row.amount"
-              :min="0.0"
-              :max="0.3"
+              :min="0"
               :precision="2"
               :precision="2"
               :step="0.01"
               :step="0.01"
               size="small"
               size="small"
@@ -52,7 +50,7 @@
 </template>
 </template>
 
 
 <script>
 <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";
 import redPacket from "@/views/course/userCoursePeriod/redPacket.vue";
 
 
 export default {
 export default {
@@ -129,38 +127,29 @@ export default {
         periodId: this.periodId,
         periodId: this.periodId,
         companyId: this.currentCompany.companyId
         companyId: this.currentCompany.companyId
       }).then(response => {
       }).then(response => {
-        console.log("-----------",JSON.stringify(response.data))
         this.redPacketList = (response.data || []).map(item => ({
         this.redPacketList = (response.data || []).map(item => ({
           ...item,
           ...item,
-          // amount: item.amount || 0.1
-          amount:item.amount ?? 0.1
+          amount: item.amount || 0
         }));
         }));
       });
       });
     },
     },
     // 保存红包金额
     // 保存红包金额
     handleSave() {
     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;
         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 => {
       batchSaveRedPacket(saveData).then(response => {
         if (response.code === 200) {
         if (response.code === 200) {
           this.$message.success('保存成功');
           this.$message.success('保存成功');

+ 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>
 <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>
     </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>
-
-  <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>
 <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>
 </script>
 <style>
 <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>
 </style>

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

@@ -0,0 +1,273 @@
+<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>
+            <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>

Some files were not shown because too many files changed in this diff