Forráskód Böngészése

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

dongdong.xiang 2 hónapja
szülő
commit
84666e8e09

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

@@ -99,3 +99,12 @@ export function crmDayCountlist(query) {
     params: query
   })
 }
+
+// 获取公司选项列表
+export function getCompanyListLikeName(query) {
+  return request({
+    url: '/company/company/getCompanyListLikeName',
+    method: 'get',
+    params: query
+  })
+}

+ 9 - 0
src/api/company/companyUser.js

@@ -94,3 +94,12 @@ export function changeCompanyUser(data, params) {
     params: params
   })
 }
+
+// 获取销售选项列表
+export function getCompanyUserListLikeName(query){
+  return request({
+    url: '/company/companyUser/getCompanyUserListLikeName',
+    method: 'get',
+    params: query
+  })
+}

+ 8 - 0
src/api/course/userCourseCamp.js

@@ -43,4 +43,12 @@ export function copyCamp(trainingCampId) {
   })
 }
 
+// 获取训练营选项列表
+export function getCampListLikeName(query) {
+  return request({
+    url: '/course/trainingCamp/getCampListLikeName',
+    method: 'get',
+    params: query
+  })
+}
 

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

@@ -51,6 +51,23 @@ export function addCourse(data) {
     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({
@@ -130,3 +147,30 @@ export function periodCountSelect(data) {
     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
+  })
+}

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

@@ -93,4 +93,13 @@ export function getVideoListByCourseId(query) {
   })
 }
 
+// 获取选项列表
+export function getVideoListLikeName(query) {
+  return request({
+    url: '/course/userCourseVideo/getVideoListLikeName',
+    method: 'get',
+    params: query
+  })
+}
+
 

+ 12 - 0
src/api/statistics/member.js

@@ -0,0 +1,12 @@
+import request from "@/utils/request"
+
+/**
+ * 获取会员统计数据
+ */
+export function dailyData(query) {
+  return request({
+    url: '/stats/member/dailyData',
+    method: 'get',
+    params: query
+  })
+}

+ 18 - 0
src/api/store/user.js

@@ -83,3 +83,21 @@ export function getAllUserListLimit(query) {
     params: query
   })
 }
+
+// 获取小黑屋用户列表
+export function darkRoomList(query) {
+  return request({
+    url: '/store/user/darkRoomList',
+    method: 'get',
+    params: query
+  })
+}
+
+// 批量解禁
+export function enabledUsers(data) {
+  return request({
+    url: '/store/user/enabledUsers',
+    method: 'post',
+    data: data
+  })
+}

+ 9 - 0
src/api/users/user.js

@@ -71,3 +71,12 @@ export function transferUser(data) {
   })
 }
 
+// 查询会员选项列表
+export function getUserListLikeName(query) {
+  return request({
+    url: '/user/fsUser/getUserListLikeName',
+    method: 'get',
+    params: query
+  })
+}
+

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

@@ -9,6 +9,7 @@
       :on-error="handleUploadError"
       :on-exceed="handleExceed"
       name="file"
+      :disabled="disabled"
       :on-remove="handleRemove"
       :show-file-list="true"
       :file-list="fileList"
@@ -56,6 +57,11 @@ export default {
        type: Number,
       default: 5,
     },
