Procházet zdrojové kódy

Merge remote-tracking branch 'origin/master'

peicj před 2 týdny
rodič
revize
1c74f23bdc

+ 40 - 0
.env.prod-cfryt

@@ -0,0 +1,40 @@
+# 页面标题
+VUE_APP_TITLE =赤峰润元堂
+# 首页菜单标题
+VUE_APP_TITLE_INDEX =赤峰润元堂
+# 公司名称
+VUE_APP_COMPANY_NAME =赤峰润元堂健康咨询有限公司
+# ICP备案号
+VUE_APP_ICP_RECORD =蒙ICP备2024019526号-1
+# ICP网站访问地址
+VUE_APP_ICP_URL =https://beian.miit.gov.cn
+# 网站LOG
+VUE_APP_LOG_URL =@/assets/logo/cfryt.png
+# 存储桶配置
+VUE_APP_OBS_ACCESS_KEY_ID = K2UTJGIN7UTZJR2XMXYG
+# 存储桶配置
+VUE_APP_OBS_SECRET_ACCESS_KEY = sbyeNJLbcYmH6copxeFP9pAoksM4NIT9Zw4x0SRX
+# 存储桶配置
+VUE_APP_OBS_SERVER = https://obs.cn-north-4.myhuaweicloud.com
+# 存储桶配置
+VUE_APP_OBS_BUCKET = cfryt-hw079058881
+# 存储桶配置
+VUE_APP_COS_BUCKET = cfryt-1323137866
+# 存储桶配置
+VUE_APP_COS_REGION = ap-chongqing
+# 线路一地址
+VUE_APP_VIDEO_LINE_1 = https://cfrytcpv.ylrzcloud.com
+# 线路二地址
+VUE_APP_VIDEO_LINE_2 = https://cqtytobs.ylrztop.com
+
+# 开发环境配置
+ENV = 'production'
+
+# FS管理系统/开发环境
+VUE_APP_BASE_API = '/prod-api'
+
+#默认 1、会员 2、企微
+VUE_APP_COURSE_DEFAULT = 1
+
+# 路由懒加载
+VUE_CLI_BABEL_TRANSPILE_MODULES = true

+ 40 - 0
.env.prod-gzzdy

@@ -0,0 +1,40 @@
+# 页面标题
+VUE_APP_TITLE =郑多燕总管理系统
+# 首页菜单标题
+VUE_APP_TITLE_INDEX =郑多燕管理系统
+# 公司名称
+VUE_APP_COMPANY_NAME =广州郑多燕健康管理有限公司
+# ICP备案号
+VUE_APP_ICP_RECORD =粤ICP备2023104913号-7
+# ICP网站访问地址
+VUE_APP_ICP_URL =https://beian.miit.gov.cn
+# 网站LOG
+VUE_APP_LOG_URL =@/assets/logo/gzzdy_logo.png
+# 存储桶配置
+VUE_APP_OBS_ACCESS_KEY_ID = K2UTJGIN7UTZJR2XMXYG
+# 存储桶配置
+VUE_APP_OBS_SECRET_ACCESS_KEY = sbyeNJLbcYmH6copxeFP9pAoksM4NIT9Zw4x0SRX
+# 存储桶配置
+VUE_APP_OBS_SERVER = https://obs.cn-north-4.myhuaweicloud.com
+# 存储桶配置
+VUE_APP_OBS_BUCKET = gzzdy1-hw079058881
+# 存储桶配置
+VUE_APP_COS_BUCKET = gzzdy-1323137866
+# 存储桶配置
+VUE_APP_COS_REGION = ap-chongqing
+# 线路一地址
+VUE_APP_VIDEO_LINE_1 = https://gzzdytcpv.ylrzcloud.com
+# 线路二地址
+VUE_APP_VIDEO_LINE_2 = https://gzzdyobs.ylrztop.com
+
+# 开发环境配置
+ENV = 'development'
+
+# FS管理系统/开发环境
+VUE_APP_BASE_API = '/prod-api'
+
+#默认 1、会员 2、企微
+VUE_APP_COURSE_DEFAULT = 2
+
+# 路由懒加载
+VUE_CLI_BABEL_TRANSPILE_MODULES = true

+ 2 - 0
package.json

@@ -50,6 +50,8 @@
     "build:prod-ddgy": "vue-cli-service build --mode prod-ddgy",
     "build:prod-yxj": "vue-cli-service build --mode prod-yxj",
     "build:prod-bjzm": "vue-cli-service build --mode prod-bjzm",
+    "build:prod-gzzdy": "vue-cli-service build --mode prod-gzzdy",
+    "build:prod-cfryt": "vue-cli-service build --mode prod-cfryt",
     "preview": "node build/index.js --preview",
     "lint": "eslint --ext .js,.vue src"
   },

+ 28 - 0
src/api/live/liveData.js

@@ -20,3 +20,31 @@ export function listLiveData(data) {
     data:data,
   })
 }
+
+export function getLiveDataDetailBySql(liveId) {
+  return request({
+    url: '/liveData/liveData/getLiveDataDetailBySql?liveId=' + liveId,
+    method: 'get'
+  })
+}
+
+export function getLiveUserDetailListBySql(liveId) {
+  return request({
+    url: '/liveData/liveData/getLiveUserDetailListBySql?liveId=' + liveId,
+    method: 'get'
+  })
+}
+
+export function getLiveDataDetailByServer(liveId) {
+  return request({
+    url: '/liveData/liveData/getLiveDataDetailByServer?liveId=' + liveId,
+    method: 'get'
+  })
+}
+
+export function getLiveUserDetailListByServer(liveId) {
+  return request({
+    url: '/liveData/liveData/getLiveUserDetailListByServer?liveId=' + liveId,
+    method: 'get'
+  })
+}

binární
src/assets/logo/cfryt.png


binární
src/assets/logo/gzzdy_logo.png


+ 225 - 3
src/views/components/course/userCourseCatalogDetails.vue

@@ -157,6 +157,89 @@
         </el-form-item>
         <el-form-item label="红包金额" prop="redPacketMoney">
           <el-input-number v-model="form.redPacketMoney" :min="0.1" :max="200" :step="0.1"></el-input-number>