+    // 大小限制(MB)
+    disabled: {
+      type: Boolean,
+      default: false,
+    },
     // 文件类型, 例如['png', 'jpg', 'jpeg']
     fileType: {
       type: Array,

+ 11 - 0
src/directive/select/elSelectLoadMore.js

@@ -0,0 +1,11 @@
+export default {
+  inserted(el, binding, vnode) {
+    const SELECT_WRAP_DOM = el.querySelector('.el-select-dropdown .el-select-dropdown__wrap')
+    SELECT_WRAP_DOM.addEventListener('scroll', function() {
+      const condition = this.scrollHeight - this.scrollTop <= this.clientHeight
+      if (condition) {
+        binding.value()
+      }
+    })
+  }
+}

+ 7 - 0
src/directive/select/index.js

@@ -0,0 +1,7 @@
+import elSelectLoadMore from './elSelectLoadMore'
+
+const install = function(Vue) {
+  Vue.directive('select-load-more', elSelectLoadMore)
+}
+
+export default install

+ 2 - 0
src/main.js

@@ -14,6 +14,7 @@ import App from './App'
 import store from './store'
 import router from './router'
 import permission from './directive/permission'
+import select from './directive/select'
 
 import VueClipboard from 'vue-clipboard2'
 
@@ -92,6 +93,7 @@ Vue.component('FileUpload', FileUpload)
 Vue.component('ImageUpload', ImageUpload)
 
 Vue.use(permission)
+Vue.use(select)
 
 /**
  * If you don't want to use mock-server

+ 1 - 1
src/utils/cos.js

@@ -3,7 +3,7 @@ import { Message } from 'element-ui';
 import { getTmpSecretKey } from '@/api/common';
 
 const config = {
-    Bucket: 'fby-1323137866',
+    Bucket: 'hylj-1323137866',
     Region: 'ap-chongqing',
 };
 

+ 1 - 1
src/utils/obs.js

@@ -29,7 +29,7 @@ export const uploadToOBS = async(file,progressCallback,type) =>  {
         return new Promise((resolve, reject) => {
             //上传对象
             obsClient.putObject({
-                Bucket: 'fby-hw079058881',//桶名称
+                Bucket: 'ylrz-obs2024',//桶名称
                 Key: key,//文件名
                 Body: file,
                 ProgressCallback: callback,//进度回调

+ 24 - 23
src/views/components/course/userCourseCatalogDetails.vue

@@ -50,6 +50,7 @@
           type="primary"
           plain
           size="mini"
+          v-if="isPrivate === 1"
           @click="updateRedPageckeOpen"
         >修改红包</el-button>
       </el-col>
@@ -77,7 +78,7 @@
               {{ formatDuration(row.duration) }}
           </template>
       </el-table-column>
-      <el-table-column label="红包金额" align="center" prop="redPacketMoney" />
+      <el-table-column label="红包金额" align="center" prop="redPacketMoney" v-if="isPrivate === 1"/>
       <el-table-column label="排序" align="center" prop="courseSort" />
       <el-table-column label="上传时间" align="center" prop="createTime" />
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
@@ -119,26 +120,26 @@
         <el-form-item label="课程排序" prop="courseSort">
           <el-input-number v-model="form.courseSort" :min="1" ></el-input-number>
         </el-form-item>
-        <el-form-item label="看课时间" prop="timeRange">
-          <el-time-picker
-            is-range
-            v-model="form.timeRange"
-            range-separator="至"
-            start-placeholder="开始时间"
-            value-format="HH:mm:ss"
-            end-placeholder="结束时间"
-            placeholder="选择时间范围">
-          </el-time-picker>
-        </el-form-item>
-        <el-form-item label="领取红包时间" prop="lastJoinTime">
-          <el-time-picker
-            v-model="form.lastJoinTime"
-            :selectableRange="form.timeRange"
-            value-format="HH:mm:ss"
-            placeholder="选择时间范围">
-          </el-time-picker>
-          <p style="color: red;margin: 0;font-size: 12px">超过领取红包时间,只允许看课,不允许领取红包</p>
-        </el-form-item>
+<!--        <el-form-item label="看课时间" prop="timeRange" v-if="isPrivate === 1">-->
+<!--          <el-time-picker-->
+<!--            is-range-->
+<!--            v-model="form.timeRange"-->
+<!--            range-separator="至"-->
+<!--            start-placeholder="开始时间"-->
+<!--            value-format="HH:mm:ss"-->
+<!--            end-placeholder="结束时间"-->
+<!--            placeholder="选择时间范围">-->
+<!--          </el-time-picker>-->
+<!--        </el-form-item>-->
+<!--        <el-form-item label="领取红包时间" prop="lastJoinTime" v-if="isPrivate === 1">-->
+<!--          <el-time-picker-->
+<!--            v-model="form.lastJoinTime"-->
+<!--            :selectableRange="form.timeRange"-->
+<!--            value-format="HH:mm:ss"-->
+<!--            placeholder="选择时间范围">-->
+<!--          </el-time-picker>-->
+<!--          <p style="color: red;margin: 0;font-size: 12px">超过领取红包时间,只允许看课,不允许领取红包</p>-->
+<!--        </el-form-item>-->
 
         <el-form-item label="视频缩略图" prop="thumbnail">
           <el-upload
@@ -170,7 +171,7 @@
           append-to-body
         />
 
-        <el-form-item label="课题选择" prop="questionBankId">
+        <el-form-item label="课题选择" prop="questionBankId" v-if="isPrivate === 1">
           <el-button size="small" type="primary" @click="chooseQuestionBank">选取课题</el-button>
           <el-table border width="100%" style="margin-top:5px;"  :data="form.questionBankList">
 
@@ -201,7 +202,7 @@
             </el-table-column>
           </el-table>
         </el-form-item >
-        <el-form-item label="红包金额" prop="redPacketMoney">
+        <el-form-item label="红包金额" prop="redPacketMoney" v-if="isPrivate === 1">
           <el-input-number v-model="form.redPacketMoney" :min="0.1" :max="200" :step="0.1" ></el-input-number>
         </el-form-item>
       </el-form>

+ 11 - 211
src/views/course/userCourse/index.vue

@@ -31,7 +31,7 @@
           @keyup.enter.native="handleQuery"
         />
       </el-form-item>
-      <el-form-item label="课堂类型" prop="isPrivate">
+      <el-form-item label="课堂类型" prop="isPrivate" style="display: none">
         <el-select v-model="queryParams.isPrivate" placeholder="请选择" clearable size="small">
           <el-option
             v-for="dict in courseTypeOptions"
@@ -144,25 +144,10 @@
           </el-popover>
         </template>
       </el-table-column>
-      <el-table-column label="小封面" align="center" prop="secondImg" width="100">
-        <template slot-scope="scope">
-          <el-popover
-            placement="right"
-            title=""
-            trigger="hover"
-          >
-            <img slot="reference" :src="scope.row.secondImg" width="50">
-            <img :src="scope.row.secondImg" style="max-width: 100px;">
-          </el-popover>
-        </template>
-      </el-table-column>
       <el-table-column label="课堂名称" align="center" show-overflow-tooltip prop="courseName"/>
       <el-table-column label="排序" align="center" prop="sort"/>
       <el-table-column label="分类名称" align="center" prop="cateName"/>
       <el-table-column label="子分类名称" align="center" prop="subCateName"/>
-      <el-table-column label="原价" align="center" prop="price"/>
-      <el-table-column label="售价" align="center" prop="sellPrice"/>
-      <el-table-column label="单节积分" align="center" prop="integral"/>
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
         <template slot-scope="scope">
           <el-button
@@ -255,36 +240,6 @@
             </el-form-item>
           </el-col>
         </el-row>
-        <el-row>
-          <!-- <el-col :span="8">
-            <el-form-item label="课堂类型" prop="courseType">
-              <el-select v-model="form.courseType" placeholder="请选择" clearable size="small">
-                <el-option
-                    v-for="dict in courseTypeOptions"
-                    :key="dict.dictValue"
-                    :label="dict.dictLabel"
-                    :value="dict.dictValue"
-                  />
-              </el-select>
-            </el-form-item>
-          </el-col> -->
-          <el-col :span="8">
-            <el-form-item label="关联达人" prop="talentId">
-              <el-select v-model="form.talentId" remote filterable clearable reserve-keyword
-                         placeholder="输入手机号搜索" :remote-method="talentMethod">
-                <el-option
-                  v-for="item in talentList"
-                  :key="item.talentId"
-                  :label="item.nickName +'#'+item.phone"
-                  :value="item.talentId">
-                  <span style="float: left">{{ item.talentId }}</span>
-                  <span style="margin-left: 30px ;">{{ item.nickName }}</span>
-                  <span style="margin-left: 30px">{{ item.phone }}</span>
-                </el-option>
-              </el-select>
-            </el-form-item>
-          </el-col>
-        </el-row>
         <el-row>
           <el-col :span="24">
             <el-form-item label="课堂简介" prop="description">
@@ -295,161 +250,6 @@
         <el-form-item label="课程封面" prop="imgUrl">
           <ImageUpload v-model="form.imgUrl" type="image" :num="10" :width="150" :height="150"/>
         </el-form-item>
-        <el-form-item label="小封面" prop="imgUrl">
-          <ImageUpload v-model="form.secondImg" type="image" :num="10" :width="150" :height="150"/>
-        </el-form-item>
-        <el-row>
-          <el-col :span="12">
-            <el-form-item label="标签" prop="tags">
-              <el-select v-model="tags" multiple placeholder="请选择标签" filterable clearable size="small">
-                <el-option
-                  v-for="dict in tagsOptions"
-                  :key="dict.dictValue"
-                  :label="dict.dictLabel"
-                  :value="dict.dictValue"
-                />
-              </el-select>
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row>
-          <el-col :span="8">
-            <el-form-item label="发课时间" prop="sendTime">
-              <el-time-picker
-                v-model="form.sendTime"
-                value-format="HH:mm"
-                format="HH:mm"
-                style="width: 100px"
-                :picker-options="{ selectableRange: startTimeRange }"
-              >
-              </el-time-picker>
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row>
-          <el-col :span="8">
-            <el-form-item label="排序" prop="sort">
-              <el-input-number v-model="form.sort" :min="0" label="排序"></el-input-number>
-            </el-form-item>
-          </el-col>
-          <el-col :span="8">
-            <el-form-item label="播放量" prop="views">
-              <el-input-number v-model="form.views" :min="0" label="浏览量"></el-input-number>
-            </el-form-item>
-          </el-col>
-          <el-col :span="8">
-            <el-form-item label="点赞量" prop="likes">
-              <el-input-number v-model="form.likes" :min="0" label="点赞量"></el-input-number>
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row>
-
-          <el-col :span="8">
-            <el-form-item label="收藏数" prop="favoriteNum">
-              <el-input-number v-model="form.favoriteNum" :min="0" label="收藏数"></el-input-number>
-            </el-form-item>
-          </el-col>
-          <el-col :span="8">
-            <el-form-item label="分享数" prop="shares">
-              <el-input-number v-model="form.shares" :min="0" label="分享数"></el-input-number>
-            </el-form-item>
-          </el-col>
-          <el-col :span="8">
-            <el-form-item label="热度值" prop="hotNum">
-              <el-input-number v-model="form.hotNum" :min="0" label="热度值"></el-input-number>
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row>
-          <el-col :span="8">
-            <el-form-item label="状态" prop="isShow">
-              <el-radio-group v-model="form.isShow">
-                <el-radio :label="item.dictValue" v-for="item in specShowOptions">{{ item.dictLabel }}</el-radio>
-              </el-radio-group>
-            </el-form-item>
-          </el-col>
-          <el-col :span="8">
-            <el-form-item label="是否推荐" prop="isTui">
-              <el-radio-group v-model="form.isTui">
-                <el-radio :label="item.dictValue" v-for="item in orOptions">{{ item.dictLabel }}</el-radio>
-              </el-radio-group>
-            </el-form-item>
-          </el-col>
-          <el-col :span="8">
-            <el-form-item label="是否精选" prop="isBest">
-              <el-radio-group v-model="form.isBest">
-                <el-radio :label="item.dictValue" v-for="item in orOptions">{{ item.dictLabel }}</el-radio>
-              </el-radio-group>
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row>
-          <el-col :span="8">
-            <el-form-item label="是否自动播放" prop="isAutoPlay">
-              <el-radio-group v-model="form.isAutoPlay">
-                <el-radio :label="item.dictValue" v-for="item in orOptions">{{ item.dictLabel }}</el-radio>
-              </el-radio-group>
-            </el-form-item>
-          </el-col>
-          <el-col :span="8">
-            <el-form-item label="是否允许快进" prop="isFast">
-              <el-radio-group v-model="form.isFast">
-                <el-radio :label="item.dictValue" v-for="item in orOptions">{{ item.dictLabel }}</el-radio>
-              </el-radio-group>
-            </el-form-item>
-          </el-col>
-          <el-col :span="8">
-            <el-form-item label="是否积分兑换" prop="isIntegral">
-              <el-radio-group v-model="form.isIntegral">
-                <el-radio :label="item.dictValue" v-for="item in orOptions">{{ item.dictLabel }}</el-radio>
-              </el-radio-group>
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row>
-          <el-col :span="8">
-            <el-form-item label="是否逐级播放" prop="isNext">
-              <el-radio-group v-model="form.isNext">
-                <el-radio :label="item.dictValue" v-for="item in orOptions">{{ item.dictLabel }}</el-radio>
-              </el-radio-group>
-            </el-form-item>
-          </el-col>
-          <el-col :span="8">
-            <el-form-item label="是否私域" prop="isPrivate">
-              <el-radio-group v-model="form.isPrivate">
-                <el-radio :label="item.dictValue" v-for="item in orOptions">{{ item.dictLabel }}</el-radio>
-              </el-radio-group>
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row>
-          <el-col :span="8">
-            <el-form-item label="课程原价" prop="price">
-              <el-input v-model="form.price" placeholder="请输入课程原价"/>
-            </el-form-item>
-          </el-col>
-          <el-col :span="8">
-            <el-form-item label="课程售价" prop="sellPrice">
-              <el-input v-model="form.sellPrice" placeholder="请输入课程售价"/>
-            </el-form-item>
-          </el-col>
-          <el-col :span="8">
-            <el-form-item label="单节所需积分" prop="integral" v-if="form.isIntegral==1">
-              <el-input v-model="form.integral" placeholder="请输入单节所需积分"/>
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-form-item label="关联公司" prop="tags">
-          <el-select v-model="companyIds" multiple placeholder="请选择公司" filterable clearable style="width: 90%;">
-            <el-option
-              v-for="dict in companyOptions"
-              :key="dict.dictValue"
-              :label="dict.dictLabel"
-              :value="dict.dictValue"
-            />
-          </el-select>
-        </el-form-item>
       </el-form>
       <div slot="footer" class="dialog-footer">
         <el-button type="primary" @click="submitForm">确 定</el-button>
@@ -558,7 +358,8 @@ export default {
         description: null,
         hotRanking: null,
         integral: null,
-        price: null
+        price: null,
+        isPrivate: 1
       },
       // 表单参数
       form: {},
@@ -799,9 +600,11 @@ export default {
       getUserCourse(courseId).then(response => {
         this.form = response.data;
         // this.form.cateId = response.data.cateId.toString();
-        getCateListByPid(this.form.cateId).then(response => {
-          this.subCategoryOptions = response.data;
-        });
+        if (this.form.cateId) {
+          getCateListByPid(this.form.cateId).then(response => {
+            this.subCategoryOptions = response.data;
+          });
+        }
         // this.form.courseType = response.data.courseType.toString();
         if (response.data.project != null) {
           this.form.project = response.data.project.toString();
@@ -835,12 +638,9 @@ export default {
     submitForm() {
       this.$refs["form"].validate(valid => {
         if (valid) {
-          if (this.tags.length > 0) {
-            this.form.tags = this.tags.toString();
-          } else {
-            this.form.tags = null
-          }
-          this.form.companyIds = this.companyIds.toString()
+
+          // 私域课程
+          this.form.isPrivate = 1
           if (this.form.courseId != null) {
             updateUserCourse(this.form).then(response => {
               this.msgSuccess("修改成功");

+ 929 - 0
src/views/course/userCourse/public.vue

@@ -0,0 +1,929 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+      <el-form-item label="课堂分类" prop="cateId">
+        <el-select v-model="queryParams.cateId" placeholder="请选择" clearable size="small"
+                   @change="getQuerySubCateList(queryParams.cateId)">
+          <el-option
+            v-for="dict in categoryOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="课堂子分类" prop="subCateId">
+        <el-select v-model="queryParams.subCateId" placeholder="请选择" clearable size="small">
+          <el-option
+            v-for="dict in querySubCateOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="课堂名称" prop="courseName">
+        <el-input
+          v-model="queryParams.courseName"
+          placeholder="请输入课堂名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="课堂类型" prop="isPrivate" style="display: none">
+        <el-select v-model="queryParams.isPrivate" placeholder="请选择" clearable size="small">
+          <el-option
+            v-for="dict in courseTypeOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['course:userCourse:add']"
+        >新增
+        </el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="success"
+          plain
+          icon="el-icon-edit"
+          size="mini"
+          :disabled="single"
+          @click="handleUpdate"
+          v-hasPermi="['course:userCourse:edit']"
+        >修改
+        </el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+          v-hasPermi="['course:userCourse:remove']"
+        >删除
+        </el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+          v-hasPermi="['course:userCourse:export']"
+        >导出
+        </el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          v-if="queryParams.isShow==0"
+          type="success"
+          plain
+          icon="el-icon-edit"
+          size="mini"
+          :disabled="multiple"
+          @click="putOn"
+          v-hasPermi="['course:userCourse:putOn']"
+        >上架
+        </el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          v-if="queryParams.isShow==1"
+          type="success"
+          plain
+          icon="el-icon-edit"
+          size="mini"
+          :disabled="multiple"
+          @click="pullOff"
+          v-hasPermi="['course:userCourse:pullOff']"
+        >下架
+        </el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-tabs type="card" v-model="queryParams.isShow" @tab-click="handleClick">
+      <el-tab-pane label="已上架" name="1"></el-tab-pane>
+      <el-tab-pane label="待上架" name="0"></el-tab-pane>
+    </el-tabs>
+    <el-table height="600" border v-loading="loading" :data="userCourseList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center"/>
+      <el-table-column label="课程ID" align="center" prop="courseId"/>
+      <el-table-column label="所属项目" align="center" prop="projectName"/>
+      <el-table-column label="封面图片" align="center" prop="imgUrl" width="120">
+        <template slot-scope="scope">
+          <el-popover
+            placement="right"
+            title=""
+            trigger="hover"
+          >
+            <img slot="reference" :src="scope.row.imgUrl" width="100">
+            <img :src="scope.row.imgUrl" style="max-width: 300px;">
+          </el-popover>
+        </template>
+      </el-table-column>
+      <el-table-column label="小封面" align="center" prop="secondImg" width="100">
+        <template slot-scope="scope">
+          <el-popover
+            placement="right"
+            title=""
+            trigger="hover"
+          >
+            <img slot="reference" :src="scope.row.secondImg" width="50">
+            <img :src="scope.row.secondImg" style="max-width: 100px;">
+          </el-popover>
+        </template>
+      </el-table-column>
+      <el-table-column label="课堂名称" align="center" show-overflow-tooltip prop="courseName"/>
+      <el-table-column label="排序" align="center" prop="sort"/>
+      <el-table-column label="分类名称" align="center" prop="cateName"/>
+      <el-table-column label="子分类名称" align="center" prop="subCateName"/>
+      <el-table-column label="原价" align="center" prop="price"/>
+      <el-table-column label="售价" align="center" prop="sellPrice"/>
+      <el-table-column label="单节积分" align="center" prop="integral"/>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            @click="handleCatalog(scope.row)"
+          >目录管理
+          </el-button>
+          <el-button
+            size="mini"
+            type="text"
+            :icon="scope.row.isShow === 1 ? 'el-icon-close' : 'el-icon-open'"
+            @click="handleShow(scope.row)"
+            v-hasPermi="['course:userCourse:editShow']"
+          >
+            {{ scope.row.isShow === 1 ? '下架' : '上架' }}
+          </el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['course:userCourse:edit']"
+          >修改
+          </el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['course:userCourse:remove']"
+          >删除
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改课程对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="1200px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="110px">
+        <el-row>
+          <el-form-item label="所属项目" prop="project">
+            <el-select v-model="form.project" placeholder="请选择项目" filterable clearable size="small">
+              <el-option
+                v-for="dict in projectOptions"
+                :key="dict.dictValue"
+                :label="dict.dictLabel"
+                :value="dict.dictValue"
+              />
+            </el-select>
+          </el-form-item>
+        </el-row>
+        <el-row>
+          <el-col :span="8">
+            <el-form-item label="课堂名称" prop="courseName">
+              <el-input v-model="form.courseName" placeholder="请输入课堂名称"/>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="课堂分类" prop="cateId">
+              <el-select v-model="form.cateId" placeholder="请选择" clearable size="small"
+                         @change="getSubCateList(form.cateId)">
+                <el-option
+                  v-for="dict in categoryOptions"
+                  :key="dict.dictValue"
+                  :label="dict.dictLabel"
+                  :value="dict.dictValue"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="课堂子分类" prop="subCateId">
+              <el-select v-model="form.subCateId" placeholder="请选择" clearable size="small">
+                <el-option
+                  v-for="dict in subCategoryOptions"
+                  :key="dict.dictValue"
+                  :label="dict.dictLabel"
+                  :value="dict.dictValue"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <!-- <el-col :span="8">
+            <el-form-item label="课堂类型" prop="courseType">
+              <el-select v-model="form.courseType" placeholder="请选择" clearable size="small">
+                <el-option
+                    v-for="dict in courseTypeOptions"
+                    :key="dict.dictValue"
+                    :label="dict.dictLabel"
+                    :value="dict.dictValue"
+                  />
+              </el-select>
+            </el-form-item>
+          </el-col> -->
+          <el-col :span="8">
+            <el-form-item label="关联达人" prop="talentId">
+              <el-select v-model="form.talentId" remote filterable clearable reserve-keyword
+                         placeholder="输入手机号搜索" :remote-method="talentMethod">
+                <el-option
+                  v-for="item in talentList"
+                  :key="item.talentId"
+                  :label="item.nickName +'#'+item.phone"
+                  :value="item.talentId">
+                  <span style="float: left">{{ item.talentId }}</span>
+                  <span style="margin-left: 30px ;">{{ item.nickName }}</span>
+                  <span style="margin-left: 30px">{{ item.phone }}</span>
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="24">
+            <el-form-item label="课堂简介" prop="description">
+              <el-input v-model="form.description" type="textarea" :rows="2" placeholder="请输入课堂简介"/>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item label="课程封面" prop="imgUrl">
+          <ImageUpload v-model="form.imgUrl" type="image" :num="10" :width="150" :height="150"/>
+        </el-form-item>
+        <el-form-item label="小封面" prop="imgUrl">
+          <ImageUpload v-model="form.secondImg" type="image" :num="10" :width="150" :height="150"/>
+        </el-form-item>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="标签" prop="tags">
+              <el-select v-model="tags" multiple placeholder="请选择标签" filterable clearable size="small">
+                <el-option
+                  v-for="dict in tagsOptions"
+                  :key="dict.dictValue"
+                  :label="dict.dictLabel"
+                  :value="dict.dictValue"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="8">
+            <el-form-item label="发课时间" prop="sendTime">
+              <el-time-picker
+                v-model="form.sendTime"
+                value-format="HH:mm"
+                format="HH:mm"
+                style="width: 100px"
+                :picker-options="{ selectableRange: startTimeRange }"
+              >
+              </el-time-picker>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="8">
+            <el-form-item label="排序" prop="sort">
+              <el-input-number v-model="form.sort" :min="0" label="排序"></el-input-number>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="播放量" prop="views">
+              <el-input-number v-model="form.views" :min="0" label="浏览量"></el-input-number>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="点赞量" prop="likes">
+              <el-input-number v-model="form.likes" :min="0" label="点赞量"></el-input-number>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+
+          <el-col :span="8">
+            <el-form-item label="收藏数" prop="favoriteNum">
+              <el-input-number v-model="form.favoriteNum" :min="0" label="收藏数"></el-input-number>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="分享数" prop="shares">
+              <el-input-number v-model="form.shares" :min="0" label="分享数"></el-input-number>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="热度值" prop="hotNum">
+              <el-input-number v-model="form.hotNum" :min="0" label="热度值"></el-input-number>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="8">
+            <el-form-item label="状态" prop="isShow">
+              <el-radio-group v-model="form.isShow">
+                <el-radio :label="item.dictValue" v-for="item in specShowOptions">{{ item.dictLabel }}</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="是否推荐" prop="isTui">
+              <el-radio-group v-model="form.isTui">
+                <el-radio :label="item.dictValue" v-for="item in orOptions">{{ item.dictLabel }}</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="是否精选" prop="isBest">
+              <el-radio-group v-model="form.isBest">
+                <el-radio :label="item.dictValue" v-for="item in orOptions">{{ item.dictLabel }}</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="8">
+            <el-form-item label="是否自动播放" prop="isAutoPlay">
+              <el-radio-group v-model="form.isAutoPlay">
+                <el-radio :label="item.dictValue" v-for="item in orOptions">{{ item.dictLabel }}</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="是否允许快进" prop="isFast">
+              <el-radio-group v-model="form.isFast">
+                <el-radio :label="item.dictValue" v-for="item in orOptions">{{ item.dictLabel }}</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="是否积分兑换" prop="isIntegral">
+              <el-radio-group v-model="form.isIntegral">
+                <el-radio :label="item.dictValue" v-for="item in orOptions">{{ item.dictLabel }}</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="8">
+            <el-form-item label="是否逐级播放" prop="isNext">
+              <el-radio-group v-model="form.isNext">
+                <el-radio :label="item.dictValue" v-for="item in orOptions">{{ item.dictLabel }}</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8" style="display: none">
+            <el-form-item label="是否私域" prop="isPrivate">
+              <el-radio-group v-model="form.isPrivate">
+                <el-radio :label="item.dictValue" v-for="item in orOptions">{{ item.dictLabel }}</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="8">
+            <el-form-item label="课程原价" prop="price">
+              <el-input v-model="form.price" placeholder="请输入课程原价"/>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="课程售价" prop="sellPrice">
+              <el-input v-model="form.sellPrice" placeholder="请输入课程售价"/>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="单节所需积分" prop="integral" v-if="form.isIntegral==1">
+              <el-input v-model="form.integral" placeholder="请输入单节所需积分"/>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item label="关联公司" prop="tags">
+          <el-select v-model="companyIds" multiple placeholder="请选择公司" filterable clearable style="width: 90%;">
+            <el-option
+              v-for="dict in companyOptions"
+              :key="dict.dictValue"
+              :label="dict.dictLabel"
+              :value="dict.dictValue"
+            />
+          </el-select>
+        </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-drawer
+      :with-header="false"
+      size="75%"
+      :title="show.title" :visible.sync="show.open" append-to-body>
+      <userCourseCatalogDetails ref="userCourseCatalogDetails"/>
+    </el-drawer>
+  </div>
+</template>
+
+<script>
+import {
+  listUserCourse,
+  getUserCourse,
+  delUserCourse,
+  addUserCourse,
+  updateUserCourse,
+  exportUserCourse,
+  updateIsShow,
+  putOn,
+  pullOff
+} from "@/api/course/userCourse";
+
+import {getSelectableRange} from "@/api/qw/sopTemp";
+import Treeselect from "@riophae/vue-treeselect";
+import "@riophae/vue-treeselect/dist/vue-treeselect.css";
+import Editor from '@/components/Editor/wang';
+import ImageUpload from '@/components/ImageUpload/index';
+import {listBySearch} from "@/api/course/userTalent";
+import userCourseCatalogDetails from '../../components/course/userCourseCatalogDetails.vue';
+import {getAllCourseCategoryList, getCatePidList, getCateListByPid} from "@/api/course/userCourseCategory";
+import {allList} from "@/api/company/company";
+
+export default {
+  name: "UserCoursePublic",
+  components: {
+    Treeselect,
+    Editor, ImageUpload, userCourseCatalogDetails
+  },
+  data() {
+    return {
+      talentParam: {
+        phone: null,
+        talentId: null
+      },
+      talentList: [],
+      startTimeRange: [],
+      show: {
+        title: "目录管理",
+        open: false
+      },
+      activeName: "1",
+      projectOptions: [],
+      tagsOptions: [],
+      tags: [],
+      companyIds: [],
+      courseTypeOptions: [],
+      orOptions: [],
+      specShowOptions: [],
+      specTypeOptions: [],
+      categoryOptions: [],
+      subCategoryOptions: [],
+      querySubCateOptions: [],
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 课程表格数据
+      userCourseList: [],
+      companyOptions: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        cateId: null,
+        subCateId: null,
+        title: null,
+        imgUrl: null,
+        userId: null,
+        sort: null,
+        status: null,
+        isVip: null,
+        isHot: null,
+        isShow: "1",
+        views: null,
+        duration: null,
+        description: null,
+        hotRanking: null,
+        integral: null,
+        price: null,
+        isPrivate: 0
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        courseName: [
+          {required: true, message: "课堂名称不能为空", trigger: "blur"}
+        ],
+        imgUrl: [
+          {required: true, message: "封面图片不能为空", trigger: "blur"}
+        ],
+        isTui: [
+          {required: true, message: "是否推荐不能为空", trigger: "blur"}
+        ],
+        isBest: [
+          {required: true, message: "是否精选不能为空", trigger: "blur"}
+        ],
+        isFast: [
+          {required: true, message: "是否允许快进不能为空", trigger: "blur"}
+        ],
+        isAutoPlay: [
+          {required: true, message: "是否自动播放不能为空", trigger: "blur"}
+        ],
+        sort: [
+          {required: true, message: "排序不能为空", trigger: "blur"}
+        ],
+        views: [
+          {required: true, message: "播放量不能为空", trigger: "blur"}
+        ],
+        likes: [
+          {required: true, message: "点赞数不能为空", trigger: "blur"}
+        ],
+        favoriteNum: [
+          {required: true, message: "收藏数不能为空", trigger: "blur"}
+        ],
+        shares: [
+          {required: true, message: "分享数不能为空", trigger: "blur"}
+        ],
+        isIntegral: [
+          {required: true, message: "是否允许积分兑换不能为空", trigger: "blur"}
+        ],
+        isShow: [
+          {required: true, message: "上架状态不能为空", trigger: "blur"}
+        ],
+        isPrivate: [
+          {required: true, message: "公私域不能为空", trigger: "blur"}
+        ],
+        integral: [
+          {required: true, message: "小节兑换积分不能为空", trigger: "blur"}
+        ],
+      }
+    };
+  },
+  created() {
+    this.getList();
+    getCatePidList().then(response => {
+      this.categoryOptions = response.data;
+    });
+
+
+    getSelectableRange().then(e => {
+      this.startTimeRange = e.data;
+    })
+    // this.getTreeselect();
+    this.getDicts("sys_spec_show").then(response => {
+      this.specShowOptions = response.data;
+    });
+    this.getDicts("sys_spec_type").then(response => {
+      this.specTypeOptions = response.data;
+    });
+    this.getDicts("sys_course_type").then(response => {
+      this.courseTypeOptions = response.data;
+    });
+    this.getDicts("sys_course_project").then(response => {
+      this.projectOptions = response.data;
+    });
+    this.getDicts("sys_course_tags").then(response => {
+      this.tagsOptions = response.data;
+    });
+    this.getDicts("sys_company_or").then(response => {
+      this.orOptions = response.data;
+    });
+    allList().then(response => {
+      this.companyOptions = response.rows;
+    });
+  },
+  methods: {
+    selectTalent() {
+
+    },
+    talentMethod(query) {
+      if (query !== '') {
+        this.talentParam.phone = query;
+        listBySearch(this.talentParam).then(response => {
+          this.talentList = response.data;
+        });
+      }
+    },
+    getSubCateList(pid) {
+      this.form.subCateId = null;
+      if (pid == '') {
+        this.subCategoryOptions = [];
+        return
+      }
+      getCateListByPid(pid).then(response => {
+        this.subCategoryOptions = response.data;
+      });
+    },
+    getQuerySubCateList(pid) {
+      this.queryParams.subCateId = null;
+      if (pid == '') {
+        this.querySubCateOptions = [];
+        return
+      }
+      this.queryParams.subCateId = null;
+      getCateListByPid(pid).then(response => {
+        this.querySubCateOptions = response.data;
+      });
+    },
+    handleShow(row) {
+      var isShowValue = row.isShow === 0 ? 1 : 0;
+      var course = {courseId: row.courseId, isShow: isShowValue};
+      updateIsShow(course).then(response => {
+        this.msgSuccess("修改成功");
+        this.getList();
+      });
+    },
+    handleCatalog(row) {
+      const courseId = row.courseId;
+      this.show.open = true;
+      setTimeout(() => {
+        this.$refs.userCourseCatalogDetails.getDetails(courseId, row.courseName, row.isPrivate);
+      }, 200);
+    },
+    handleClick(tab, event) {
+      this.queryParams.isShow = tab.name;
+      this.getList();
+    },
+    /** 转换课堂分类数据结构 */
+    normalizer(node) {
+      if (node.children && !node.children.length) {
+        delete node.children;
+      }
+      return {
+        id: node.cateId,
+        label: node.cateName,
+        children: node.children
+      };
+    },
+    getTreeselect() {
+      getAllCourseCategoryList().then(response => {
+        this.categoryOptions = [];
+        const data = this.handleTree(response.data, "cateId", "pid");
+        this.categoryOptions = data;
+      });
+    },
+    /** 查询课程列表 */
+    getList() {
+      this.loading = true;
+      listUserCourse(this.queryParams).then(response => {
+        this.userCourseList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        courseId: null,
+        cateId: null,
+        subCateId: null,
+        title: null,
+        imgUrl: null,
+        secondImg: null,
+        userId: null,
+        sort: null,
+        createTime: null,
+        updateTime: null,
+        status: 0,
+        isVip: null,
+        isAutoPlay: "1",
+        isIntegral: "0",
+        isShow: "1",
+        isFast: "1",
+        isTui: "1",
+        isBest: "1",
+        isNext: "1",
+        isPrivate: "0",
+        views: 100000,
+        duration: null,
+        description: null,
+        hotRanking: null,
+        integral: null,
+        price: null,
+        likes: 100000,
+        shares: 100000,
+        favoriteNum: 100000,
+        hotNum: 100000,
+      };
+      this.tags = [];
+      this.subCategoryOptions = []
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.queryParams.isShow = this.activeName
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.courseId)
+      this.single = selection.length !== 1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.talentList = [];
+      this.open = true;
+      this.title = "添加课程";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      this.talentList = [];
+      const courseId = row.courseId || this.ids
+      getUserCourse(courseId).then(response => {
+        this.form = response.data;
+        // this.form.cateId = response.data.cateId.toString();
+        getCateListByPid(this.form.cateId).then(response => {
+          this.subCategoryOptions = response.data;
+        });
+        // this.form.courseType = response.data.courseType.toString();
+        if (response.data.project != null) {
+          this.form.project = response.data.project.toString();
+        }
+        if (response.data.tags != null) {
+          this.tags = response.data.tags.split(",")
+        }
+        this.form.isAutoPlay = response.data.isAutoPlay.toString();
+        this.form.isShow = response.data.isShow.toString();
+        this.form.isBest = response.data.isBest.toString();
+        this.form.isFast = response.data.isFast.toString();
+        this.form.isIntegral = response.data.isIntegral.toString();
+        this.form.isTui = response.data.isTui.toString();
+        this.form.isNext = response.data.isNext.toString();
+        this.form.isPrivate = response.data.isPrivate.toString();
+        this.talentParam.talentId = response.data.talentId;
+        if (this.form.companyIds != null) {
+          this.companyIds = ((this.form.companyIds).split(",").map(Number))
+        } else {
+          this.companyIds = []
+        }
+
+        listBySearch(this.talentParam).then(response => {
+          this.talentList = response.data;
+        });
+        this.open = true;
+        this.title = "修改课程";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.tags.length > 0) {
+            this.form.tags = this.tags.toString();
+          } else {
+            this.form.tags = null
+          }
+          this.form.companyIds = this.companyIds.toString()
+          this.form.isPrivate = 0
+          if (this.form.courseId != null) {
+            updateUserCourse(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addUserCourse(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const courseIds = row.courseId || this.ids;
+      this.$confirm('是否确认删除课程编号为"' + courseIds + '"的数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function () {
+        return delUserCourse(courseIds);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      }).catch(() => {
+      });
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有课程数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        this.exportLoading = true;
+        return exportUserCourse(queryParams);
+      }).then(response => {
+        this.download(response.msg);
+        this.exportLoading = false;
+      }).catch(() => {
+      });
+    },
+    putOn() {
+      const courseIds = this.ids;
+      if (courseIds == null || courseIds == "") {
+        return this.$message("未选择课程");
+      }
+      this.$confirm('是否确认批量上架课程?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function () {
+        return putOn(courseIds);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("上架成功");
+      }).catch(function () {
+      });
+    },
+    pullOff() {
+      const courseIds = this.ids;
+      if (courseIds == null || courseIds == "") {
+        return this.$message("未选择课程");
+      }
+      this.$confirm('是否确认批量下架课程?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function () {
+        return pullOff(courseIds);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("下架成功");
+      }).catch(function () {
+      });
+    }
+  }
+};
+</script>

+ 317 - 25
src/views/course/userCoursePeriod/index.vue

@@ -164,6 +164,7 @@
           <el-table-column type="selection" width="55" align="center" />
           <el-table-column label="营期名称" align="center" prop="periodName" />
           <el-table-column label="公司名称" align="center" prop="companyName" />
+          <el-table-column label="营期状态" align="center" prop="periodStatus" width="100" :formatter="periodStatusFormatter" />
           <el-table-column label="开营开始时间" align="center" prop="periodStartingTime" width="180" />
           <el-table-column label="开营结束时间" align="center" prop="periodEndTime" width="180" />
           <el-table-column label="创建时间" align="center" prop="createTime" width="180" />
@@ -195,6 +196,12 @@
                 icon="el-icon-setting"
                 @click="handlePeriodSettings(scope.row)"
               >营期相关设置</el-button>
+              <el-button
+                size="mini"
+                type="text"
+                icon="el-icon-circle-close"
+                @click="handleClosePeriod(scope.row)"
+              >结束营期</el-button>
               <el-button
                 size="mini"
                 type="text"
@@ -252,6 +259,7 @@
         </el-form-item>
         <el-form-item label="开营日期" prop="periodStartingTime">
           <el-date-picker
+            :disabled = "isDisabledDateRange"
             :style="{display: form.periodType == 1 ? '' : 'none !important'}"
             v-model="form.dateRange"
             @change="timeChange(1)"
@@ -271,24 +279,27 @@
           </el-date-picker>
         </el-form-item>
 