+        </el-form-item>
+         <!-- v-if="!!form.randomRedPacketRulesArr" -->
+        <el-form-item label="随机红包金额"  >
+          <template >
+          <div v-for="(rule, index) in form.randomRedPacketRulesArr" :key="index" class="form-row">
+           <el-form-item
+            label="随机红包金额区间"
+            :prop="`randomRedPacketRulesArr.${index}.minAmount`"
+            :rules="[
+              { required: true, message: '请输入最小金额', trigger: 'blur' },
+              { validator: validateMinAmount, trigger: 'blur', index: index }
+            ]"
+            class="form-item-amount"
+          >
+            <el-input
+              v-model.number="rule.minAmount"
+              type="number"
+              :min="0.01"
+              :precision="2"
+              :step="0.01"
+              placeholder="最小金额"
+              size="small"
+              class="amount-input"
+              @input="handleAmountInput(rule, 'minAmount')"
+            ></el-input>
+            <span class="separator">-</span>
+            <el-input
+              v-model.number="rule.maxAmount"
+              type="number"
+              :min="rule.minAmount || 0.01"
+              :precision="2"
+              :step="0.01"
+              placeholder="最大金额"
+              size="small"
+              class="amount-input"
+              @input="handleAmountInput(rule, 'maxAmount')"
+            ></el-input>
+            <span class="suffix">元</span>
+          </el-form-item>
+              <el-form-item
+                label="随机权重"
+                :prop="`randomRedPacketRulesArr.${index}.weight`"
+                :rules="[
+                  { required: true, message: '请输入权重', trigger: 'blur' },
+                  { type: 'integer', message: '权重必须为整数', trigger: 'blur' },
+                ]"
+                class="form-item-weight"
+              >
+                <el-input
+                  v-model.number="rule.weight"
+                  type="number"
+                  :min="1"
+                  placeholder="权重"
+                  size="small"
+                ></el-input>
+              </el-form-item>
+              <el-tooltip class="item" effect="dark" content="权重越高,被随机到的概率越大" placement="top">
+                <i class="el-icon-question"></i>
+              </el-tooltip>
+              <div class="action-buttons">
+                <el-button
+                  icon="el-icon-plus"
+                  size="mini"
+                  type="text"
+                  @click="addRule(index)"
+                  class="add-btn"
+                >
+                  新增
+                </el-button>
+                <el-button
+                  icon="el-icon-delete"
+                  size="mini"
+                  type="text"
+                  @click="deleteRule(index)"
+                  :disabled="form.randomRedPacketRulesArr.length <= 1"
+                  class="delete-btn"
+                >
+                  删除
+                </el-button>
+              </div>
+          </div>
+        </template>
+           
         </el-form-item>
         <el-form-item label="是否关联商品">
           <el-radio v-model="form.isProduct" :label=0>否</el-radio>
@@ -355,10 +438,21 @@ import {getByIds} from '@/api/course/courseQuestionBank'
 import CourseWatchComment from "./courseWatchComment.vue";
 import {getCateListByPid, getCatePidList} from '@/api/course/userCourseCategory'
 import draggable from 'vuedraggable'