-<!--        <div v-if="form.periodType == 1">-->
-<!--          <el-form-item label="开课时间" prop="periodType">-->
-<!--            <el-radio-group v-model="form.periodType">-->
-<!--              <el-radio :label="1" >多课程</el-radio>-->
-<!--              <el-radio :label="2" >单课程</el-radio>-->
-<!--            </el-radio-group>-->
-<!--          </el-form-item>-->
-<!--          <el-form-item :label="'第' + item.lesson + '节'" prop="periodStartingTime" v-for="item in form.days">-->
-<!--            <el-date-picker-->
-<!--              v-model="item.dateRange"-->
-<!--              type="datetimerange"-->
-<!--              range-separator="至"-->
-<!--              start-placeholder="开始日期"-->
-<!--              end-placeholder="结束日期"-->
-<!--              value-format="yyyy-MM-dd HH:mm:ss">-->
-<!--            </el-date-picker>-->
-<!--          </el-form-item>-->
-<!--        </div>-->
+<!--        <el-form-item label="看课时间" prop="timeRange">-->
+<!--          <el-time-picker-->
+<!--            is-range-->
+<!--            v-model="form.timeRange"-->
+<!--            @input="$forceUpdate()"-->
+<!--            range-separator="至"-->
+<!--            start-placeholder="开始时间"-->
+<!--            value-format="HH:mm:ss"-->
+<!--            end-placeholder="结束时间"-->
+<!--            placeholder="选择时间范围">-->
+<!--          </el-time-picker>-->
+<!--        </el-form-item>-->
+<!--        <el-form-item label="领取红包时间" prop="lastJoinTime">-->
+<!--          <el-time-picker-->
+<!--            v-model="form.lastJoinTime"-->
+<!--            :selectableRange="form.lastJoinTime"-->
+<!--            value-format="HH:mm:ss"-->
+<!--            placeholder="选择时间范围">-->
+<!--          </el-time-picker>-->
+<!--          <p style="color: red;margin: 0;font-size: 12px">超过领取红包时间,只允许看课,不允许领取红包</p>-->
+<!--        </el-form-item>-->
       </el-form>
       <div class="drawer-footer">
         <el-button type="primary" @click="submitForm">确 定</el-button>
@@ -335,7 +346,7 @@
 <!--      </div>-->
 <!--    </el-dialog>-->
 
-    <!-- 添加或修改会员营期对话框-->
+    <!-- 添加课程对话框-->
     <el-dialog title="添加课程" :visible.sync="course.addOpen" width="500px" append-to-body>
       <el-form ref="courseAddForm" :model="course.form" label-width="100px">
         <el-form-item label="课程" prop="courseId">
@@ -358,12 +369,77 @@
             />
           </el-select>
         </el-form-item>
+        <el-form-item label="看课时间" prop="timeRange">
+          <el-time-picker
+            is-range
+            v-model="course.form.timeRange"
+            range-separator="至"
+            start-placeholder="开始时间"
+            value-format="HH:mm:ss"
+            end-placeholder="结束时间"
+            placeholder="选择时间范围">
+          </el-time-picker>
+        </el-form-item>
+        <el-form-item label="领取红包时间" prop="lastJoinTime">
+          <el-time-picker
+            v-model="course.form.joinTime"
+            :selectableRange="course.form.timeRange"
+            value-format="HH:mm:ss"
+            placeholder="选择时间范围">
+          </el-time-picker>
+          <p style="color: red;margin: 0;font-size: 12px">超过领取红包时间,只允许看课,不允许领取红包</p>
+        </el-form-item>
       </el-form>
       <div slot="footer" class="dialog-footer">
         <el-button type="primary" @click="submitCourseForm">确 定</el-button>
         <el-button @click="closeAddCourse">取 消</el-button>
       </div>
     </el-dialog>
+    <el-dialog title="修改看课时间" :visible.sync="updateCourse.open" width="500px" append-to-body>
+      <el-form ref="courseUpdateForm" :model="updateCourse.form" label-width="100px">
+        <el-form-item label="看课时间" prop="timeRange">
+          <el-time-picker
+            is-range
+            v-model="updateCourse.form.timeRange"
+            range-separator="至"
+            start-placeholder="开始时间"
+            value-format="HH:mm:ss"
+            end-placeholder="结束时间"
+            placeholder="选择时间范围">
+          </el-time-picker>
+        </el-form-item>
+        <el-form-item label="领取红包时间" prop="lastJoinTime">
+          <el-time-picker
+            v-model="updateCourse.form.joinTime"
+            :selectableRange="updateCourse.form.timeRange"
+            value-format="HH:mm:ss"
+            placeholder="选择时间范围">
+          </el-time-picker>
+          <p style="color: red;margin: 0;font-size: 12px">超过领取红包时间,只允许看课,不允许领取红包</p>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitUpdateCourseForm">确 定</el-button>
+        <el-button @click="closeUpdateCourse">取 消</el-button>
+      </div>
+    </el-dialog>
+    <el-dialog title="修改营期时间" :visible.sync="updateDateOpen" width="500px" append-to-body>
+      <el-form ref="courseUpdateForm" :model="form" label-width="100px">
+        <el-form-item label="营期时间" prop="dayDate">
+          <el-date-picker
+            v-model="form.dayDate"
+            :selectableRange="form.dayDate"
+            value-format="yyyy-MM-dd"
+            type="date"
+            placeholder="选择时间">
+          </el-date-picker>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="updateDate">确 定</el-button>
+        <el-button @click="updateDateOpen = false">取 消</el-button>
+      </div>
+    </el-dialog>
 
 <!--    <red-packet-->
 <!--      :visible.sync="redPacketVisible"-->
@@ -396,12 +472,58 @@
                 v-hasPermi="['course:period:add']"
               >添加课程</el-button>
             </el-col>
+            <el-col :span="1.5">
+              <el-button
+                type="primary"
+                size="mini"
+                :disabled="updateCourse.ids.length <= 0"
+                @click="handleUpdateCourse"
+                v-hasPermi="['course:period:add']"
+              >修改看课时间</el-button>
+            </el-col>
           </el-row>
-          <el-table v-loading="course.loading" :data="course.list">
+          <el-table v-loading="course.loading" :data="course.list" @selection-change="handleSelectionCourseChange">
+            <el-table-column type="selection" width="55" align="center" />
             <el-table-column label="课程" align="center" prop="courseName" width="180" />
             <el-table-column label="小节" align="center" prop="videoName" />
-            <el-table-column label="营期时间" align="center" prop="dayDate" width="150" />
-            <el-table-column label="创建时间" align="center" prop="createTime" width="150" />
+            <el-table-column label="营期时间" align="center" prop="dayDate" />
+            <el-table-column label="开始时间" align="center" prop="startDateTime" width="100">
+              <template slot-scope="scope">
+                <el-tag>{{parseTime(scope.row.startDateTime, '{h}:{i}:{s}')}}</el-tag>
+              </template>
+            </el-table-column>
+            <el-table-column label="结束时间" align="center" prop="endDateTime" width="100">
+              <template slot-scope="scope">
+                <el-tag type="success">{{parseTime(scope.row.endDateTime, '{h}:{i}:{s}')}}</el-tag>
+              </template>
+            </el-table-column>
+            <el-table-column label="领取红包时间" align="center" prop="lastJoinTime" width="100">
+              <template slot-scope="scope">
+                <el-tag type="danger">{{parseTime(scope.row.lastJoinTime, '{h}:{i}:{s}')}}</el-tag>
+              </template>
+            </el-table-column>
+            <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+              <template slot-scope="scope">
+                <el-button
+                  size="mini"
+                  type="text"
+                  icon="el-icon-top"
+                  @click="handleTop(scope.row)"
+                >上移</el-button>
+              <el-button
+                size="mini"
+                type="text"
+                icon="el-icon-bottom"
+                @click="handleBottom(scope.row)"
+              >下移</el-button>
+<!--              <el-button-->
+<!--                size="mini"-->
+<!--                type="text"-->
+<!--                icon="el-icon-edit"-->
+<!--                @click="handleUpdateDate(scope.row)"-->
+<!--              >修改营期时间</el-button>-->
+              </template>
+            </el-table-column>
           </el-table>
         </el-tab-pane>
         <el-tab-pane label="公司列表" name="company">
@@ -432,7 +554,7 @@
 </template>
 
 <script>
-import {addPeriod, delPeriod, exportPeriod, getPeriod, pagePeriod, updatePeriod, getDays, addCourse, updateListCourseData} from "@/api/course/userCoursePeriod";
+import {addPeriod, delPeriod, exportPeriod, getPeriod, pagePeriod, updatePeriod, getDays, addCourse, updateCourseTime, updateCourseDate, updateListCourseData, periodCourseMove, closePeriod} from "@/api/course/userCoursePeriod";
 import {getCompanyList} from "@/api/company/company";
 import { listCamp, addCamp, editCamp, delCamp, copyCamp } from "@/api/course/userCourseCamp";
 import { courseList,videoList } from '@/api/course/courseRedPacketLog'
@@ -451,6 +573,7 @@ export default {
     return {
       // 遮罩层
       loading: true,
+      updateDateOpen: false,
       // 左侧遮罩层
       leftLoading: true,
       // 选中数组
@@ -472,6 +595,7 @@ export default {
       videoList: [],
       // 弹出层标题
       title: "",
+      isDisabledDateRange: false, //是否禁用开营日期
       // 是否显示弹出层
       open: false,
       // 查询参数
@@ -506,6 +630,12 @@ export default {
         addOpen: false,
         form: {},
       },
+      updateCourse: {
+        open: false,
+        loading: true,
+        ids: [],
+        form: {},
+      },
       // 表单校验
       rules: {
       },
@@ -640,11 +770,15 @@ export default {
       this.selectedPeriods = selection;
       this.batchSetRedPacketDisabled = selection.length === 0;
     },
+    handleSelectionCourseChange(selection) {
+      this.updateCourse.ids = selection.map(item => item.id)
+    },
     /** 新增按钮操作 */
     handleAdd() {
       this.reset();
       this.open = true;
       this.title = "添加会员营期";
+      this.isDisabledDateRange = false;
     },
     /** 修改按钮操作 */
     handleUpdate(row) {
@@ -652,6 +786,13 @@ export default {
       const periodId = row.periodId || this.ids
       getPeriod(periodId).then(response => {
         this.form = response.data;
+        if (this.form.companyId) {
+          this.form.companyId = this.form.companyId.split(',').map(id => Number(id));
+        }
+        // 设置看课时间范围(回显)
+        if (this.form.viewStartTime && this.form.viewEndTime) {
+          this.form.timeRange = [this.form.viewStartTime, this.form.viewEndTime];
+        }
         if(this.form.periodType == 1){
           this.form.dateRange = [this.form.periodStartingTime, this.form.periodEndTime];
         }
@@ -660,6 +801,7 @@ export default {
         }
         this.open = true;
         this.title = "修改会员营期";
+        this.isDisabledDateRange = true;
       });
     },
     /** 提交按钮 */
@@ -667,6 +809,11 @@ export default {
       this.$refs["form"].validate(valid => {
         if (valid) {
           let data = JSON.parse(JSON.stringify(this.form));
+          // 处理看课时间范围
+          if (data.timeRange && data.timeRange.length === 2) {
+            data.viewStartTime = data.timeRange[0];
+            data.viewEndTime = data.timeRange[1];
+          }
           data.companyId = data.companyId.join()
           data.trainingCampId = this.queryParams.trainingCampId
           if (data.periodId != null) {
@@ -691,8 +838,14 @@ export default {
     },
     /** 删除按钮操作 */
     handleDelete(row) {
+      //添加删除判断,只能删除未开始的营期
+      console.log(row.periodStatus)
+      if(row.periodStatus !== 1){
+        this.$message.error('营期处于进行中或者结束,不能删除');
+        return;
+      }
       const periodIds = row.periodId || this.ids;
-      this.$confirm('是否确认删除会员营期编号为"' + periodIds + '"的数据项?', "警告", {
+      this.$confirm('是否确认删除该营期?', "警告", {
           confirmButtonText: "确定",
           cancelButtonText: "取消",
           type: "warning"
@@ -771,7 +924,11 @@ export default {
         dateRange: [],
         date: null,
         days: [],
-        periodEndTime: null
+        periodEndTime: null,
+        timeRange: [], // 看课时间范围
+        viewStartTime: null, // 看课开始时间
+        viewEndTime: null, // 看课结束时间
+        lastJoinTime: null // 领取红包时间
       };
       this.resetForm("form");
     },
@@ -1085,6 +1242,13 @@ export default {
         }
       });
     },
+    handleUpdateCourse() {
+      this.updateCourse.open = true;
+      this.updateCourse.form = {
+        ids: this.updateCourse.ids,
+        joinTime: [],
+      };
+    },
     closeAddCourse() {
       this.course.addOpen = false;
       this.course.form = {
@@ -1097,6 +1261,9 @@ export default {
         this.$refs.courseAddForm.resetFields();
       }
     },
+    closeUpdateCourse() {
+      this.course.open = false;
+    },
     courseChange(row){
       this.course.form.videoIds = [];
       videoList(row).then(response => {
@@ -1106,6 +1273,10 @@ export default {
     submitCourseForm(){
       this.$refs.courseAddForm.validate(valid => {
         if (valid) {
+          if(this.course.form.timeRange != null && this.course.form.timeRange.length === 2){
+            this.course.form.startTime = this.course.form.timeRange[0];
+            this.course.form.endTime1 = this.course.form.timeRange[1];
+          }
           // 提交数据
           addCourse(this.course.form).then(response => {
             this.$message.success('添加成功');
@@ -1116,6 +1287,31 @@ export default {
         }
       });
     },
+    submitUpdateCourseForm(){
+      this.$refs.courseUpdateForm.validate(valid => {
+        if (valid) {
+          if(this.updateCourse.form.timeRange != null && this.updateCourse.form.timeRange.length === 2){
+            this.updateCourse.form.startTime = this.updateCourse.form.timeRange[0];
+            this.updateCourse.form.endTime1 = this.updateCourse.form.timeRange[1];
+          }
+          // 提交数据
+          updateCourseTime(this.updateCourse.form).then(response => {
+            this.$message.success('添加成功');
+            this.updateCourse.open = false;
+            // 重新加载训练营列表
+            this.getCourseList();
+          });
+        }
+      });
+    },
+    updateDate(){
+      updateCourseDate(this.form).then(response => {
+          this.$message.success('修改成功');
+          this.updateDateOpen = false;
+          // 重新加载训练营列表
+          this.getCourseList();
+        });
+    },
     saveCourseData(){
       updateListCourseData(this.course.list).then(response => {
         this.$message.success('保存成功');
@@ -1140,6 +1336,23 @@ export default {
       // 根据当前激活的tab加载对应数据
       this.handleTabClick({ name: this.activeTab });
     },
+    // 结束营期
+    handleClosePeriod(row) {
+      const msg = `注: 1.确认结束营期,该营期的开营结束时间改为当天24点。2.当天正在播放中的课程不变。3.第二天如有未开始的课程,统一改为已结束。是否确认结束 ${row.periodName} 营期吗?`
+      this.$confirm(msg, "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        closePeriod({id: row.periodId}).then(response => {
+          if (response.code === 200) {
+            this.getList()
+          } else {
+            this.$message.error(response.msg)
+          }
+        })
+      }).catch(() => {})
+    },
     handleBatchRedPacketSuccess() {
       this.batchRedPacketVisible = false;
       this.getCourseList();
@@ -1152,6 +1365,85 @@ export default {
         this.redPacketVisible = true;
       }
     },
+    /** 上移课程 */
+    handleTop(row) {
+      const currentIndex = this.course.list.findIndex(item => item.id === row.id);
+      if (currentIndex <= 0) {
+        this.$message.warning('已经是第一条数据');
+        return;
+      }
+
+      // 获取上一条数据
+      const prevRow = this.course.list[currentIndex - 1];
+      console.log({
+        id: row.id,
+        targetId: prevRow.id,
+        type: 1 //上移
+      })
+      periodCourseMove({
+        id: row.id,
+        targetId: prevRow.id,
+        type: 1 //上移
+      }).then(response => {
+        if (response.code === 200) {
+          this.$message.success('上移成功');
+          this.getCourseList();
+        } else {
+          this.$message.error(response.msg || '上移失败');
+        }
+      }).catch(() => {
+        this.$message.error('上移失败');
+      });
+    },
+    /** 下移课程 */
+    handleBottom(row) {
+      const currentIndex = this.course.list.findIndex(item => item.id === row.id);
+      if (currentIndex === -1 || currentIndex >= this.course.list.length - 1) {
+        this.$message.warning('已经是最后一条数据');
+        return;
+      }
+
+      // 获取下一条数据
+      const nextRow = this.course.list[currentIndex + 1];
+
+      periodCourseMove({
+        id: row.id,
+        targetId: nextRow.id,
+        type: 2 //下移
+      }).then(response => {
+        if (response.code === 200) {
+          this.$message.success('下移成功');
+          this.getCourseList(); // 重新加载列表
+        } else {
+          this.$message.error(response.msg || '下移失败');
+        }
+      }).catch(() => {
+        this.$message.error('下移失败');
+      });
+    },
+    /** 营期状态格式化 */
+    periodStatusFormatter(row) {
+      const statusMap = {
+        1: '未开始',
+        2: '进行中',
+        3: '已结束'
+      };
+      return statusMap[row.periodStatus] || '未知状态';
+    },
+    /** 开课状态格式化 */
+    courseStatusFormatter(row) {
+      const statusMap = {
+        0: '未开始',
+        1: '进行中',
+        2: '已结束'
+      };
+      return statusMap[row.status] || '未知状态';
+    },
+    /** 营期状态格式化 */
+    handleUpdateDate(row) {
+      this.form = {id: row.id, dayDate: row.dayDate};
+      this.updateDateOpen = true;
+    },
   },
 };
 </script>

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

@@ -11,7 +11,7 @@
               size="mini"
               type="text"
               @click="handleInputAmount(scope.row)"
-            >录入金额</el-button>
+            >设置红包</el-button>
           </template>
         </el-table-column>
       </el-table>

+ 57 - 38
src/views/course/videoResource/index.vue

@@ -84,11 +84,6 @@
                 {{ scope.$index + 1 }}
               </template>
             </el-table-column>
-      <el-table-column label="主键ID" align="center" width="80">
-        <template slot-scope="scope">
-          <a @click="copy(scope.row.id)" style="color: #409EFF;">ID</a>
-        </template>
-      </el-table-column>
       <el-table-column label="素材名称" align="center" :show-overflow-tooltip="true" prop="resourceName" width="300"/>
       <el-table-column label="文件名称" align="center" :show-overflow-tooltip="true" prop="fileName" width="300"/>
       <el-table-column label="分类" align="center" width="120">
@@ -122,7 +117,7 @@
       <el-table-column label="关联题目" align="center" width="150">
         <template slot-scope="scope">
           <a
-            @click="handleViewProject(scope.row)"
+            @click="handleViewProject(scope.row, 3)"
             :style="scope.row.projectIds ? {
               backgroundColor: '#409EFF',
               color: 'white',
@@ -137,7 +132,7 @@
               backgroundColor: 'rgb(154 156 159)',
               color: 'white',
               border: 'none',
-              cursor: 'not-allowed',
+              cursor: 'pointer',
               borderRadius: '4px',
               padding: '4px 12px',
               fontSize: '12px',
@@ -188,7 +183,7 @@
           <el-input v-model="form.resourceName" placeholder="请输入" />
         </el-form-item>
 
-        <el-form-item label="文件名称" prop="fileName" style="margin-top: 20px">
+        <el-form-item label="文件名称" prop="fileName" style="margin-top: 20px;display: none">
           <el-input v-model="form.fileName" placeholder="请输入" />
         </el-form-item>
 
@@ -330,8 +325,7 @@
         <el-table-column label="关联项目" align="center" min-width="100">
           <template slot-scope="scope">
           <a
-            :diabled="scope.row.progress < 100"
-            @click="handleViewProject(scope.row)"
+            @click="handleViewProject(scope.row, 4)"
             :style="scope.row.projectIds.length > 0 ? {
               backgroundColor: '#409EFF',
               color: 'white',
@@ -346,7 +340,7 @@
               backgroundColor: 'rgb(154 156 159)',
               color: 'white',
               border: 'none',
-              cursor: 'not-allowed',
+              cursor: 'pointer',
               borderRadius: '4px',
               padding: '4px 12px',
               fontSize: '12px',
@@ -428,7 +422,7 @@
             </el-select>
           </el-form-item>
 
-          <el-form-item label="关联题目" prop="projectIds">
+          <el-form-item label="关联题目" prop="projectIds" style="display: none">
             <el-select
               ref="customSelect"
               class="custom-select-class"
@@ -469,7 +463,7 @@
             <el-input v-model="batchEditDialog.form.resourceName" placeholder="请输入" />
           </el-form-item>
 
-          <el-form-item label="文件名称" prop="fileName" style="margin-top: 20px">
+          <el-form-item label="文件名称" prop="fileName" style="margin-top: 20px;display: none">
             <el-input v-model="batchEditDialog.form.fileName" placeholder="请输入" />
           </el-form-item>
 
@@ -731,10 +725,6 @@ export default {
       batchAddVisible: false,
       batchLoading: false,
       videoList: [],
-      batchForm: {
-        resourceName: null,
-        typeId: null
-      },
 
       // 批量上传相关
       showUpload: false,
@@ -777,7 +767,7 @@ export default {
             { required: true, message: "文件名称不能为空", trigger: "blur" }
           ]
         },
-      }
+      },
     }
   },
   watch: {
@@ -892,9 +882,9 @@ export default {
       const id = row.id
 
       // 获取数据并设置表单
-      getVideoResource(id).then(response => {
+      getVideoResource(id).then(async response => {
         this.form = response.data;
-        this.changeCateType(this.form.typeId)
+        await this.changeCateType(this.form.typeId)
 
         // 处理projectIds,确保是数组格式
         if (this.form.projectIds && typeof this.form.projectIds === 'string') {
@@ -906,7 +896,7 @@ export default {
         // 如果存在关联项目,获取项目详情用于回显
         if (this.form.projectIds && this.form.projectIds.length > 0) {
           // 加载项目列表信息用于回显
-          getByIds({ids: this.form.projectIds.join(',')}).then(reponse => {
+          await getByIds({ids: this.form.projectIds.join(',')}).then(reponse => {
             this.projectShowList = reponse.data
           });
         }
@@ -982,12 +972,12 @@ export default {
         this.rootTypeList = response.data
       });
     },
-    changeCateType(val) {
+    async changeCateType(val) {
       if (!val) {
         this.subTypeList = []
         return
       }
-      getCateListByPid(val).then(response => {
+      await getCateListByPid(val).then(response => {
         this.subTypeList = response.data
       })
     },
@@ -1023,7 +1013,6 @@ export default {
     //获取第一帧封面
     async getFirstThumbnail(file, form){
       getThumbnail(file).then(response => {
-        console.log("获取到第一帧为封面=======>",response.url)
         form.thumbnail = response.url;
       })
     },
@@ -1032,7 +1021,7 @@ export default {
       try {
         const data = await uploadObject(file, (progress) => {
           const progressEvent = {
-            percent: Math.floor(progress.percent * 100) / 2, // COS SDK 百分比是 0-1,el-upload 需要 0-100
+            percent: Math.floor(progress.percent * 100 / 2), // COS SDK 百分比是 0-1,el-upload 需要 0-100
             loaded: progress.loaded,
             total: progress.total,
             lengthComputable: true // 文件上传通常总大小可知
@@ -1040,7 +1029,7 @@ export default {
           onProgress(progressEvent);
         }, 1);
 
-        let line_1 = `https://blytcpv.ylrzcloud.com${data.urlPath}`;
+        let line_1 = `https://rttcpv.ylrzcloud.com${data.urlPath}`;
 
         form.fileKey = data.urlPath.substring(1);
         form.videoUrl = line_1;
@@ -1064,7 +1053,7 @@ export default {
           onProgress(progressEvent);
         }, 1);
 
-        form.line2 = `https://blyobs.ylrztop.com/${data.urlPath}`;
+        form.line2 = `https://rtobs.ylrztop.com/${data.urlPath}`;
         this.$message.success("线路二上传成功");
       } catch (error) {
         this.$message.error("线路二上传失败");
@@ -1107,13 +1096,13 @@ export default {
     /** 批量新增 */
     handleBatchAdd() {
       this.batchAddVisible = true;
-      this.batchForm = {
-        resourceName: null,
-        typeId: null
-      };
       this.videoList = []; // 清空之前的视频列表
     },
     cancelBeforeBatch(done, cancel) {
+      if (!this.videoList || this.videoList.length === 0) {
+        done()
+        return
+      }
       this.$confirm('关闭窗口视频需要重新上传,确定关闭吗?', '提示', {
         confirmButtonText: '确定',
         cancelButtonText: '取消',
@@ -1126,6 +1115,10 @@ export default {
     },
     /** 取消批量 */
     cancelBatch() {
+      if (!this.videoList || this.videoList.length === 0) {
+        this.batchAddVisible = false
+        return
+      }
       this.$confirm('关闭窗口视频需要重新上传,确定关闭吗?', '提示', {
         confirmButtonText: '确定',
         cancelButtonText: '取消',
@@ -1177,7 +1170,7 @@ export default {
       this.batchEditDialog.open = false
     },
     batchEditSubmitForm() {
-      let temp = this.videoList.find(item => item.fileKey === this.batchEditDialog.form.fileKey)
+      let temp = this.videoList.find(item => item.tempId === this.batchEditDialog.form.tempId)
       Object.assign(temp, this.batchEditDialog.form)
       this.batchEditDialog.open = false
     },
@@ -1191,7 +1184,7 @@ export default {
         // 从视频列表中删除该项
         const index = this.videoList.findIndex(item => {
           // 通过文件名和大小确定唯一性,因为临时视频可能没有id
-          return item.fileKey === row.fileKey
+          return item.tempId === row.tempId
         });
 
         if (index !== -1) {
@@ -1232,6 +1225,7 @@ export default {
 
       // 创建临时视频对象添加到列表中
       const tempVideo = {
+        tempId: Math.random().toString(36).substring(2, 15),
         resourceName: file.name.split('.')[0], // 使用文件名作为视频名称
         fileName: file.name,
         thumbnail: null,
@@ -1256,7 +1250,7 @@ export default {
       const video = document.createElement('video');
       video.preload = 'metadata';
       video.onloadedmetadata = () => {
-        const index = this.videoList.findIndex(item => item.resourceName === tempVideo.resourceName);
+        const index = this.videoList.findIndex(item => item.tempId === tempVideo.tempId);
         if (index !== -1) {
           tempVideo.duration = Math.round(video.duration);
         }
@@ -1337,7 +1331,9 @@ export default {
     /** 打开项目选择弹窗 */
     openProjectDialog(projectIds, type) {
       this.$nextTick(() => {
-        this.$refs.customSelect.blur();
+        if (this.$refs.customSelect) {
+          this.$refs.customSelect.blur();
+        }
       });
       // 重置查询参数
       this.projectQueryParams = {
@@ -1377,12 +1373,34 @@ export default {
       // 更新表单中的项目ID
       if (this.selectedType === 0) {
         this.form.projectIds = this.selectedProjectIds;
-      } else if (this.selectedType === 1) {
+      }
+
+      else if (this.selectedType === 1) {
         this.batchUploadForm.projectIds = this.selectedProjectIds;
-      } else if (this.selectedType === 2) {
+      }
+
+      else if (this.selectedType === 2) {
         this.batchEditDialog.form.projectIds = this.selectedProjectIds;
       }
 
+      else if (this.selectedType === 3) {
+        const params = {
+          id: this.currentRow.id,
+          projectIds: this.selectedProjectIds.join(",")
+        }
+        updateVideoResource(params).then(response => {
+          if (response.code === 200) {
+            this.msgSuccess("修改成功");
+            this.open = false;
+            this.getList();
+          }
+        });
+      }
+
+      else if (this.selectedType === 4) {
+        this.currentRow.projectIds = this.selectedProjectIds
+      }
+
       this.projectDialogVisible = false;
     },
 
@@ -1418,7 +1436,7 @@ export default {
     },
 
     /** 处理查看项目 */
-    handleViewProject(row) {
+    handleViewProject(row, type) {
       // 保存当前选择的行,以便后续操作
       this.currentRow = row;
 
@@ -1426,6 +1444,7 @@ export default {
       this.projectShowList = [];
 
       if (!row.projectIds || row.projectIds.length === 0) {
+        this.openProjectDialog(null, type)
         return;
       }
 

+ 1 - 1
src/views/fs/user/index.vue

@@ -119,7 +119,7 @@
             <el-option
               v-for="item in companyUserOptions"
               :key="item.userId"
-              :label="item.nickName"
+              :label="item.nickName + '_' + item.userName"
               :value="item.userId"
             />
           </el-select>

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

@@ -9,6 +9,7 @@
       :on-error="handleUploadError"
       :on-exceed="handleExceed"
       name="file"
+      :disabled="disabled"
       :on-remove="handleRemove"
       :show-file-list="true"
       :file-list="fileList"
@@ -54,6 +55,11 @@ export default {
       type: Array,
       default: () => ["png", "jpg", "jpeg"],
     },
+    // 大小限制(MB)
+    disabled: {
+      type: Boolean,
+      default: false,
+    },
     // 是否显示提示
     isShowTip: {
       type: Boolean,
@@ -112,12 +118,14 @@ export default {
       if(findex > -1) {
         this.fileList.splice(findex, 1);
         this.$emit("input", this.listToString(this.fileList));
+        this.$emit("change", this.listToString(this.fileList));    // 新增change事件触发
       }
     },
     // 上传成功回调
     handleUploadSuccess(res,file) {
       this.fileList.push({ name: res.url, url: res.url });
       this.$emit("input", this.listToString(this.fileList));
+      this.$emit("change", this.listToString(this.fileList));    // 新增change事件触发
       this.loading.close();
     },
     // 上传前loading加载

+ 38 - 22
src/views/qw/sopTemp/index.vue

@@ -2,7 +2,7 @@
   <div class="app-container">
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
       <el-form-item label="销售公司" prop="companyId">
-        <el-select filterable  v-model="queryParams.companyId" clearable placeholder="请选择公司名" size="small">
+        <el-select filterable v-model="queryParams.companyId" clearable placeholder="请选择公司名" size="small">
           <el-option
             v-for="item in companys"
             :key="item.companyId"
@@ -122,7 +122,7 @@
       <el-table-column label="模板编号" align="center" prop="id"/>
       <el-table-column label="销售公司" align="center">
         <template slot-scope="scope">
-          <el-tag v-for="item in companys" v-if="scope.row.companyId == item.companyId">{{item.companyName}}</el-tag>
+          <el-tag v-for="item in companys" v-if="scope.row.companyId == item.companyId">{{ item.companyName }}</el-tag>
         </template>
       </el-table-column>
       <el-table-column label="模板标题" align="center" prop="name"/>
@@ -210,7 +210,7 @@
           <el-input v-model="form.name" placeholder="请输入模板标题"/>
         </el-form-item>
         <el-form-item label="销售公司" prop="companyId">
-          <el-select filterable  v-model="form.companyId" placeholder="请选择公司名" size="small">
+          <el-select filterable v-model="form.companyId" placeholder="请选择公司名" size="small">
             <el-option
               v-for="item in companys"
               :key="item.companyId"
@@ -240,7 +240,8 @@
           </el-select>
         </el-form-item>
         <el-form-item label="课程" prop="courseId" v-if="form.sendType == 5 && !form.id">
-          <el-select v-model="form.courseId"placeholder="请选择课程" style=" margin-right: 10px;" size="mini" filterable>
+          <el-select v-model="form.courseId" placeholder="请选择课程" style=" margin-right: 10px;" size="mini"
+                     filterable>
             <el-option
               v-for="dict in courseList"
               :key="dict.dictValue"
@@ -256,19 +257,19 @@
           <el-input-number v-model="form.sort" :min="0" label="排序"></el-input-number>
         </el-form-item>
         <el-form-item label="每天发送次数" prop="num" v-if="form.sendType == 5 && !form.id">
-          <el-input-number v-model="form.num" :min="1" label="每天发送次数"></el-input-number>
+          <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 == 5 && !form.id">
-            <el-time-picker
-              class="custom-input"
-              v-for="index in form.num"
-              v-model="form.timeList[index]"
-              value-format="HH:mm"
-              format="HH:mm"
-              :picker-options="{ selectableRange: startTimeRange }"
-              placeholder="时间"
-              style="width: 100px;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: 100px;height: 20px;margin-left: 10px;margin-top: 10px">
+          </el-time-picker>
         </el-form-item>
       </el-form>
       <div slot="footer" class="dialog-footer" style="float: right;">
@@ -282,7 +283,8 @@
         <el-table-column label="小节" align="left" prop="videoName"/>
         <el-table-column label="金额" align="center">
           <template slot-scope="scope">
-            <el-input-number v-model="scope.row.redPacketMoney" :min="0.01" step="0.01" label="红包金额"></el-input-number>
+            <el-input-number v-model="scope.row.redPacketMoney" :min="0.01" step="0.01"
+                             label="红包金额"></el-input-number>
           </template>
         </el-table-column>
       </el-table>
@@ -307,7 +309,7 @@ import {
   updateRedPackage,
   updateTemp
 } from "@/api/qw/sopTemp";
-import { getCompanyList } from "@/api/company/company";
+import {getCompanyList} from "@/api/company/company";
 import {courseList} from "@/api/qw/sop";
 import fa from "element-ui/src/locale/lang/fa";
 
@@ -452,7 +454,7 @@ export default {
         sendType: this.sendType,
         sort: 0,
         num: 1,
-        timeList:[],
+        timeList: [{value: ""}],
         status: "1",
       };
       this.resetForm("form");
@@ -578,6 +580,10 @@ export default {
       delete this.form.rules
       this.$refs["form"].validate(valid => {
         if (valid) {
+          let f = JSON.parse(JSON.stringify(this.form));
+          if (f.timeList && f.timeList.length > 0) {
+            f.timeList = f.timeList.map(item => item.value);
+          }
           const loading = this.$loading({
             lock: true,
             text: 'Loading',
@@ -585,22 +591,22 @@ export default {
             background: 'rgba(0, 0, 0, 0.7)'
           });
           if (this.command == 2) {
-            copyTemplate(this.form).then(response => {
+            copyTemplate(f).then(response => {
               this.msgSuccess("复制成功");
               this.open = false;
               loading.close();
               this.getList();
             });
           } else {
-            if (this.form.id != null) {
-              updateTemp(this.form).then(response => {
+            if (f.id != null) {
+              updateTemp(f).then(response => {
                 this.msgSuccess("修改成功");
                 this.open = false;
                 loading.close();
                 this.getList();
               });
             } else {
-              addTemp(this.form).then(response => {
+              addTemp(f).then(response => {
                 this.msgSuccess("新增成功");
                 this.open = false;
                 loading.close();
@@ -658,6 +664,16 @@ export default {
         this.exportLoading = false;
       }).catch(() => {
       });
+    },
+    sendNumChange(val, old) {
+      if (val > old) {
+        for (let i = 0; i < val - old; i++) {
+          this.form.timeList.push({value: ""});
+        }
+      } else {
+        let len = old - val;
+        this.form.timeList.splice(Math.max(this.form.timeList.length - len, 0), len);
+      }
     }
   }
 };

+ 60 - 26
src/views/qw/sopTemp/updateSopTemp.vue

@@ -52,7 +52,7 @@
                                   placeholder="内容名称,仅内部可见"/>
                       </el-form-item>
                       <el-form-item label="课程" v-if="form.sendType == 5 && item.content && item.content.length > 0" required>
-                        <el-select :disabled="formType == 3 || form.sendType == 5" v-model="item.content[0].courseId"
+                        <el-select :disabled="(formType == 3 || form.sendType == 5) && item.id != null" v-model="item.content[0].courseId"
                                    placeholder="请选择课程" style=" margin-right: 10px;" size="mini" remote
                                    filterable
                                    @change="courseChangeUpdate(item.content[0], index, 0)">
@@ -63,7 +63,7 @@
                             :value="parseInt(dict.dictValue)"
                           />
                         </el-select>
-                        <el-select :disabled="formType == 3 || form.sendType == 5" v-model="item.content[0].videoId"
+                        <el-select :disabled="(formType == 3 || form.sendType == 5) && item.id != null" v-model="item.content[0].videoId"
                                    placeholder="请选择小节" size="mini" style=" margin-right: 10px;" remote
                                    filterable
                                    @change="videoIdChange(item.content[0],index,0)">
@@ -74,17 +74,6 @@
                             :value="parseInt(dict.dictValue)"
                           />
                         </el-select>
-                        <el-select :disabled="formType == 3" v-model="item.content[0].courseType"
-                                   placeholder="请选择消息类型" size="mini"
-                                   style=" margin-right: 10px;" v-if="item.content[0].type != 4 ">
-                          <el-option
-                            v-for="dict in sysFsSopWatchStatus"
-                            :key="dict.dictValue"
-                            :label="dict.dictLabel"
-                            :value="Number(dict.dictValue)"
-                          />
-                        </el-select>
-
                       </el-form-item>
                       <el-form-item label="规则">
                         <div v-for="(content, contentIndex) in item.content" :key="contentIndex"
@@ -128,6 +117,18 @@
                                     inactive-value="0">
                                   </el-switch>
                                 </el-form-item>
+                                <el-form-item label="消息类型" v-if="form.sendType != 4">
+                                  <el-select :disabled="formType == 3" v-model="content.courseType"
+                                             placeholder="请选择消息类型" size="mini"
+                                             style=" margin-right: 10px;" v-if="content.type != 4 ">
+                                    <el-option
+                                      v-for="dict in sysFsSopWatchStatus"
+                                      :key="dict.dictValue"
+                                      :label="dict.dictLabel"
+                                      :value="Number(dict.dictValue)"
+                                    />
+                                  </el-select>
+                                </el-form-item>
 
                                 <el-form-item label="消息类别" v-if="form.sendType != 4 && form.sendType != 5">
                                   <el-radio-group v-model="content.type"
@@ -135,7 +136,6 @@
                                                   @change="updateHtml(() => content.contentType = '1')">
                                     <el-radio :label="1">普通</el-radio>
                                     <el-radio :label="2">课程</el-radio>
-                                    <el-radio :label="3">订单</el-radio>
                                     <el-radio :label="4">AI触达</el-radio>
                                     <el-radio :label="5">打标签</el-radio>
                                   </el-radio-group>
@@ -260,10 +260,10 @@
                                               </el-radio>
                                             </el-radio-group>
                                           </div>
-                                          <div v-if="form.sendType == 2 || form.sendType == 5">
+                                          <div v-if="form.sendType == 2">
                                             <el-radio-group v-model="setList.contentType"
                                                             :disabled="formType == 3"
-                                                            @change="handleContentTypeChange(content,index,contentIndex,setIndex)">
+                                                            @change="handleContentTypeChange(content,index,contentIndex,setIndex, item, 'contentType', $event)">
                                               <el-radio
                                                 :key="item.dictValue"
                                                 :label="item.dictValue"
@@ -272,6 +272,18 @@
                                               </el-radio>
                                             </el-radio-group>
                                           </div>
+                                          <div v-if="form.sendType == 5">
+                                            <el-radio-group v-model="setList.contentType"
+                                                            :disabled="formType == 3 || (form.sendType == 5 && contentIndex != 0 && setIndex == 0)"
+                                                            @change="handleContentTypeChange(content,index,contentIndex,setIndex, item, 'contentType', $event)">
+                                              <el-radio
+                                                :key="item.dictValue"
+                                                :label="item.dictValue"
+                                                :disabled="(content.type!=2 && item.dictValue === '9') || (content.isOfficial==1 && ['5','6','7','8','9'].includes(item.dictValue))"
+                                                v-for="item in sysQwSopAiContentType" v-if="setIndex == 0 ? courseTypeList.includes(item.dictValue) : !courseTypeList.includes(item.dictValue)">{{ item.dictLabel }}
+                                              </el-radio>
+                                            </el-radio-group>
+                                          </div>
                                         </el-form-item>
                                         <el-form-item label="内容">
                                           <el-input :disabled="formType == 3" v-if="setList.contentType == 1 "
@@ -300,19 +312,22 @@
                                             v-if="setList.contentType == 3  || (setList.contentType == 9 && content.type==2 )">
                                             <el-card class="box-card">
                                               <el-form-item label="链接标题:" label-width="100px" required>
-                                                <el-input :disabled="formType == 3" v-model="setList.linkTitle"
+                                                <el-input :disabled="formType == 3 || (form.sendType == 5 && contentIndex != 0 && setIndex == 0)" v-model="setList.linkTitle"
+                                                          @change="updateAll(setIndex, item, 'linkTitle', $event)"
                                                           placeholder="请输入链接标题"
                                                           style="width: 90%;"/>
                                               </el-form-item>
                                               <el-form-item label="链接描述:" label-width="100px" required>
-                                                <el-input :disabled="formType == 3" type="textarea" :rows="3"
+                                                <el-input :disabled="formType == 3 || (form.sendType == 5 && contentIndex != 0 && setIndex == 0)" type="textarea" :rows="3"
                                                           v-model="setList.linkDescribe"
+                                                          @change="updateAll(setIndex, item, 'linkDescribe', $event)"
                                                           placeholder="请输入链接描述"
                                                           style="width: 90%;margin-top: 1%;"/>
                                               </el-form-item>
                                               <el-form-item label="链接封面:" label-width="100px" required>
-                                                <ImageUpload :disabled="formType == 3" v-model="setList.linkImageUrl"
+                                                <ImageUpload :disabled="formType == 3 || (form.sendType == 5 && contentIndex != 0 && setIndex == 0)" v-model="setList.linkImageUrl"
                                                              type="image" :num="1"
+                                                             @input="updateAll(setIndex, item, 'linkImageUrl', $event)"
                                                              :file-size="2" :width="150" :height="150"
                                                              style="margin-top: 1%;"/>
                                               </el-form-item>
@@ -337,12 +352,16 @@
                                             <el-card class="box-card">
                                               <el-form-item label="标题" prop="miniprogramTitle">
                                                 <el-input v-model="setList.miniprogramTitle"
+                                                          :disabled="formType == 3 || (form.sendType == 5 && contentIndex != 0 && setIndex == 0)"
+                                                          @change="updateAll(setIndex, item, 'miniprogramTitle', $event)"
                                                           placeholder="请输入小程序消息标题,最长为64字节" :rows="2"
                                                           maxlength="64" type="textarea"
                                                           @input="checkByteLength(content,setList.contentType,content.isOfficial)"/>
                                               </el-form-item>
                                               <el-form-item label="封面" prop="miniprogramPicUrl">
                                                 <ImageUpload v-if="content.isOfficial !== '1'"
+                                                             @change="updateAll(setIndex, item, 'miniprogramPicUrl', $event)"
+                                                             :disabled="formType == 3 || (form.sendType == 5 && contentIndex != 0 && setIndex == 0)"
                                                              v-model="setList.miniprogramPicUrl" type="image" :num="10"
                                                              :width="150" :height="150"/>
                                               </el-form-item>
@@ -479,7 +498,7 @@
                                         <el-form-item label="添加短链"
                                                       v-if="content.type == 2 && setList.contentType == 1  ">
                                           <el-tooltip content="请先根据课程选定课程小节之后再添加" effect="dark"
-                                                      :disabled="!!content.videoId">
+                                                      :disabled="!content.videoId">
                                             <el-switch
                                               @change="updateHtml"
                                               v-model="setList.isBindUrl"
@@ -500,6 +519,8 @@
                                                       v-if="content.type == 2 && setList.isBindUrl == '1' && setList.contentType != 2  && setList.contentType != 5  && setList.contentType != 6 && setList.contentType != 8 && setList.contentType != 9 && setList.contentType != 10  ">
                                           <el-row>
                                             <el-input type="number" v-model="setList.expiresDays"
+                                                      :disabled="formType == 3 || (form.sendType == 5 && contentIndex != 0 && setIndex == 0)"
+                                                      @change="updateAll(setIndex, item, 'expiresDays', $event)"
                                                       style="width: 200px">
                                               <template slot="append">天</template>
                                             </el-input>
@@ -545,7 +566,7 @@
                                     <el-col :span="1" :offset="1">
                                       <i class="el-icon-delete" @click="delSetList(index,contentIndex,setIndex)"
                                          style="margin-top: 20px;"
-                                         v-if="content.setting.length>1 && (formType != 3)"></i>
+                                         v-if="content.setting.length>1 && formType != 3 && !(form.sendType == 5 && setIndex == 0)"></i>
                                     </el-col>
                                   </el-row>
                                 </div>
@@ -707,6 +728,7 @@ export default {
       dayList: [],
       ruleList: [],
       ids: [],
+      courseTypeList: ['3', '4', '9'],
       sysFsSopWatchStatus: [],
       //消息内容类型 企微版
       sysQwSopContentType: [],
@@ -1412,7 +1434,7 @@ export default {
         });
       }
     },
-    handleContentTypeChange(content, index, countIndex) {
+    handleContentTypeChange(content, index, countIndex, setIndex, item, fieldName, val) {
       //如果是链接的才上
       if (content.courseId != null && content.type == 2) {
         const selectedCourse = this.courseList.find(course => parseInt(course.dictValue) === content.courseId);
@@ -1450,7 +1472,14 @@ export default {
           }
         }
       }
-
+      if(countIndex == 0 && setIndex == 0){
+        this.updateAll(setIndex, item, fieldName, val);
+        for (let index in item.content) {
+          if(index != 0){
+            this.handleContentTypeChange(item.content[index], 0, index, 0, item, fieldName, val);
+          }
+        }
+      }
     },
     videoIdChange(content, index, countIndex) {
 
@@ -1699,9 +1728,14 @@ export default {
       val || val();
 
     },
-    // updateChange(val) {
-    //   console.log(val)
-    // }
+    updateAll(setIndex, list, fieldName, newVal) {
+      if(this.form.sendType == 5 && setIndex == 0) {
+        console.info("更新数据", newVal)
+        for (let index in list.content) {
+          this.$set(list.content[index].setting[0], fieldName, newVal);
+        }
+      }
+    }
   }
 };
 </script>

+ 1 - 0
src/views/sop/companySopRole/index.vue

@@ -197,6 +197,7 @@ export default {
     getList() {
       this.loading = true;
       listCompanySopRole(this.queryParams).then(response => {
+        console.info(response.rows)
         this.companySopRoleList = response.rows;
         this.total = response.total;
         this.loading = false;

+ 649 - 0
src/views/statistics/member/index.vue

@@ -0,0 +1,649 @@
+<template>
+  <div class="member-statistics-page">
+  <!-- 会员统计页面容器 -->
+   <div class="member-statistics-container">
+    <!-- =================== 顶部查询区域 =================== -->
+      <el-form class="filter-container" :inline="true" :model="queryParams" ref="queryForm">
+        <div class="filter-item">
+          <!-- 按日/按月切换 -->
+          <el-radio-group v-model="queryParams.dateType" size="small" @input="handleChangeDateType">
+            <el-radio-button label="day">按每日</el-radio-button>
+            <el-radio-button label="month">按每月</el-radio-button>
+          </el-radio-group>
+        </div>
+
+        <div class="filter-item">
+          <!-- 日期选择器 -->
+          <el-date-picker
+            v-model="queryParams.dateRange"
+            :key="queryParams.dateType"
+            :type="queryParams.dateType === 'day' ? 'daterange' : 'monthrange'"
+            size="small"
+            range-separator="至"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+            style="width: 260px;"
+            :clearable="false"
+          />
+        </div>
+
+        <!-- 经销商选择 -->
+        <el-form-item class="filter-item" label="经销商" prop="dealerId">
+          <!-- 经销商选择 -->
+          <el-select
+            v-model="queryParams.dealerId"
+            placeholder="请选择经销商"
+            size="small"
+            clearable filterable remote
+            :remote-method="loadCompanyOptions"
+            v-select-load-more="loadMoreCompanyOptions"
+            :loading="companyOptionsLoading"
+            @change="handleChangeDealer"
+            style="width: 140px;"
+          >
+            <el-option v-for="item in dealerOptions" :key="item.dictValue" :label="item.dictLabel" :value="item.dictValue" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item class="filter-item" label="群管" prop="groupId">
+          <!-- 分组选择 -->
+          <el-select
+            v-model="queryParams.groupId"
+            placeholder="请选择群管"
+            size="small"
+            clearable filterable remote
+            :remote-method="loadCompanyUserOptions"
+            v-select-load-more="loadMoreCompanyUserOptions"
+            :loading="companyUserOptionsLoading"
+            style="width: 140px;"
+          >
+            <el-option v-for="item in groupOptions" :key="item.dictValue" :label="item.dictLabel" :value="item.dictValue" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item class="filter-item" label="会员" prop="memberId">
+          <!-- 会员选择 -->
+          <el-select
+            v-model="queryParams.memberId"
+            placeholder="请选择会员"
+            size="small"
+            clearable filterable remote
+            :remote-method="loadUserOptions"
+            v-select-load-more="loadMoreUserOptions"
+            :loading="userOptionsLoading"
+            style="width: 140px;"
+          >
+            <el-option v-for="item in memberOptions" :key="item.dictValue" :label="item.dictLabel" :value="item.dictValue" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item class="filter-item" label="训练营" prop="trainerId">
+          <!-- 训练营选择 -->
+          <el-select
+            v-model="queryParams.trainerId"
+            placeholder="请选择训练营"
+            size="small"
+            clearable filterable remote
+            :remote-method="loadTrainerOptions"
+            v-select-load-more="loadMoreTrainerOptions"
+            :loading="trainerOptionsLoading"
+            @change="changeTrainer"
+            style="width: 140px;"
+          >
+            <el-option v-for="item in trainerOptions" :key="item.dictValue" :label="item.dictLabel" :value="item.dictValue" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item class="filter-item" label="营期" prop="campPeriod">
+          <!-- 营期选择 -->
+          <el-select
+            v-model="queryParams.campPeriod"
+            placeholder="请选择营期"
+            size="small"
+            clearable filterable remote
+            :remote-method="loadPeriodOptions"
+            v-select-load-more="loadMorePeriodOptions"
+            :loading="campPeriodOptionsLoading"
+            @change="changeCampPeriod"
+            style="width: 140px;"
+          >
+            <el-option v-for="item in campPeriodOptions" :key="item.dictValue" :label="item.dictLabel" :value="item.dictValue" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item class="filter-item" label="课程" prop="courseId">
+          <!-- 课程选择 -->
+          <el-select
+            v-model="queryParams.courseId"
+            placeholder="请选择课程"
+            size="small"
+            clearable filterable remote
+            :remote-method="loadCourseOptions"
+            v-select-load-more="loadMoreCourseOptions"
+            :loading="courseOptionsLoading"
+            style="width: 140px;"
+          >
+            <el-option v-for="item in courseOptions" :key="item.dictValue" :label="item.dictLabel" :value="item.dictValue" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item class="filter-item" label="手机号" prop="mobile">
+          <!-- 手机号输入框 -->
+          <el-input
+            v-model="queryParams.mobile"
+            placeholder="请输入手机号"
+            size="small"
+            clearable
+            style="width: 200px;"
+          />
+        </el-form-item>
+
+        <div class="filter-item">
+         <!-- 操作按钮组 -->
+         <el-button type="primary" icon="el-icon-search" size="small" @click="handleQuery">搜索</el-button>
+        </div>
+
+        <div class="filter-item" style="margin-left: auto; display: none;">
+         <el-button size="small">更换所属群管</el-button>
+         <el-button type="primary" size="small">导出全部</el-button>
+        </div>
+      </el-form>
+    </div>
+
+    <!-- =================== 表格数据展示区域 =================== -->
+    <div class="table-container">
+      <el-table
+        v-loading="loading"
+        :data="tableData"
+        border
+        style="width: 100%;"
+        show-summary
+        :summary-row-style="{ background: '#fafafa' }"
+      >
+        <!-- 选择列 -->
+        <el-table-column type="selection" width="50" align="center" />
+        <!-- 序号列 -->
+        <el-table-column label="序号" type="index" width="60" align="center" />
+        <!-- 统计日期列 -->
+        <el-table-column prop="date" label="统计日期" min-width="100" align="center" />
+        <!-- 会员名称列 -->
+        <el-table-column prop="memberName" label="会员名称" min-width="100" align="center" />
+        <!-- 性别列 -->
+        <el-table-column prop="gender" label="性别" width="60" align="center" />
+        <!-- 手机号列 -->
+        <el-table-column prop="mobile" label="手机号" min-width="100" align="center" />
+        <!-- 会员标签列 -->
+        <el-table-column prop="memberTag" label="会员标签" min-width="100" align="center" />
+        <!-- 所属群管列 -->
+        <el-table-column prop="groupName" label="所属群管" min-width="100" align="center" />
+        <!-- 所属经销商列 -->
+        <el-table-column prop="dealerName" label="所属经销商" min-width="100" align="center" />
+        <!-- 观看课程列 -->
+        <el-table-column prop="viewCount" label="观看课程" min-width="80" align="center" />
+        <!-- 完课课程列 -->
+        <el-table-column prop="finishCount" label="完课课程" min-width="80" align="center" />
+        <!-- 完课率列 -->
+        <el-table-column prop="finishRate" label="完课率" min-width="80" align="center" />
+        <!-- 观看次数列 -->
+        <el-table-column prop="viewTimes" label="观看次数" min-width="80" align="center" />
+        <!-- 完课次数列 -->
+        <el-table-column prop="finishTimes" label="完课次数" min-width="80" align="center" />
+        <!-- 视频率列 -->
+        <el-table-column prop="videoRate" label="视频率" min-width="80" align="center" />
+
+        <!-- 操作列 -->
+        <el-table-column label="操作" fixed="right" min-width="160" align="center">
+          <template slot-scope="scope">
+            <!-- 会员分析按钮 -->
+            <el-button type="text" size="small" style="color: #409EFF;">会员分析</el-button>
+            <!-- 红包记录按钮 -->
+            <el-button type="text" size="small" style="color: #409EFF;">红包记录</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination
+        v-show="queryParams.total > 0"
+        :total="queryParams.total"
+        :page.sync="queryParams.pageNum"
+        :limit.sync="queryParams.pageSize"
+        @pagination="getList"
+      />
+    </div>
+  </div>
+</template>
+
+<script>
+import {dailyData} from '@/api/statistics/member'
+import {getCompanyListLikeName} from '@/api/company/company'
+import {getCompanyUserListLikeName} from '@/api/company/companyUser'
+import {getUserListLikeName} from '@/api/users/user'
+import {getCampListLikeName} from '@/api/course/userCourseCamp'
+import {getPeriodListLikeName} from '@/api/course/userCoursePeriod'
+import {getVideoListLikeName} from '@/api/course/userCourseVideo'
+import moment from "moment"
+
+export default {
+  // 组件名称
+  name: 'MemberStatistics',
+
+  // 组件数据
+  data() {
+    return {
+      // ============= 查询参数 =============
+      queryParams: {
+        dateType: 'day', // 日期类型:day-按日,month-按月
+        dateRange: [new Date(), new Date()], // 日期范围
+        dealerId: '', // 经销商ID
+        groupId: '', // 群组ID
+        memberId: '', // 会员ID
+        trainerId: '', // 训练官ID
+        campPeriod: '', // 营期
+        courseId: '', // 课程ID
+        mobile: '', // 手机号
+        pageNum: 1, // 当前页码
+        pageSize: 30, // 每页记录数
+        total: 0, // 总记录数
+      },
+
+      // ============= 页面状态 =============
+      loading: false, // 加载状态
+
+      // ============= 数据相关 =============
+      tableData: [], // 表格数据
+
+      // ============= 下拉选项数据 =============
+      companyOptionsParams: {
+        name: undefined,
+        hasNextPage: false,
+        pageNum: 1,
+        pageSize: 10
+      },
+      companyOptionsLoading: false,
+      dealerOptions: [], // 经销商选项
+      companyUserOptionsParams: {
+        name: undefined,
+        companyId: undefined,
+        hasNextPage: false,
+        pageNum: 1,
+        pageSize: 10
+      },
+      companyUserOptionsLoading: false,
+      groupOptions: [], // 群组选项
+      userOptionsParams: {
+        name: undefined,
+        hasNextPage: false,
+        pageNum: 1,
+        pageSize: 10
+      },
+      userOptionsLoading: false,
+      memberOptions: [], // 会员选项
+      trainerOptionsParams: {
+        name: undefined,
+        hasNextPage: false,
+        pageNum: 1,
+        pageSize: 10
+      },
+      trainerOptionsLoading: false,
+      trainerOptions: [], // 训练营选项
+      campPeriodOptionsParams: {
+        name: undefined,
+        campId: undefined,
+        hasNextPage: false,
+        pageNum: 1,
+        pageSize: 10
+      },
+      campPeriodOptionsLoading: false,
+      campPeriodOptions: [], // 营期选项
+      courseOptionsParams: {
+        name: undefined,
+        periodId: undefined,
+        hasNextPage: false,
+        pageNum: 1,
+        pageSize: 10
+      },
+      courseOptionsLoading: false,
+      courseOptions: [] // 课程选项
+    }
+  },
+
+  // ============= 生命周期钩子 =============
+  created() {
+    // 页面创建时初始化数据
+    this.getList() // 加载表格数据
+  },
+
+  // ============= 组件方法 =============
+  methods: {
+    /**
+     * 获取表格数据
+     * 根据查询参数从服务器获取会员统计数据
+     */
+    getList() {
+      this.loading = true // 显示加载中状态
+      // 模拟异步请求
+      let query = {
+        type: this.queryParams.dateType === 'day' ? 1 : 2,
+        startDate: moment(this.queryParams.dateRange[0]).format('YYYY-MM-DD'),
+        endDate: moment(this.queryParams.dateRange[1]).format('YYYY-MM-DD'),
+        companyId: this.queryParams.dealerId,
+        companyUserId: this.queryParams.groupId,
+        userId: this.queryParams.memberId,
+        phone: this.queryParams.mobile,
+        trainCampId: this.queryParams.trainerId,
+        periodId: this.queryParams.campPeriod,
+        videoId: this.queryParams.courseId,
+        pageNum: this.queryParams.pageNum,
+        pageSize: this.queryParams.pageSize
+      }
+      dailyData(query).then(response => {
+        if (response && response.code === 200) {
+          this.tableData = response.data.list
+          this.queryParams.total = response.data.total
+          this.loading = false // 隐藏加载中状态
+        }
+      })
+    },
+
+    // 切换日期类型
+    handleChangeDateType() {
+      this.queryParams.dateRange  = [new Date(), new Date()]
+
+      // 重新请求数据
+      this.getList()
+    },
+
+    /**
+     * 查询按钮点击事件
+     * 根据筛选条件重新查询数据
+     */
+    handleQuery() {
+      this.queryParams.pageNum = 1 // 重置为第一页
+      this.getList() // 重新获取数据
+    },
+
+    /**
+     * 重置查询条件
+     * 清空所有筛选条件并重新加载数据
+     */
+    resetQuery() {
+      // 重置所有查询条件
+      this.queryParams = {
+        dateType: 'day', // 重置日期类型为按天
+        dateRange: [], // 清空日期范围
+        publicId: '', // 清空公众号ID
+        dealerId: '', // 清空经销商ID
+        groupId: '', // 清空群组ID
+        memberId: '', // 清空会员ID
+        trainerId: '', // 清空训练官ID
+        campPeriod: '', // 清空营期
+        courseId: '', // 清空课程ID
+        mobile: '' // 清空手机号
+      }
+      this.getList() // 重新获取数据
+    },
+
+    // 加载经销商选项
+    loadCompanyOptions(query) {
+      this.dealerOptions = [];
+      if (query === '') {
+        return;
+      }
+
+      this.companyOptionsParams.pageNum = 1
+      this.companyOptionsParams.name = query
+      this.companyOptionsLoading = true;
+      this.getCompanyListLikeName()
+    },
+    getCompanyListLikeName() {
+      getCompanyListLikeName(this.companyOptionsParams).then(response => {
+        this.dealerOptions = [...this.dealerOptions, ...response.data.list]
+        this.companyOptionsParams.hasNextPage = response.data.hasNextPage
+        this.companyOptionsLoading = false;
+      });
+    },
+    loadMoreCompanyOptions() {
+      if (!this.companyOptionsParams.hasNextPage) {
+        return;
+      }
+
+      this.companyOptionsParams.pageNum += 1
+      this.getCompanyListLikeName()
+    },
+    handleChangeDealer(val) {
+      this.groupOptions = [];
+      this.companyUserOptionsLoading = true;
+      if (!val) {
+        this.companyUserOptionsParams.companyId = undefined
+        this.getCompanyUserListLikeName()
+        return
+      }
+
+      this.companyUserOptionsParams.companyId = val
+      this.companyUserOptionsParams.pageNum = 1
+      this.getCompanyUserListLikeName()
+    },
+
+    // 群组选项
+    loadCompanyUserOptions(query) {
+      this.groupOptions = [];
+      if (query === '') {
+        return;
+      }
+
+      this.companyUserOptionsParams.pageNum = 1
+      this.companyUserOptionsParams.name = query
+      this.companyUserOptionsLoading = true;
+      this.getCompanyUserListLikeName()
+    },
+    getCompanyUserListLikeName() {
+      getCompanyUserListLikeName(this.companyUserOptionsParams).then(response => {
+        this.groupOptions = [...this.groupOptions, ...response.data.list]
+        this.companyUserOptionsParams.hasNextPage = response.data.hasNextPage
+        this.companyUserOptionsLoading = false;
+      });
+    },
+    loadMoreCompanyUserOptions() {
+      if (!this.companyUserOptionsParams.hasNextPage) {
+        return;
+      }
+
+      this.companyUserOptionsParams.pageNum += 1
+      this.getCompanyUserListLikeName()
+    },
+
+    // 会员选项
+    loadUserOptions(query) {
+      this.memberOptions = [];
+      if (query === '') {
+        return;
+      }
+
+      this.userOptionsParams.pageNum = 1
+      this.userOptionsParams.name = query
+      this.userOptionsLoading = true;
+      this.getUserListLikeName()
+    },
+    getUserListLikeName() {
+      getUserListLikeName(this.userOptionsParams).then(response => {
+        this.memberOptions = [...this.memberOptions, ...response.data.list]
+        this.userOptionsParams.hasNextPage = response.data.hasNextPage
+        this.userOptionsLoading = false;
+      });
+    },
+    loadMoreUserOptions() {
+      if (!this.userOptionsParams.hasNextPage) {
+        return;
+      }
+
+      this.userOptionsParams.pageNum += 1
+      this.getUserListLikeName()
+    },
+
+    // 训练营选项
+    loadTrainerOptions(query) {
+      this.trainerOptions = [];
+      if (query === '') {
+        return;
+      }
+
+      this.trainerOptionsParams.pageNum = 1
+      this.trainerOptionsParams.name = query
+      this.trainerOptionsLoading = true;
+      this.getTrainerListLikeName()
+    },
+    getTrainerListLikeName() {
+      getCampListLikeName(this.trainerOptionsParams).then(response => {
+        this.trainerOptions = [...this.trainerOptions, ...response.data.list]
+        this.trainerOptionsParams.hasNextPage = response.data.hasNextPage
+        this.trainerOptionsLoading = false;
+      });
+    },
+    loadMoreTrainerOptions() {
+      if (!this.trainerOptionsParams.hasNextPage) {
+        return;
+      }
+
+      this.trainerOptionsParams.pageNum += 1
+      this.getTrainerListLikeName()
+    },
+    changeTrainer(val) {
+      this.campPeriodOptions = [];
+      this.campPeriodOptionsLoading = true;
+      if (!val) {
+        this.campPeriodOptionsParams.companyId = undefined
+        this.getPeriodListLikeName()
+        return
+      }
+
+      this.campPeriodOptionsParams.companyId = val
+      this.campPeriodOptionsParams.pageNum = 1
+      this.getPeriodListLikeName()
+    },
+
+    // 营期选项
+    loadPeriodOptions(query) {
+      this.campPeriodOptions = [];
+      if (query === '') {
+        return;
+      }
+
+      this.campPeriodOptionsParams.pageNum = 1
+      this.campPeriodOptionsParams.name = query
+      this.campPeriodOptionsLoading = true;
+      this.getPeriodListLikeName()
+    },
+    getPeriodListLikeName() {
+      getPeriodListLikeName(this.campPeriodOptionsParams).then(response => {
+        this.campPeriodOptions = [...this.campPeriodOptions, ...response.data.list]
+        this.campPeriodOptionsParams.hasNextPage = response.data.hasNextPage
+        this.campPeriodOptionsLoading = false;
+      });
+    },
+    loadMorePeriodOptions() {
+      if (!this.campPeriodOptionsParams.hasNextPage) {
+        return;
+      }
+
+      this.campPeriodOptionsParams.pageNum += 1
+      this.getPeriodListLikeName()
+    },
+    changeCampPeriod(val) {
+      this.courseOptions = []
+      this.courseOptionsLoading = true;
+      if (!val) {
+        this.courseOptionsParams.periodId = undefined
+        this.getCourseListLikeName()
+        return
+      }
+
+      this.courseOptionsParams.periodId = val
+      this.courseOptionsParams.pageNum = 1
+      this.getCourseListLikeName()
+    },
+
+    // 课程选项
+    loadCourseOptions(query) {
+      this.courseOptions = [];
+      if (query === '') {
+        return;
+      }
+
+      this.courseOptionsParams.pageNum = 1
+      this.courseOptionsParams.name = query
+      this.courseOptionsLoading = true;
+      this.getCourseListLikeName()
+    },
+    getCourseListLikeName() {
+      getVideoListLikeName(this.courseOptionsParams).then(response => {
+        this.courseOptions = [...this.courseOptions, ...response.data.list]
+        this.courseOptionsParams.hasNextPage = response.data.hasNextPage
+        this.courseOptionsLoading = false;
+      });
+    },
+    loadMoreCourseOptions() {
+      if (!this.courseOptionsParams.hasNextPage) {
+        return;
+      }
+
+      this.courseOptionsParams.pageNum += 1
+      this.getCourseListLikeName()
+    },
+  }
+}
+</script>
+
+<style scoped>
+.member-statistics-page {
+  padding: 10px;
+}
+/* 会员统计页面容器样式 */
+.member-statistics-container {
+  padding: 10px;
+  background-color: #fff;
+}
+
+/* 筛选区域样式 */
+.filter-container {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  background-color: #fff;
+  padding: 15px;
+  border-radius: 4px;
+}
+
+.filter-item {
+  margin-right: 15px;
+  margin-bottom: 10px;
+}
+
+/* 表格容器样式 */
+.table-container {
+  background-color: #FFF;
+  height: 72vh;
+}
+
+/* 表格深度样式 */
+::v-deep .el-table th {
+  color: #606266;
+  font-weight: bold;
+}
+
+::v-deep .el-table--border th, ::v-deep .el-table--border td {
+  border-right: 1px solid #EBEEF5;
+}
+
+::v-deep .el-table {
+  border: 1px solid #EBEEF5;
+  border-bottom: none;
+}
+
+::v-deep .el-input__inner {
+  border-radius: 4px;
+}
+
+::v-deep .el-button {
+  border-radius: 4px;
+}
+</style>

+ 179 - 0
src/views/user/darkRoom/index.vue

@@ -0,0 +1,179 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px" @submit.native.prevent>
+      <el-form-item label="关键词" prop="keyword">
+        <el-input
+          style="width:220px"
+          v-model="queryParams.keyword"
+          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-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-button
+        size="mini"
+        type="primary"
+        style="margin-left: 5px"
+        :disabled="ids.length === 0"
+        @click="handleUpdateBatch"
+        v-hasPermi="['store:user:darkRoomList']"
+      >批量启用</el-button>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table  height="500" border v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="用户id" align="center" prop="userId" />
+      <el-table-column label="用户昵称" align="center" prop="nickname" />
+      <el-table-column label="用户头像" align="center" prop="avatar">
+        <template slot-scope="scope">
+          <el-popover trigger="hover" placement="top">
+            <el-image :src="scope.row.avatar" style="width: 200px;height: 200px"/>
+            <div slot="reference" class="name-wrapper">
+              <el-avatar shape="square" size="large" :src="scope.row.avatar"/>
+            </div>
+          </el-popover>
+        </template>
+      </el-table-column>
+      <el-table-column label="注册时间" align="center" prop="createTime" width="160px"/>
+      <el-table-column label="看课数量" align="center" prop="watchCourseCount" />
+      <el-table-column label="缺课数量" align="center" prop="missCourseCount" />
+      <el-table-column label="缺课状态" align="center" prop="missCourseStatus">
+        <template slot-scope="scope">
+          <el-tag effect="dark" type="danger"  v-if="scope.row.missCourseStatus === 1">已缺课</el-tag>
+          <el-tag effect="dark" type="success" v-else>未缺课</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="参与营期数量" align="center" prop="partCourseCount" width="100px"/>
+      <el-table-column label="最后一次看课时间" align="center" prop="lastWatchDate"  width="160px"/>
+      <el-table-column label="用户状态" align="center" prop="courseCountStatus">
+        <template slot-scope="scope">
+          <el-tag effect="dark" type="success" v-if="scope.row.courseCountStatus === 1">正常</el-tag>
+          <el-tag effect="dark" type="info"    v-else-if="scope.row.courseCountStatus === 2">停止</el-tag>
+          <el-tag effect="dark" type="danger"  v-else>未看</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="停课天数" align="center" prop="stopWatchDays" />
+      <el-table-column label="完播时间" align="center" prop="completeWatchDate" width="160px"/>
+      <el-table-column label="标签名称" align="center" prop="tag" />
+      <el-table-column label="所属销售名称" align="center" prop="companyUserNickName" width="120px"/>
+      <el-table-column label="备注" align="center" prop="remark" />
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['store:user:darkRoomList']"
+          >启用</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </div>
+</template>
+
+<script>
+import { darkRoomList,enabledUsers } from "@/api/store/user";
+
+export default {
+  name: "darkRoom",
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 用户列表数据
+      userList: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        keyword: null,
+        isBlack: true
+      },
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询客户列表 */
+    getList() {
+      this.loading = true;
+      darkRoomList(this.queryParams).then(response => {
+        this.userList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.userId)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    // 启用
+    handleUpdate(row) {
+      enabledUsers([row.userId]).then(response => {
+        if (response.code === 200) {
+          this.msgSuccess("取消禁用成功");
+          this.getList();
+        } else {
+          this.msgError(response.msg);
+        }
+      });
+    },
+    // 启用
+    handleUpdateBatch() {
+      enabledUsers(this.ids).then(response => {
+        if (response.code === 200) {
+          this.msgSuccess("取消禁用成功");
+          this.getList();
+        } else {
+          this.msgError(response.msg);
+        }
+      });
+    }
+  }
+};
+</script>
+<style scoped>
+.app-container {
+  height: 87.5vh;
+}
+</style>