+import { getConfigByKey } from '@/api/system/config'
 
 export default {
   name: "userCourseCatalog",
   components: {VideoUpload, QuestionBank, CourseWatchComment, CourseProduct, draggable},
+  watch:{
+    // 深度监听 rules 数组的变化,以更新总权重
+    "form.randomRedPacketRulesArr": {
+      handler(val) {
+        // this.calculateTotalWeight();
+        this.validateRules();
+      },
+      deep: true,
+    },
+  },
   data() {
     return {
       duration: null,
@@ -454,7 +548,15 @@ export default {
       openVideoSort: false,
       // 表单参数
       form: {
-        courseProducts: []
+        courseProducts: [],
+        randomRedPacketRules:null,
+        randomRedPacketRulesArr:[
+           {
+            minAmount: 0.01,
+            maxAmount: 0.01,
+            weight: 100,
+          }
+        ]
       },
       updateBatchData: {
         open: false,
@@ -477,12 +579,25 @@ export default {
         videoId: null,
         title: ""
       },
+      enableRandomRedPacket:false
     }
   },
   created() {
     this.getDicts("sys_course_temp_type").then(response => {
       this.typeOptions = response.data;
     });
+        getConfigByKey('randomRedpacket:config').then(res=>{
+        let configData = res.data;
+        if(!!configData && !!configData.configValue){
+           let configValue = JSON.parse(configData.configValue);
+           if(!!configValue.enableRandomRedpacket){
+            this.enableRandomRedPacket = configValue.enableRandomRedpacket;
+            console.log("this.enableRandomRedPacket ::" + this.enableRandomRedPacket)
+           }
+        }
+    }).catch(res=>{
+
+    })
   },
   methods: {
     getPickerOptions() {
@@ -713,6 +828,14 @@ export default {
         isFirst: 0,
         listingStartTime: null,
         listingEndTime: null,
+        randomRedPacketRules:null,
+        randomRedPacketRulesArr:[
+          {
+            minAmount: 0.01,
+            maxAmount: 0.01,
+            weight: 100,
+          }
+        ]
       };
       this.videoURL = '';
       this.progress = 0;
@@ -768,7 +891,19 @@ export default {
       this.packageList = [];
       const videoId = row.videoId || this.ids
       getUserCourseVideo(videoId).then(response => {
+        console.log(response);
         this.form = response.data;
+        if(!!this.form.randomRedPacketRules){
+           this.$set(this.form, 'randomRedPacketRulesArr', JSON.parse(this.form.randomRedPacketRules)) ;
+          // this.form.randomRedPacketRulesArr = JSON.parse(this.form.randomRedPacketRules);
+        }else{
+          //处理初始值
+         this.form.randomRedPacketRulesArr = [{
+            minAmount: 0.01,
+            maxAmount: 0.01,
+            weight: 100,
+          }]
+        }
         if (response.data.videoUrl != null && response.data.videoUrl !== '') {
           this.videoUrl = response.data.videoUrl;
         }
@@ -813,7 +948,7 @@ export default {
             });
             return
           }
-          if (this.form.isProduct != null && this.form.isProduct == 1 && this.form.courseProducts.length < 1) {
+          if (this.form.isProduct != null && this.form.isProduct == 1 && (this.form.courseProducts == null || this.form.courseProducts.length < 1)) {
             this.$message({
               message: '请选择关联商品',
               type: 'warning'
@@ -829,6 +964,11 @@ export default {
           if (this.form.courseProducts != null) {
             this.form.productId = this.form.courseProducts.map(item => item.id).join(',');
           }
+          if(!!this.form.randomRedPacketRulesArr){
+            let rulesJson = JSON.stringify(this.form.randomRedPacketRulesArr);
+            this.form.randomRedPacketRules =  rulesJson;
+          }
+
           if (this.form.videoId != null) {
             updateUserCourseVideo(this.form).then(response => {
               this.msgSuccess("修改成功");
@@ -981,7 +1121,15 @@ export default {
       this.redData.loading = true;
       this.redData.queryParams.courseId = this.courseId;
       getVideoListByCourseId(this.redData.queryParams).then(response => {
-        this.redData.list = response.rows;
+        if(!!response.rows && response.rows.length >0){
+          for(let i = 0; i < response.rows.length; i++){
+            if(!!response.rows[i].randomRedPacketRules){
+             this.$set(response.rows[i], 'randomRedPacketRulesArr', JSON.parse(response.rows[i].randomRedPacketRules)) ;
+            }
+          }
+        }
+         this.redData.list = response.rows;
+         console.log(this.redData.list);
         this.redData.loading = false;
       });
     },
@@ -998,6 +1146,80 @@ export default {
       this.commentDialog.title = `查看评论 - ${row.title}`;
       this.commentDialog.open = true;
     },
+    // 实时过滤金额输入,只允许两位小数
+    handleAmountInput(rule, field) {
+      let value = rule[field];
+      if (value === null || value === undefined) return;
+      
+      // 转换为字符串处理
+      let str = value.toString();
+      
+      // 移除除数字和小数点外的所有字符
+      str = str.replace(/[^0-9.]/g, '');
+      
+      // 只保留一个小数点
+      const dotIndex = str.indexOf('.');
+      if (dotIndex !== -1) {
+        str = str.substring(0, dotIndex + 1) + str.substring(dotIndex + 1).replace(/\./g, '');
+      }
+      
+      // 限制小数点后最多两位
+      if (dotIndex !== -1 && str.length > dotIndex + 3) {
+        str = str.substring(0, dotIndex + 3);
+      }
+      
+      // 转换回数字并更新
+      rule[field] = parseFloat(str) || 0;
+    },
+      deleteRule(index) {
+      this.$confirm("确定要删除这个区间吗?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      }).then(() => {
+        this.form.randomRedPacketRulesArr.splice(index, 1);
+        this.$message({
+          type: "success",
+          message: "删除成功!",
+        });
+      });
+    },
+      addRule(index) {
+      // 在当前行的后面插入一个新行
+      this.form.randomRedPacketRulesArr.splice(index + 1, 0, {
+        minAmount: 0.01,
+        maxAmount: 0.01,
+        weight: 100,
+      });
+    },
+       // 自定义校验规则:确保最大金额大于最小金额
+    validateMinAmount(rule, value, callback) {
+      // debugger;
+      // const maxAmount = this.form29.rules[].maxAmount
+      
+      const index = rule.index;
+      const maxAmount = this.form.randomRedPacketRulesArr[index].maxAmount;
+
+      if (value > maxAmount) {
+        callback(new Error("最小金额不能大于最大金额"));
+      } else {
+        callback();
+      }
+    },
+      validateRules() {        
+      this.form.randomRedPacketRulesArr.forEach((rule) => {
+        if (rule.minAmount === undefined || rule.minAmount < 0.01) {
+          rule.minAmount = 0.01;
+        }
+        if (rule.maxAmount === undefined || rule.maxAmount < rule.minAmount) {
+          rule.maxAmount = rule.minAmount;
+        }
+        if (rule.weight === undefined || rule.weight < 1) {
+          rule.weight = 1;
+        }
+      });
+    },
+
   }
 }
 </script>

+ 192 - 4
src/views/course/userCourse/index.vue

@@ -276,6 +276,86 @@
         <el-form-item label="红包金额" prop="redPacketMoney">
           <el-input-number v-model="openRedPage.redPacketMoney" :min="0.1" :max="200" :step="0.1"></el-input-number>
         </el-form-item>
+        <div v-if="!!enableRandomRedPacket" style=" display: flex;
+                  flex-direction: column;">
+             <div v-for="(rule, index) in openRedPage.rules" :key="index" class="form-row">
+           <el-form-item
+            label="随机红包金额区间"
+            :prop="`rules.${index}.minAmount`"
+            :rules="[
+              { required: true, message: '请输入最小金额', trigger: 'blur' },
+              { validator: validateMinAmount, trigger: 'blur', index: index }
+            ]"
+            class="form-item-amount"
+          >
+            <el-input
+              v-model.number="rule.minAmount"
+              type="number"
+              :min="0.01"
+              :precision="2"
+              :step="0.01"
+              placeholder="最小金额"
+              size="small"
+              class="amount-input"
+              @input="handleAmountInput(rule, 'minAmount')"
+            ></el-input>
+            <span class="separator">-</span>
+            <el-input
+              v-model.number="rule.maxAmount"
+              type="number"
+              :min="rule.minAmount || 0.01"
+              :precision="2"
+              :step="0.01"
+              placeholder="最大金额"
+              size="small"
+              class="amount-input"
+              @input="handleAmountInput(rule, 'maxAmount')"
+            ></el-input>
+            <span class="suffix">元</span>
+          </el-form-item>
+              <el-form-item
+                label="随机权重"
+                :prop="`rules.${index}.weight`"
+                :rules="[
+                  { required: true, message: '请输入权重', trigger: 'blur' },
+                  { type: 'integer', message: '权重必须为整数', trigger: 'blur' },
+                ]"
+                class="form-item-weight"
+              >
+                <el-input
+                  v-model.number="rule.weight"
+                  type="number"
+                  :min="1"
+                  placeholder="权重"
+                  size="small"
+                ></el-input>
+              </el-form-item>
+              <el-tooltip class="item" effect="dark" content="权重越高,被随机到的概率越大" placement="top">
+                <i class="el-icon-question"></i>
+              </el-tooltip>
+              <div class="action-buttons">
+                <el-button
+                  icon="el-icon-plus"
+                  size="mini"
+                  type="text"
+                  @click="addRule(index)"
+                  class="add-btn"
+                >
+                  新增
+                </el-button>
+                <el-button
+                  icon="el-icon-delete"
+                  size="mini"
+                  type="text"
+                  @click="deleteRule(index)"
+                  :disabled="openRedPage.rules.length <= 1"
+                  class="delete-btn"
+                >
+                  删除
+                </el-button>
+              </div>
+            </div>
+        </div>
       </el-form>
       <div slot="footer" class="dialog-footer">
         <el-button type="primary" @click="submitFormRedPage">确 定</el-button>
@@ -380,7 +460,7 @@ import userCourseCatalogDetails from '../../components/course/userCourseCatalogD
 import {getAllCourseCategoryList, getCatePidList, getCateListByPid} from "@/api/course/userCourseCategory";
 import {allList} from "@/api/company/company";
 import VideoUpload from '@/components/VideoUpload/index.vue'
-
+import { getConfigByKey } from '@/api/system/config'
 export default {
   name: "UserCourse",
   components: {
@@ -388,6 +468,16 @@ export default {
     Treeselect,
     Editor, ImageUpload, userCourseCatalogDetails
   },
+  watch:{
+  // 深度监听 rules 数组的变化,以更新总权重
+    "openRedPage.rules": {
+      handler(val) {
+        // this.calculateTotalWeight();
+        this.validateRules();
+      },
+      deep: true,
+    },
+  },
   data() {
     return {
       talentParam: {
@@ -438,6 +528,14 @@ export default {
         courseId:null,
         courseName:null,
         redPacketMoney:0.1,
+        //随机红包配置
+        rules:[
+           {
+            minAmount: 0.01,
+            maxAmount: 0.01,
+            weight: 100,
+          }
+        ]
       },
       // 查询参数
       queryParams: {
@@ -552,11 +650,26 @@ export default {
             { required: true, message: '支持单位不能为空', trigger: 'blur' }
           ],
         }
-      }
+      },
+      enableRandomRedPacket:false
     };
   },
   created() {
     this.getList();
+    getConfigByKey('randomRedpacket:config').then(res=>{
+        console.log("res::")
+        console.log(res);
+        let configData = res.data;
+        if(!!configData && !!configData.configValue){
+           let configValue = JSON.parse(configData.configValue);
+           console.log(configValue);
+           if(!!configValue.enableRandomRedpacket){
+            this.enableRandomRedPacket = configValue.enableRandomRedpacket;
+           }
+        }
+    }).catch(res=>{
+
+    })
     getCatePidList().then(response => {
       this.categoryOptions = response.data;
     });
@@ -809,13 +922,14 @@ export default {
 
       const courseId = this.openRedPage.courseId;
       const redPacketMoney = this.openRedPage.redPacketMoney;
+      let randomRedPacketRules = JSON.stringify( this.openRedPage.rules);
+      console.log(randomRedPacketRules)
       this.$confirm('是否确认将课程id 为"' + courseId + '"的红包批量修改为:【'+redPacketMoney+'】?', "警告", {
         confirmButtonText: "确定",
         cancelButtonText: "取消",
         type: "warning"
       }).then(function () {
-
-        return updateUserCourseRedPage({courseId:courseId,redPacketMoney:redPacketMoney});
+        return updateUserCourseRedPage({courseId:courseId,redPacketMoney:redPacketMoney,randomRedPacketRules:randomRedPacketRules});
       }).then(() => {
         this.getList();
         this.msgSuccess("修改成功");
@@ -985,6 +1099,80 @@ export default {
       this.resetForm('configForm')
       this.configDialog.dialogVisible = false;
     },
+    // 实时过滤金额输入,只允许两位小数
+    handleAmountInput(rule, field) {
+      let value = rule[field];
+      if (value === null || value === undefined) return;
+      
+      // 转换为字符串处理
+      let str = value.toString();
+      
+      // 移除除数字和小数点外的所有字符
+      str = str.replace(/[^0-9.]/g, '');
+      
+      // 只保留一个小数点
+      const dotIndex = str.indexOf('.');
+      if (dotIndex !== -1) {
+        str = str.substring(0, dotIndex + 1) + str.substring(dotIndex + 1).replace(/\./g, '');
+      }
+      
+      // 限制小数点后最多两位
+      if (dotIndex !== -1 && str.length > dotIndex + 3) {
+        str = str.substring(0, dotIndex + 3);
+      }
+      
+      // 转换回数字并更新
+      rule[field] = parseFloat(str) || 0;
+    },
+      deleteRule(index) {
+      this.$confirm("确定要删除这个区间吗?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      }).then(() => {
+        this.openRedPage.rules.splice(index, 1);
+        this.$message({
+          type: "success",
+          message: "删除成功!",
+        });
+      });
+    },
+      addRule(index) {
+      // 在当前行的后面插入一个新行
+      this.openRedPage.rules.splice(index + 1, 0, {
+        minAmount: 0.01,
+        maxAmount: 0.01,
+        weight: 100,
+      });
+    },
+       // 自定义校验规则:确保最大金额大于最小金额
+    validateMinAmount(rule, value, callback) {
+      // debugger;
+      // const maxAmount = this.form29.rules[].maxAmount
+      
+      const index = rule.index;
+      const maxAmount = this.openRedPage.rules[index].maxAmount;
+
+      if (value > maxAmount) {
+        callback(new Error("最小金额不能大于最大金额"));
+      } else {
+        callback();
+      }
+    },
+      validateRules() {
+      this.openRedPage.rules.forEach((rule) => {
+        if (rule.minAmount === undefined || rule.minAmount < 0.01) {
+          rule.minAmount = 0.01;
+        }
+        if (rule.maxAmount === undefined || rule.maxAmount < rule.minAmount) {
+          rule.maxAmount = rule.minAmount;
+        }
+        if (rule.weight === undefined || rule.weight < 1) {
+          rule.weight = 1;
+        }
+      });
+    },
+
   }
 };
 </script>

+ 8 - 1
src/views/his/company/index.vue

@@ -145,6 +145,7 @@
       <el-table-column label="到期时间" align="center" prop="limitTime" width="180"/>
       <el-table-column label="创建时间" align="center" prop="createTime" width="180"/>
       <el-table-column label="更新时间" align="center" prop="updateTime" width="180"/>
+      <el-table-column v-if="'济南联志健康'==signProjectName"  align="center" width="180" label="分公司配置商户号" prop="mchId" />
       <!--      <el-table-column label="主机重启时间" align="center" prop="restartTime" width="180" />-->
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="220px">
         <template slot-scope="scope">
@@ -732,11 +733,12 @@ import { cateList } from '@/api/his/packageCate'
 import { getConfigByKey } from '@/api/system/config'
 import { listDept } from '@/api/system/dept'
 import { listAll } from '@/api/course/coursePlaySourceConfig'
-
+import{ getSignProjectName } from '@/api/course/qw/courseWatchLog'
 export default {
   name: 'Company',
   data() {
     return {
+      signProjectName:"",
       redSubmit: false,
       //分账参数
       revenue:{
@@ -916,6 +918,11 @@ export default {
     }
   },
   created() {
+    getSignProjectName()
+    .then(res=>{
+      this.signProjectName = res.signProjectName;
+    })
+    .catch(res=>{console.log("getSignProjectName Err")})
     this.getList()
     this.getDicts('sys_company_status').then(response => {
       this.statusOptions = response.data

+ 27 - 12
src/views/hisStore/storeOrder/healthStoreList.vue

@@ -76,6 +76,15 @@
           @keyup.enter.native="handleQuery"
         />
       </el-form-item>
+      <el-form-item label="银行交易流水号" prop="bankTransactionId">
+        <el-input
+          v-model="queryParams.bankTransactionId"
+          placeholder="请输入银行交易流水号"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
 
       <el-form-item label="手机号" prop="userPhone">
         <el-input
@@ -426,6 +435,7 @@
     @sort-change="handleSortChange" :default-sort="{prop: 'createTime', order: 'descending'}">
       <el-table-column align="center" type="selection" width="55"/>
       <el-table-column align="center" label="订单号" prop="orderCode" width="200px"/>
+      <el-table-column label="银行交易流水号" align="center" prop="bankTransactionId" width="180" />
       <el-table-column label="ERP电话" align="center" prop="erpPhone" width="120px" v-if="SFDFopen"/>
       <el-table-column label="ERP账号" align="center" prop="erpAccount" width="120px" v-if="SFDFopen"/>
       <el-table-column label="小程序名称" align="center" prop="miniProgramName"/>
@@ -1056,26 +1066,30 @@ export default {
       },
       // 可选择的导出字段列表
       exportFieldOptions: [
+
+        { key: 'erpPhone', label: 'ERP电话', checked: false },
+        { key: 'erpAccount', label: 'ERP账号', checked: false },
         { key: 'orderCode', label: '订单号', checked: true },
-        { key: 'miniProgramName', label: '小程序名称', checked: true },
-        { key: 'patientName', label: '就诊人', checked: true },
-        { key: 'productName', label: '编号', checked: true },
-        { key: 'barCode', label: '药品信息', checked: false },
+        { key: 'userId', label: '会员ID', checked: true },
+        { key: 'orderType', label: '订单类型', checked: true },
+        { key: 'companyName', label: '公司名称', checked: true },
+        { key: 'companyUserNickName', label: '所属销售', checked: true },
+        { key: 'realName', label: '收货人姓名', checked: true },
+        { key: 'userPhone', label: '收货人电话', checked: true },
+        { key: 'userAddress', label: '详细地址', checked: true },
         { key: 'payMoney', label: '实收金额', checked: true },
         { key: 'payRemain', label: '物流代收金额', checked: false },
-        { key: 'payType', label: '支付方式', checked: true },
         { key: 'createTime', label: '下单时间', checked: true },
         { key: 'payTime', label: '支付时间', checked: true },
+        { key: 'payType', label: '支付方式', checked: true },
         { key: 'status', label: '订单状态', checked: true },
-        { key: 'userName', label: '收货人姓名', checked: true },
-        { key: 'userPhone', label: '收货人电话', checked: true },
-        { key: 'userAddress', label: '详细地址', checked: true },
-        { key: 'deliveryCode', label: '快递公司编号', checked: false },
+        { key: 'barCode', label: '商品编码', checked: false },
+
+
+        { key: 'deliverySn', label: '快递公司编号', checked: false },
         { key: 'deliveryName', label: '快递公司', checked: false },
-        { key: 'deliverySn', label: '快递单号', checked: false },
+        { key: 'deliveryId', label: '快递单号', checked: false },
         { key: 'remark', label: '备注', checked: false },
-        { key: 'erpPhone', label: 'ERP电话', checked: false },
-        { key: 'erpAccount', label: 'ERP账号', checked: false },
 
       ],
       appMallOptions:[],
@@ -1149,6 +1163,7 @@ export default {
         pageSize: 10,
         orderCode: null,
         orderCodes:[],
+        bankTransactionId: null,
         extendOrderId: null,
         userId: null,
         realName: null,

+ 25 - 12
src/views/hisStore/storeOrder/index.vue

@@ -91,6 +91,15 @@
           @keyup.enter.native="handleQuery"
         />
       </el-form-item>
+      <el-form-item label="银行交易流水号" prop="bankTransactionId">
+        <el-input
+          v-model="queryParams.bankTransactionId"
+          placeholder="请输入银行交易流水号"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
 
       <el-form-item label="产品名称" prop="productName">
         <el-input
@@ -443,6 +452,7 @@
     @sort-change="handleSortChange" :default-sort="{prop: 'createTime', order: 'descending'}">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="订单号" align="center" prop="orderCode" width="200px" />
+      <el-table-column label="银行交易流水号" align="center" prop="bankTransactionId" width="180" />
       <el-table-column label="ERP电话" align="center" prop="erpPhone" width="120px" v-if="SFDFopen "/>
       <el-table-column label="ERP账号" align="center" prop="erpAccount" width="120px" v-if="SFDFopen"/>
       <el-table-column label="所属公司" align="center" prop="companyName" />
@@ -1103,28 +1113,30 @@ export default {
       },
       // 可选择的导出字段列表
       exportFieldOptions: [
+
+        { key: 'erpPhone', label: 'ERP电话', checked: false },
+        { key: 'erpAccount', label: 'ERP账号', checked: false },
         { key: 'orderCode', label: '订单号', checked: true },
-        { key: 'miniProgramName', label: '小程序名称', checked: true },
+        { key: 'userId', label: '会员ID', checked: true },
+        { key: 'orderType', label: '订单类型', checked: true },
         { key: 'companyName', label: '公司名称', checked: true },
         { key: 'companyUserNickName', label: '所属销售', checked: true },
-        { key: 'patientName', label: '就诊人', checked: true },
-        { key: 'productName', label: '编号', checked: true },
-        { key: 'barCode', label: '药品信息', checked: false },
+        { key: 'realName', label: '收货人姓名', checked: true },
+        { key: 'userPhone', label: '收货人电话', checked: true },
+        { key: 'userAddress', label: '详细地址', checked: true },
         { key: 'payMoney', label: '实收金额', checked: true },
         { key: 'payRemain', label: '物流代收金额', checked: false },
-        { key: 'payType', label: '支付方式', checked: true },
         { key: 'createTime', label: '下单时间', checked: true },
         { key: 'payTime', label: '支付时间', checked: true },
+        { key: 'payType', label: '支付方式', checked: true },
         { key: 'status', label: '订单状态', checked: true },
-        { key: 'userName', label: '收货人姓名', checked: true },
-        { key: 'userPhone', label: '收货人电话', checked: true },
-        { key: 'userAddress', label: '详细地址', checked: true },
-        { key: 'deliveryCode', label: '快递公司编号', checked: false },
+        { key: 'barCode', label: '商品编码', checked: false },
+
+
+        { key: 'deliverySn', label: '快递公司编号', checked: false },
         { key: 'deliveryName', label: '快递公司', checked: false },
-        { key: 'deliverySn', label: '快递单号', checked: false },
+        { key: 'deliveryId', label: '快递单号', checked: false },
         { key: 'remark', label: '备注', checked: false },
-        { key: 'erpPhone', label: 'ERP电话', checked: false },
-        { key: 'erpAccount', label: 'ERP账号', checked: false },
 
       ],
 
@@ -1202,6 +1214,7 @@ export default {
         pageSize: 10,
         orderCode: null,
         orderCodes:[],
+        bankTransactionId: null,
         extendOrderId: null,
         userId: null,
         realName: null,

+ 1 - 2
src/views/live/components/productAfterSalesOrder.vue

@@ -20,7 +20,6 @@
             <el-button size="mini"  v-hasPermi="['store:storeAfterSales:audit1']" v-show="afterSales.salesStatus==0&&afterSales.status===0" @click="handleAudit1">平台审核</el-button>
             <el-button size="mini"  v-hasPermi="['store:storeAfterSales:audit2']" v-show="afterSales.salesStatus==0&&afterSales.status===2" @click="handleAudit2">仓库审核</el-button>
             <el-button size="mini"  v-hasPermi="['store:storeAfterSales:refund']" @click="handleRefund"  v-show="afterSales.salesStatus==0&&afterSales.status===3">财务审核</el-button>
-            <el-button size="mini"  v-hasPermi="['store:storeAfterSales:refund']" @click="handleImmediatelyRefund"  v-show="afterSales.salesStatus==0">立即退款</el-button>
             <el-button size="mini"  v-hasPermi="['store:storeAfterSales:cancel']" @click="cancel"  v-show="afterSales.salesStatus==0">撤销</el-button>
             <el-button size="mini"  @click="showOrder">查看订单</el-button>
           </div>
@@ -335,7 +334,7 @@ export default {
         cancelButtonText: "取消",
         type: "warning"
       }).then(function() {
-       
+
         let param ={orderId:_this.order.orderId}
         console.log(param)
          debugger;

+ 1 - 0
src/views/live/live/index.vue

@@ -198,6 +198,7 @@
             size="mini"
             type="text"
             icon="el-icon-edit"
+            v-if="scope.row.status != 2"
             @click="handleUpdate(scope.row)"
             v-hasPermi="['live:live:edit']"
           >修改</el-button>

+ 1 - 1
src/views/live/liveAfteraSales/index.vue

@@ -183,7 +183,7 @@
             size="mini"
             type="text"
             @click="handleShow(scope.row)"
-            v-hasPermi="['store:storeAfterSales:query']"
+            v-hasPermi="['live:liveAfteraSales:query']"
           >查看</el-button>
 
         </template>

+ 323 - 4
src/views/live/liveData/index.vue

@@ -309,11 +309,256 @@
         <el-button type="primary" @click="confirmAutoRemark">确 定</el-button>
       </div>
     </el-dialog>
+
+    <!-- 详情侧边栏 -->
+    <el-drawer
+      title="直播间详情"
+      :visible.sync="detailDrawerVisible"
+      direction="rtl"
+      size="60%"
+      :before-close="closeDetailDrawer"
+    >
+      <div v-if="!showUserDetail" class="detail-content">
+        <el-card shadow="never" style="margin-bottom: 20px;">
+          <div slot="header">
+            <span>总体数据</span>
+            <el-button style="float: right; padding: 3px 0" type="text" @click="handleViewUserDetail">查看用户详情</el-button>
+          </div>
+          <el-row :gutter="20">
+            <el-col :span="12">
+              <div class="detail-item">
+                <span class="detail-label">视频时长:</span>
+                <span class="detail-value">{{ formatDuration(detailData.videoDuration || 0) }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="detail-item">
+                <span class="detail-label">累计观看人数:</span>
+                <span class="detail-value">{{ detailData.totalViewers || 0 }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="detail-item">
+                <span class="detail-label">累计完课人数:</span>
+                <span class="detail-value">{{ detailData.totalCompletedCourses || 0 }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="detail-item">
+                <span class="detail-label">到课完课率:</span>
+                <span class="detail-value">{{ formatPercent(detailData.totalCompletionRate) }}</span>
+              </div>
+            </el-col>
+          </el-row>
+        </el-card>
+
+        <el-card shadow="never" style="margin-bottom: 20px;">
+          <div slot="header">直播数据</div>
+          <el-row :gutter="20">
+            <el-col :span="12">
+              <div class="detail-item">
+                <span class="detail-label">直播观看人数:</span>
+                <span class="detail-value">{{ detailData.liveViewers || 0 }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="detail-item">
+                <span class="detail-label">>20分钟人数(直播):</span>
+                <span class="detail-value">{{ detailData.liveOver20Minutes || 0 }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="detail-item">
+                <span class="detail-label">>30分钟人数(直播):</span>
+                <span class="detail-value">{{ detailData.liveOver30Minutes || 0 }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="detail-item">
+                <span class="detail-label">到课完课率直播(>20分钟):</span>
+                <span class="detail-value">{{ formatPercent(detailData.liveCompletionRate20) }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="detail-item">
+                <span class="detail-label">到课完课率直播(>30分钟):</span>
+                <span class="detail-value">{{ formatPercent(detailData.liveCompletionRate30) }}</span>
+              </div>
+            </el-col>
+          </el-row>
+        </el-card>
+
+        <el-card shadow="never" style="margin-bottom: 20px;">
+          <div slot="header">回放数据</div>
+          <el-row :gutter="20">
+            <el-col :span="12">
+              <div class="detail-item">
+                <span class="detail-label">回放观看人数:</span>
+                <span class="detail-value">{{ detailData.playbackViewers || 0 }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="detail-item">
+                <span class="detail-label">>20分钟人数(回放):</span>
+                <span class="detail-value">{{ detailData.playbackOver20Minutes || 0 }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="detail-item">
+                <span class="detail-label">>30分钟人数(回放):</span>
+                <span class="detail-value">{{ detailData.playbackOver30Minutes || 0 }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="detail-item">
+                <span class="detail-label">到课完课率回放(>20分钟):</span>
+                <span class="detail-value">{{ formatPercent(detailData.playbackCompletionRate20) }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="detail-item">
+                <span class="detail-label">到课完课率回放(>30分钟):</span>
+                <span class="detail-value">{{ formatPercent(detailData.playbackCompletionRate30) }}</span>
+              </div>
+            </el-col>
+          </el-row>
+        </el-card>
+
+        <el-card shadow="never" style="margin-bottom: 20px;">
+          <div slot="header">时长统计</div>
+          <el-row :gutter="20">
+            <el-col :span="12">
+              <div class="detail-item">
+                <span class="detail-label">直播峰值:</span>
+                <span class="detail-value">{{ detailData.livePeak || 0 }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="detail-item">
+                <span class="detail-label">直播平均时长:</span>
+                <span class="detail-value">{{ formatDuration(detailData.liveAvgDuration || 0) }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="detail-item">
+                <span class="detail-label">回放平均时长:</span>
+                <span class="detail-value">{{ formatDuration(detailData.playbackAvgDuration || 0) }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="detail-item">
+                <span class="detail-label">回放完播率:</span>
+                <span class="detail-value">{{ formatPercent(detailData.playbackFinishRate) }}</span>
+              </div>
+            </el-col>
+          </el-row>
+        </el-card>
+
+        <el-card shadow="never" style="margin-bottom: 20px;">
+          <div slot="header">订单数据</div>
+          <el-row :gutter="20">
+            <el-col :span="12">
+              <div class="detail-item">
+                <span class="detail-label">GMV:</span>
+                <span class="detail-value">{{ formatMoney(detailData.gmv) }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="detail-item">
+                <span class="detail-label">付费人数:</span>
+                <span class="detail-value">{{ detailData.paidUsers || 0 }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="detail-item">
+                <span class="detail-label">付费单数:</span>
+                <span class="detail-value">{{ detailData.paidOrders || 0 }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="detail-item">
+                <span class="detail-label">峰值转化率:</span>
+                <span class="detail-value">{{ formatPercent(detailData.peakConversionRate) }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="detail-item">
+                <span class="detail-label">总到课转化率:</span>
+                <span class="detail-value">{{ formatPercent(detailData.totalViewerConversionRate) }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="detail-item">
+                <span class="detail-label">30min完课转化率:</span>
+                <span class="detail-value">{{ formatPercent(detailData.completion30MinConversionRate) }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="detail-item">
+                <span class="detail-label">峰值R值:</span>
+                <span class="detail-value">{{ formatMoney(detailData.peakRValue) }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="detail-item">
+                <span class="detail-label">完课R值:</span>
+                <span class="detail-value">{{ formatMoney(detailData.completionRValue) }}</span>
+              </div>
+            </el-col>
+          </el-row>
+        </el-card>
+
+        <el-card shadow="never" v-if="detailData.productSalesList && detailData.productSalesList.length > 0">
+          <div slot="header">单品销量统计</div>
+          <el-table :data="detailData.productSalesList" border>
+            <el-table-column prop="productName" label="商品名称" min-width="200"></el-table-column>
+            <el-table-column prop="salesCount" label="销量" width="100" align="center"></el-table-column>
+            <el-table-column prop="salesAmount" label="销售额" width="150" align="center">
+              <template slot-scope="scope">
+                {{ formatMoney(scope.row.salesAmount) }}
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-card>
+      </div>
+
+      <!-- 用户详情列表 -->
+      <div v-else class="user-detail-content">
+        <el-button type="text" icon="el-icon-arrow-left" @click="showUserDetail = false" style="margin-bottom: 20px;">返回</el-button>
+        <el-table
+          :data="userDetailList"
+          v-loading="userDetailLoading"
+          border
+          style="width: 100%"
+        >
+          <el-table-column type="index" label="序号" width="60" align="center"></el-table-column>
+          <el-table-column prop="userName" label="用户名称" min-width="120"></el-table-column>
+          <el-table-column prop="liveWatchDuration" label="直播观看时长" width="140" align="center">
+            <template slot-scope="scope">
+              {{ formatDuration(scope.row.liveWatchDuration || 0) }}
+            </template>
+          </el-table-column>
+          <el-table-column prop="playbackWatchDuration" label="回放观看时长" width="140" align="center">
+            <template slot-scope="scope">
+              {{ formatDuration(scope.row.playbackWatchDuration || 0) }}
+            </template>
+          </el-table-column>
+          <el-table-column prop="orderCount" label="订单数" width="100" align="center"></el-table-column>
+          <el-table-column prop="orderAmount" label="订单金额" width="120" align="center">
+            <template slot-scope="scope">
+              {{ formatMoney(scope.row.orderAmount) }}
+            </template>
+          </el-table-column>
+          <el-table-column prop="companyName" label="分公司" min-width="150"></el-table-column>
+          <el-table-column prop="salesName" label="销售" min-width="120"></el-table-column>
+        </el-table>
+      </div>
+    </el-drawer>
   </div>
 </template>
 
 <script>
-import { listLiveData, exportLiveData, autoTagAndRemark, dashboardData } from "@/api/live/liveData";
+import { listLiveData, exportLiveData, autoTagAndRemark, dashboardData, getLiveDataDetailBySql, getLiveUserDetailListBySql } from "@/api/live/liveData";
 import { batchUpdateExternalContactNotes } from "@/api/qw/externalContact";
 import { addTag } from "@/api/qw/externalContact";
 
@@ -377,7 +622,14 @@ export default {
         rules: [],
         remarkFormat: 'time',
         position: 1
-      }
+      },
+      // 详情侧边栏
+      detailDrawerVisible: false,
+      currentLiveId: null,
+      detailData: {},
+      userDetailList: [],
+      userDetailLoading: false,
+      showUserDetail: false
     };
   },
   created() {
@@ -582,8 +834,9 @@ export default {
     },
     /** 查看详情 */
     handleViewDetail(row) {
-      // 可以跳转到详情页面或打开详情弹窗
-      this.$message.info('查看详情功能待实现');
+      this.currentLiveId = row.liveId;
+      this.detailDrawerVisible = true;
+      this.loadDetailData();
     },
     /** 格式化时长 */
     formatDuration(seconds) {
@@ -609,6 +862,48 @@ export default {
       // this.getStatistics();
       this.getList();
       this.$message.success('设置成功');
+    },
+    /** 加载详情数据 */
+    loadDetailData() {
+      if (!this.currentLiveId) return;
+      // 使用SQL方式获取详情数据
+      getLiveDataDetailBySql(this.currentLiveId).then(response => {
+        if (response.code === 200) {
+          this.detailData = response.data || {};
+        }
+      });
+    },
+    /** 查看用户详情 */
+    handleViewUserDetail() {
+      if (!this.currentLiveId) return;
+      this.showUserDetail = true;
+      this.userDetailLoading = true;
+      getLiveUserDetailListBySql(this.currentLiveId).then(response => {
+        if (response.code === 200) {
+          this.userDetailList = response.data || [];
+        }
+        this.userDetailLoading = false;
+      }).catch(() => {
+        this.userDetailLoading = false;
+      });
+    },
+    /** 关闭详情侧边栏 */
+    closeDetailDrawer() {
+      this.detailDrawerVisible = false;
+      this.showUserDetail = false;
+      this.detailData = {};
+      this.userDetailList = [];
+      this.currentLiveId = null;
+    },
+    /** 格式化百分比 */
+    formatPercent(value) {
+      if (value == null || value === undefined) return '0%';
+      return (typeof value === 'number' ? value : parseFloat(value)).toFixed(2) + '%';
+    },
+    /** 格式化金额 */
+    formatMoney(value) {
+      if (value == null || value === undefined) return '¥0.00';
+      return '¥' + (typeof value === 'number' ? value : parseFloat(value)).toFixed(2);
     }
   }
 };
@@ -656,4 +951,28 @@ export default {
   padding-left: 10px;
   padding-top: 30px;
 }
+
+.detail-content {
+  padding: 20px;
+}
+
+.detail-item {
+  margin-bottom: 15px;
+  line-height: 32px;
+}
+
+.detail-label {
+  color: #606266;
+  font-size: 14px;
+}
+
+.detail-value {
+  color: #303133;
+  font-size: 14px;
+  font-weight: bold;
+}
+
+.user-detail-content {
+  padding: 20px;
+}
 </style>

+ 11 - 0
src/views/live/liveOrder/index.vue

@@ -34,6 +34,15 @@
           @keyup.enter.native="handleQuery"
         />
       </el-form-item>
+      <el-form-item label="银行交易流水号" prop="bankTransactionId">
+        <el-input
+          v-model="queryParams.bankTransactionId"
+          placeholder="请输入银行交易流水号"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
 
       <el-form-item label="收货人电话" prop="userPhone">
         <el-input
@@ -187,6 +196,7 @@
     <el-table border v-loading="loading" :data="liveOrderList" @selection-change="handleSelectionChange">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="订单号" align="center" prop="orderCode" />
+      <el-table-column label="银行交易流水号" align="center" prop="bankTransactionId" width="180" />
       <el-table-column label="销售ID" align="center" prop="companyUserId" >
         <template slot-scope="scope">
           <span v-if="scope.row.companyUserId > 0" >{{ scope.row.companyUserId }}</span>
@@ -413,6 +423,7 @@ export default {
         orderStartTime: null,
         orderEndTime: null,
         status: null,
+        bankTransactionId: null,
       },
       ruleForm:{
         miniAppId: null,

+ 329 - 2
src/views/system/config/config.vue

@@ -2255,6 +2255,107 @@
         </el-form>
       </el-tab-pane>
 
+      <el-tab-pane label="拼手气红包配置" name="randomRedpacket:config">
+        <el-form ref="form29" :model="form29" label-width="150px">
+          <el-form-item label="是否开启拼手气红包" prop="enableRandomRedpacket">
+          
+            <el-switch
+              v-model="form29.enableRandomRedpacket"
+              active-color="#13ce66"
+              inactive-color="#ff4949">
+            </el-switch>
+          </el-form-item>
+        <div v-if="!!form29.enableRandomRedpacket" style=" display: flex;
+                  flex-direction: column;
+                  margin-bottom: 50px;">
+                   
+             <div v-for="(rule, index) in form29.rules" :key="index" class="form-row">
+              
+           <el-form-item
+            label="金额区间"
+            :prop="`rules.${index}.minAmount`"
+            :rules="[
+              { required: true, message: '请输入最小金额', trigger: 'blur' },
+              { validator: validateMinAmount, trigger: 'blur', index: index }
+            ]"
+            class="form-item-amount"
+          >
+            <el-input
+              v-model.number="rule.minAmount"
+              type="number"
+              :min="0.01"
+              :precision="2"
+              :step="0.01"
+              placeholder="最小金额"
+              size="small"
+              class="amount-input"
+              @input="handleAmountInput(rule, 'minAmount')"
+            ></el-input>
+            <span class="separator">-</span>
+            <el-input
+              v-model.number="rule.maxAmount"
+              type="number"
+              :min="rule.minAmount || 0.01"
+              :precision="2"
+              :step="0.01"
+              placeholder="最大金额"
+              size="small"
+              class="amount-input"
+              @input="handleAmountInput(rule, 'maxAmount')"
+            ></el-input>
+            <span class="suffix">元</span>
+          </el-form-item>
+
+              <el-form-item
+                label="随机权重"
+                :prop="`rules.${index}.weight`"
+                :rules="[
+                  { required: true, message: '请输入权重', trigger: 'blur' },
+                  { type: 'integer', message: '权重必须为整数', trigger: 'blur' },
+                ]"
+                class="form-item-weight"
+              >
+                <el-input
+                  v-model.number="rule.weight"
+                  type="number"
+                  :min="1"
+                  placeholder="权重"
+                  size="small"
+                ></el-input>
+              </el-form-item>
+              <el-tooltip class="item" effect="dark" content="权重越高,被随机到的概率越大" placement="top">
+                <i class="el-icon-question"></i>
+              </el-tooltip>
+              <div class="action-buttons">
+                <el-button
+                  icon="el-icon-plus"
+                  size="mini"
+                  type="text"
+                  @click="addRule(index)"
+                  class="add-btn"
+                >
+                  新增
+                </el-button>
+                <el-button
+                  icon="el-icon-delete"
+                  size="mini"
+                  type="text"
+                  @click="deleteRule(index)"
+                  :disabled="form29.rules.length <= 1"
+                  class="delete-btn"
+                >
+                  删除
+                </el-button>
+              </div>
+            </div>
+        </div>
+
+          <div class="footer" style="margin-top:20px">
+            <el-button type="primary" @click="submitForm29">提 交</el-button>
+          </div>
+        </el-form>
+      </el-tab-pane>
+
     </el-tabs>
 
 
@@ -2433,6 +2534,18 @@ export default {
       },
       form27: {},
       form28: {},
+      form29:{
+        //是否开启拼手气红包
+        enableRandomRedpacket:false,
+         rules: [
+          {
+            minAmount: 0.01,
+            maxAmount: 0.01,
+            weight: 100,
+          },
+        ],
+
+      },
       storeProductScrmColumns:[],
       storeScrmColumns: [],
       photoArr: [],
@@ -2519,7 +2632,15 @@ export default {
     },
     appImages: function(val) {
       this.form25.images = val.join(',')
-    }
+    },
+     // 深度监听 rules 数组的变化,以更新总权重
+    "form29.rules": {
+      handler(val) {
+        this.calculateTotalWeight();
+        this.validateRules();
+      },
+      deep: true,
+    },
   },
   methods: {
 
@@ -2671,8 +2792,13 @@ export default {
     },
     getConfigByKey(key) {
       getConfigByKey(key).then(response => {
+        if(!!response.data){
         this.configId = response.data.configId
         this.configKey = response.data.configKey
+        }else{
+            this.configId = null;
+            this.configKey = key;
+          }
         if (key == 'sys.oss.cloudStorage') {
           this.form1 = JSON.parse(response.data.configValue)
         }
@@ -2807,6 +2933,13 @@ export default {
           this.configKey = response.data.configKey
           this.form28 = {...this.form28, ...JSON.parse(response.data.configValue)}
         }
+        if (key == 'randomRedpacket:config') {
+          if(!!response.data){
+          this.configId = response.data.configId
+          this.configKey = response.data.configKey
+          this.form29 = {...this.form29, ...JSON.parse(response.data.configValue)}
+          }
+        }
       })
     },
     /** 提交按钮 */
@@ -3162,7 +3295,104 @@ export default {
     },
     removeDisabledTime(index) {
       this.form18.disabledTimeList.splice(index, 1)
-    }
+    },
+    submitForm29(){
+      console.log("开始校验")
+       this.$refs['form29'].validate(valid => {
+        if (valid) {
+          var param = { configId: this.configId, configKey: this.configKey, configValue: JSON.stringify(this.form29) }
+          console.log(param)
+          param.configName = "拼手气红包配置";
+          updateConfigByKey(param).then(response => {
+            if (response.code === 200) {
+              this.msgSuccess('修改成功')
+            }
+          })
+        }
+      })
+    },
+     calculateTotalWeight() {
+      this.totalWeight = this.form29.rules.reduce((sum, rule) => {
+        return sum + (rule.weight || 0);
+      }, 0);
+    },
+
+    validateRules() {
+      this.form29.rules.forEach((rule) => {
+        if (rule.minAmount === undefined || rule.minAmount < 0.01) {
+          rule.minAmount = 0.01;
+        }
+        if (rule.maxAmount === undefined || rule.maxAmount < rule.minAmount) {
+          rule.maxAmount = rule.minAmount;
+        }
+        if (rule.weight === undefined || rule.weight < 1) {
+          rule.weight = 1;
+        }
+      });
+    },
+
+    // 自定义校验规则:确保最大金额大于最小金额
+    validateMinAmount(rule, value, callback) {
+      // debugger;
+      // const maxAmount = this.form29.rules[].maxAmount
+      
+      const index = rule.index;
+      const maxAmount = this.form29.rules[index].maxAmount;
+
+      if (value > maxAmount) {
+        callback(new Error("最小金额不能大于最大金额"));
+      } else {
+        callback();
+      }
+    },
+
+    addRule(index) {
+      // 在当前行的后面插入一个新行
+      this.form29.rules.splice(index + 1, 0, {
+        minAmount: 0.01,
+        maxAmount: 0.01,
+        weight: 100,
+      });
+    },
+
+    deleteRule(index) {
+      this.$confirm("确定要删除这个区间吗?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      }).then(() => {
+        this.form29.rules.splice(index, 1);
+        this.$message({
+          type: "success",
+          message: "删除成功!",
+        });
+      });
+    },
+     // 实时过滤金额输入,只允许两位小数
+    handleAmountInput(rule, field) {
+      let value = rule[field];
+      if (value === null || value === undefined) return;
+      
+      // 转换为字符串处理
+      let str = value.toString();
+      
+      // 移除除数字和小数点外的所有字符
+      str = str.replace(/[^0-9.]/g, '');
+      
+      // 只保留一个小数点
+      const dotIndex = str.indexOf('.');
+      if (dotIndex !== -1) {
+        str = str.substring(0, dotIndex + 1) + str.substring(dotIndex + 1).replace(/\./g, '');
+      }
+      
+      // 限制小数点后最多两位
+      if (dotIndex !== -1 && str.length > dotIndex + 3) {
+        str = str.substring(0, dotIndex + 3);
+      }
+      
+      // 转换回数字并更新
+      rule[field] = parseFloat(str) || 0;
+    },
   }
 }
 </script>
@@ -3174,4 +3404,101 @@ export default {
   align-items: flex-end;
   justify-content: flex-end;
 }
+
+
+
+
+.red-packet-config {
+  padding: 20px;
+}
+
+.card-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+
+.red-packet-form {
+  margin-top: 20px;
+}
+
+/* 每一行的样式 */
+.form-row {
+  display: flex;
+  /* align-items: center;
+  padding: 10px;
+  border-bottom: 1px solid #ebeef5; */
+   display: flex;
+  /* 关键改动:添加以下两行 */
+  align-items: center;    /* 垂直居中对齐 */
+  justify-content: flex-start; /* 水平方向从左到右排列(默认值,可显式写出) */
+  
+  padding: 10px;
+  border-bottom: 1px solid #ebeef5;
+}
+
+/* 最后一行去掉下边框 */
+.form-row:last-child {
+  border-bottom: none;
+}
+
+.form-item-amount {
+  flex: 1;
+  margin-right: 20px;
+}
+
+.form-item-weight {
+  width: 220px;
+  margin-right: 20px;
+}
+
+.action-buttons {
+  display: flex;
+  align-items: center;
+}
+
+.amount-input {
+  width: 100px !important;
+  text-align: center;
+}
+
+/* 金额输入框之间的连接符 */
+.separator {
+  margin: 0 10px;
+  color: #999;
+}
+
+.suffix {
+  margin-left: 10px;
+  color: #999;
+}
+
+.add-btn {
+  /* color: #48bb78 !important; */
+}
+
+.delete-btn {
+  color: #f56c6c !important;
+}
+
+.rules-info {
+  margin-top: 20px;
+  padding: 15px;
+  background-color: #f9f9f9;
+  border-radius: 4px;
+  font-size: 14px;
+  color: #666;
+}
+
+.info-number {
+  color: #1890ff;
+  font-weight: bold;
+  margin: 0 4px;
+}
+
+.action-bar {
+  margin-top: 20px;
+  display: flex;
+  justify-content: flex-end;
+}
 </style>