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

Merge remote-tracking branch 'origin/master'

xyx преди 5 дни
родител
ревизия
abbb0be4b9

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

@@ -76,3 +76,12 @@ export function setPayNotify(data) {
     data: data
   })
 }
+
+//同步订单状态
+export function oneClickShipping(data) {
+  return request({
+    url: '/store/store/storePayment/oneClickShipping',
+    method: 'post',
+    data: data
+  })
+}

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

@@ -20,3 +20,39 @@ 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'
+  })
+}
+
+// 导出直播间用户详情数据
+export function exportLiveUserDetail(liveId) {
+  return request({
+    url: '/liveData/liveData/exportLiveUserDetail?liveId=' + liveId,
+    method: 'get'
+  })
+}

+ 6 - 1
src/views/his/adv/index.vue

@@ -109,6 +109,7 @@
         </template>
       </el-table-column>
       <el-table-column label="地址" show-overflow-tooltip align="center" prop="advUrl" width="150px" />
+      <el-table-column label="跳转小程序原始id" show-overflow-tooltip align="center" prop="originalId" width="150px" />
 
       <el-table-column label="排序" align="center" prop="sort" />
       <el-table-column label="类型" align="center" prop="advType" width="130px">
@@ -199,6 +200,9 @@
        <el-form-item label="APP地址" prop="advUrl" v-if="form.showType==2||form.showType==4 ">
          <el-input v-model="form.appAdvUrl" placeholder="请输入APP地址" />
        </el-form-item>
+        <el-form-item label="跳转小程序原始id" prop="advUrl" v-if="form.showType==2||form.showType==4 ">
+          <el-input v-model="form.originalId" placeholder="请输入跳转小程序原始id" />
+        </el-form-item>
        <el-form-item label="文章内容" v-show="form.showType==3">
          <editor ref="myeditor" @on-text-change="updateText" />
        </el-form-item>
@@ -368,7 +372,8 @@ export default {
         sort: null,
         advType: null,
         showType: null,
-        activeId: null
+        activeId: null,
+        originalId: null
       };
       this.resetForm("form");
     },

+ 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

+ 285 - 9
src/views/his/integralOrder/index.vue

@@ -1,14 +1,60 @@
 <template>
   <div class="app-container">
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
-      <el-form-item label="订单编号" prop="orderCode">
-        <el-input
-          v-model="queryParams.orderCode"
-          placeholder="请输入订单编号"
-          clearable
-          size="small"
-          @keyup.enter.native="handleQuery"
-        />
+      <el-form-item label="订单编号" prop="orderCodes">
+        <div class="tag-input-container">
+          <!-- 标签显示区域 -->
+          <div class="tags-wrapper" @click="focusInput">
+            <!-- 已添加的订单号标签 -->
+            <el-tag
+              v-for="(code, index) in queryParams.orderCodes"
+              :key="index"
+              closable
+              size="small"
+              @close="removeOrderCode(index)"
+              class="order-tag"
+              :class="{ 'tag-error': false }"
+            >
+              {{ code }}
+            </el-tag>
+
+            <!-- 输入框 -->
+            <el-input
+              ref="tagInput"
+              v-model="currentInput"
+              v-show="inputVisible || queryParams.orderCodes.length === 0"
+              :placeholder="queryParams.orderCodes.length === 0 ? '请输入订单号,按回车或逗号分隔' : '继续输入...'"
+              size="small"
+              class="tag-input"
+              @keydown.native="handleKeyDown"
+              @keyup.native="handleKeyUp"
+              @blur="handleInputConfirm"
+              @focus="inputVisible = true"
+              clearable
+            />
+
+            <!-- 添加按钮(当没有输入时显示) -->
+            <el-button
+              v-if="!inputVisible && queryParams.orderCodes.length > 0"
+              class="button-new-tag"
+              size="small"
+              @click="showInput"
+              icon="el-icon-plus"
+              type="text"
+            >
+              添加订单号
+            </el-button>
+          </div>
+
+          <!-- 输入提示 -->
+          <div class="input-tips">
+            <span class="tip-text">
+              支持:回车、逗号、空格分隔 |
+              已添加 {{ queryParams.orderCodes.length }} 个订单号
+              <span v-if="maxOrderCodes > 0"> (最多{{ maxOrderCodes }}个)</span>
+            </span>
+          </div>
+        </div>
       </el-form-item>
       <el-form-item label="用户名称" prop="userName">
         <el-input
@@ -174,6 +220,20 @@
       <el-table-column label="ERP账号" align="center" prop="loginAccount" />
       <el-table-column label="ERP电话" align="center" prop="erpPhone" />
       <el-table-column label="订单编号" align="center" prop="orderCode" />
+      <el-table-column label="商品名称" align="center" width="200">
+        <template slot-scope="scope">
+          <div style="display: flex; align-items: center; justify-content: center;">
+            <span>{{ scope.row.goodsName }}</span>
+            <el-image
+              v-if="scope.row.goodsImage"
+              :src="scope.row.goodsImage"
+              style="width: 40px; height: 40px; margin-left: 8px;"
+              fit="cover"
+              :preview-src-list="[scope.row.goodsImage]"
+            />
+          </div>
+        </template>
+      </el-table-column>
       <el-table-column label="用户名称" align="center" prop="userName" />
       <el-table-column label="用户电话" align="center" prop="userPhone" />
       <el-table-column label="用户地址" align="center" prop="userAddress" show-overflow-tooltip />
@@ -528,6 +588,7 @@ export default {
         pageNum: 1,
         pageSize: 10,
         orderCode: null,
+        orderCodes: [], // 添加订单号数组
         userName: null,
         userPhone: null,
         integral: null,
@@ -541,6 +602,15 @@ export default {
         companyId:null,
         loginAccount: null  // 添加ERP账号筛选字段
       },
+      // 最大订单号数量限制
+      maxOrderCodes: {
+        type: Number,
+        default: 50
+      },
+      // 输入框是否可见
+      inputVisible: false,
+      // 当前输入值
+      currentInput: '',
        createTime:null,
       qwCompanyList:[],
       companyUserNameList:[],
@@ -691,10 +761,57 @@ export default {
     /** 查询积分商品订单列表 */
     getList() {
       this.loading = true;
+      
+      // 直接传递订单编号数组给后端
       listIntegralOrder(this.queryParams).then(response => {
-        this.integralOrderList = response.rows;
+        // 解析itemJson字段,提取goodsName和图片
+        const processedData = response.rows.map(item => {
+          let goodsName = '';
+          let goodsImage = '';
+          try {
+            if (item.itemJson) {
+              const itemData = JSON.parse(item.itemJson);
+              // 如果itemJson是数组格式,取第一个商品的名称和图片
+              if (Array.isArray(itemData) && itemData.length > 0) {
+                goodsName = itemData[0].goodsName || '';
+                // 提取图片,优先使用imgUrl,如果没有则使用images的第一张
+                if (itemData[0].imgUrl) {
+                  goodsImage = itemData[0].imgUrl;
+                } else if (itemData[0].images && itemData[0].images.split(',').length > 0) {
+                  goodsImage = itemData[0].images.split(',')[0];
+                }
+              }
+              // 如果itemJson是对象格式,直接取goodsName和图片
+              else if (itemData.goodsName) {
+                goodsName = itemData.goodsName;
+                // 提取图片,优先使用imgUrl,如果没有则使用images的第一张
+                if (itemData.imgUrl) {
+                  goodsImage = itemData.imgUrl;
+                } else if (itemData.images && itemData.images.split(',').length > 0) {
+                  goodsImage = itemData.images.split(',')[0];
+                }
+              }
+            }
+          } catch (error) {
+            console.error('解析itemJson失败:', error);
+            goodsName = '';
+            goodsImage = '';
+          }
+          
+          return {
+            ...item,
+            goodsName: goodsName,
+            goodsImage: goodsImage
+          };
+        });
+        
+        this.integralOrderList = processedData;
         this.total = response.total;
         this.loading = false;
+      }).catch(error => {
+        console.error('查询订单列表失败:', error);
+        this.loading = false;
+        this.$message.error('查询订单列表失败');
       });
     },
     // 取消按钮
@@ -739,6 +856,12 @@ export default {
       this.queryParams.qwUserId=null;
       this.queryParams.companyId=null;
       this.queryParams.companyUserId=null;
+      
+      // 清除订单号标签
+      this.queryParams.orderCodes = [];
+      this.currentInput = '';
+      this.inputVisible = false;
+      
       this.handleQuery();
 
     },
@@ -1093,6 +1216,159 @@ export default {
     submitOrderStatusFileForm(){
       this.$refs.uploadStatus.submit();
     },
+    // 处理键盘按下事件
+    handleKeyDown(event) {
+      const { key, target } = event
+      
+      // 处理退格键删除标签
+      if (key === 'Backspace' && !target.value && this.queryParams.orderCodes.length > 0) {
+        event.preventDefault()
+        this.removeOrderCode(this.queryParams.orderCodes.length - 1)
+      }
+      
+      // 处理分隔符
+      if ([',', ',', ' ', 'Enter'].includes(key)) {
+        event.preventDefault()
+        this.handleInputConfirm()
+      }
+    },
+
+    // 处理键盘抬起事件(实时分割输入)
+    handleKeyUp(event) {
+      const value = event.target.value
+      
+      // 检查是否包含分隔符
+      if (/[,,\s]/.test(value)) {
+        this.handleInputConfirm()
+      }
+    },
+
+    // 确认输入
+    handleInputConfirm() {
+      const inputValue = this.currentInput.trim()
+      
+      if (inputValue) {
+        // 分割多个订单号
+        const codes = inputValue.split(/[,,\s]+/).filter(code => code.trim())
+        
+        codes.forEach(code => {
+          this.addOrderCode(code.trim())
+        })
+      }
+      
+      this.currentInput = ''
+    },
+
+    // 添加订单号
+    addOrderCode(code) {
+      if (!code) return
+      
+      // 检查数量限制
+      if (this.maxOrderCodes > 0 && this.queryParams.orderCodes.length >= this.maxOrderCodes) {
+        this.$message.warning(`最多只能添加 ${this.maxOrderCodes} 个订单号`)
+        return
+      }
+      
+      // 检查重复
+      if (this.queryParams.orderCodes.includes(code)) {
+        this.$message.warning(`订单号 "${code}" 已存在`)
+        return
+      }
+      
+      // 添加到列表
+      this.queryParams.orderCodes.push(code)
+    },
+
+    // 删除订单号
+    removeOrderCode(index) {
+      this.queryParams.orderCodes.splice(index, 1)
+    },
+
+    // 显示输入框
+    showInput() {
+      this.inputVisible = true
+      this.$nextTick(() => {
+        this.$refs.tagInput.focus()
+      })
+    },
+    // 聚焦输入框
+    focusInput() {
+      if (!this.inputVisible) {
+        this.showInput()
+      }
+    },
   }
 };
 </script>
+
+<style scoped>
+.tag-input-container {
+  min-width: 445px;
+}
+
+.tags-wrapper {
+  min-height: 32px;
+  padding: 4px 8px;
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+  cursor: text;
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: 4px;
+  transition: border-color 0.2s;
+}
+
+.tags-wrapper:hover {
+  border-color: #c0c4cc;
+}
+
+.tags-wrapper:focus-within {
+  border-color: #409eff;
+  box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
+}
+
+.order-tag {
+  margin: 2px;
+  flex-shrink: 0;
+}
+
+.tag-error {
+  background-color: #fef0f0;
+  border-color: #fbc4c4;
+  color: #f56c6c;
+}
+
+.tag-input {
+  border: none;
+  outline: none;
+  flex: 1;
+  min-width: 120px;
+}
+
+.tag-input >>> .el-input__inner {
+  border: none;
+  padding: 0;
+  height: 24px;
+  line-height: 24px;
+}
+
+.button-new-tag {
+  height: 24px;
+  line-height: 22px;
+  padding: 0 8px;
+  margin: 2px;
+}
+
+.input-tips {
+  margin-top: 4px;
+  font-size: 12px;
+  color: #909399;
+}
+
+.tip-text {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+</style>

+ 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,

+ 25 - 1
src/views/hisStore/storePayment/index.vue

@@ -160,6 +160,15 @@
           @click="handlePayNotify"
           v-hasPermi="['store:storePayment:payNotify']"
         >同步订单状态</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          icon="el-icon-s-order"
+          size="mini"
+          type="success"
+          @click="wxOneClickShipping"
+        >微信一键线下自提发货
+        </el-button>
       </el-col>
 	  <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
@@ -287,7 +296,7 @@
 </template>
 
 <script>
-import { refundStorePayment,listStorePayment, getStorePayment, delStorePayment, addStorePayment, updateStorePayment, exportStorePayment,setPayNotify } from "@/api/hisStore/storePayment";
+import { refundStorePayment,listStorePayment, getStorePayment, delStorePayment, addStorePayment, updateStorePayment, exportStorePayment,setPayNotify, oneClickShipping} from "@/api/hisStore/storePayment";
 import { getCompanyList } from "@/api/company/company";
 import { listLiveOrderPayments,getLivePayment,updateLivePayment,exportLiveOrderPayments } from "@/api/live/liveOrderPayment";
 
@@ -605,6 +614,21 @@ export default {
         }).then(response => {
           this.download(response.msg);
         }).catch(function() {});
+    },
+    wxOneClickShipping(){
+      this.$confirm('是否同步微信线下发货?', '一键发货', {
+        confirmButtonText: '是',
+        cancelButtonText: '否',
+        type: 'warning'
+      }).then(() => {
+        oneClickShipping().then(response => {
+          if(response.code === 200) {
+            this.$message.success("操作成功!")
+            this.getList();
+          }
+        })
+      }).catch(() => {
+      });
     }
   }
 };

+ 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;

+ 7 - 4
src/views/live/live/index.vue

@@ -130,9 +130,9 @@
       <el-checkbox :indeterminate="isIndeterminate" v-model="allChecked" @change="toggleSelectAll">
         {{ multipleSelection.length > 0 ? `已选 ${multipleSelection.length} 条` : '选中本页' }}
       </el-checkbox>
-      <el-button  plain size="mini" @click="handleShelf">上架</el-button>
-      <el-button  plain size="mini" @click="handleUnshelf">下架</el-button>
-      <el-button  plain size="mini" @click="handleDeleteSelected">删除</el-button>
+      <el-button  plain size="mini" @click="handleShelf" v-hasPermi="['live:live:operation']">上架</el-button>
+      <el-button  plain size="mini" @click="handleUnshelf" v-hasPermi="['live:live:operation']">下架</el-button>
+      <el-button  plain size="mini" @click="handleDeleteSelected" v-hasPermi="['live:live:operation']">删除</el-button>
       <!--      <el-dropdown>-->
       <!--        <el-button plain size="mini">-->
       <!--          更多操作<i class="el-icon-arrow-down el-icon&#45;&#45;right"></i>-->
@@ -198,8 +198,9 @@
             size="mini"
             type="text"
             icon="el-icon-edit"
+            v-if="scope.row.status != 2"
             @click="handleUpdate(scope.row)"
-            v-hasPermi="['live:live:edit']"
+            v-hasPermi="['live:live:edit', 'live:live:operation']"
           >修改</el-button>
           <el-button
             size="mini"
@@ -230,12 +231,14 @@
               <el-dropdown-item
                 v-if="scope.row.status != 2"
                 @click.native="handleStart(scope.row)"
+                v-hasPermi="['live:live:operation']"
               >
                 <i class="el-icon-monitor"></i> 去直播
               </el-dropdown-item>
               <el-dropdown-item
                 v-if="scope.row.status == 2"
                 @click.native="handleEnded(scope.row)"
+                v-hasPermi="['live:live:operation']"
               >
                 <i class="el-icon-service"></i> 结束
               </el-dropdown-item>

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

@@ -125,7 +125,7 @@
             size="mini"
             :disabled="exportLoading"
             @click="handleExport"
-            v-hasPermi="['live:liveAfteraSales:export']"
+            v-hasPermi="['live:liveAfterSales:export']"
           >导出</el-button>
         </el-col>
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>

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

@@ -309,11 +309,259 @@
         <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">
+        <div style="margin-bottom: 20px; display: flex; justify-content: space-between; align-items: center;">
+          <el-button type="text" icon="el-icon-arrow-left" @click="showUserDetail = false">返回</el-button>
+          <el-button type="primary" size="small" icon="el-icon-download" :loading="userDetailExportLoading" @click="handleExportUserDetail">导出用户详情</el-button>
+        </div>
+        <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, exportLiveUserDetail } from "@/api/live/liveData";
 import { batchUpdateExternalContactNotes } from "@/api/qw/externalContact";
 import { addTag } from "@/api/qw/externalContact";
 
@@ -377,7 +625,15 @@ export default {
         rules: [],
         remarkFormat: 'time',
         position: 1
-      }
+      },
+      // 详情侧边栏
+      detailDrawerVisible: false,
+      currentLiveId: null,
+      detailData: {},
+      userDetailList: [],
+      userDetailLoading: false,
+      showUserDetail: false,
+      userDetailExportLoading: false
     };
   },
   created() {
@@ -582,8 +838,9 @@ export default {
     },
     /** 查看详情 */
     handleViewDetail(row) {
-      // 可以跳转到详情页面或打开详情弹窗
-      this.$message.info('查看详情功能待实现');
+      this.currentLiveId = row.liveId;
+      this.detailDrawerVisible = true;
+      this.loadDetailData();
     },
     /** 格式化时长 */
     formatDuration(seconds) {
@@ -609,6 +866,75 @@ 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);
+    },
+    /** 导出用户详情数据 */
+    handleExportUserDetail() {
+      if (!this.currentLiveId) {
+        this.$message.warning('无法获取直播间ID');
+        return;
+      }
+      if (!this.userDetailList || this.userDetailList.length === 0) {
+        this.$message.warning('暂无用户详情数据可导出');
+        return;
+      }
+      this.$confirm('是否确认导出当前直播间的用户详情数据?', "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        this.userDetailExportLoading = true;
+        return exportLiveUserDetail(this.currentLiveId);
+      }).then(response => {
+        if (response.code === 200) {
+          this.download(response.msg);
+          this.$message.success('导出成功');
+        }
+        this.userDetailExportLoading = false;
+      }).catch(() => {
+        this.userDetailExportLoading = false;
+      });
     }
   }
 };
@@ -656,4 +982,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,

+ 28 - 12
src/views/system/config/config.vue

@@ -277,11 +277,16 @@
       <el-tab-pane label="商城配置" name="his.store">
         <el-form ref="form1" :model="form7" label-width="180px">
 
-          <el-form-item label="货到付款支付比例" prop="payRate">
-            <el-tooltip class="item" effect="dark" content="货到付款支付比例(%)" placement="top-end">
+          <el-form-item label="物流代收支付比例" prop="payRate">
+            <el-tooltip class="item" effect="dark" content="物流代收支付比例(%)" placement="top-end">
               <el-input-number v-model="form7.payRate"></el-input-number>
             </el-tooltip>
           </el-form-item>
+          <el-form-item label="货到付款加收运费(元)" prop="payPostage">
+            <el-tooltip class="item" effect="dark" content="货到付款加收运费(元)" placement="top-end">
+              <el-input-number v-model="form7.payPostage"></el-input-number>
+            </el-tooltip>
+          </el-form-item>
           <el-form-item label="待支付订单取消时间" prop="unPayTime">
             <el-tooltip class="item" effect="dark" content="待支付订单取消时间(分钟)" placement="top-end">
               <el-input-number v-model="form7.unPayTime" :min="1"></el-input-number>
@@ -1145,6 +1150,14 @@
             </el-tooltip>
           </el-form-item>
 
+          <el-form-item label="是否开启微信发货" prop="isWeChatShipping">
+            <el-switch
+              v-model="form17.isWeChatShipping"
+              active-color="#13ce66"
+              inactive-color="#ff4949">
+            </el-switch>
+          </el-form-item>
+
           <div class="footer">
             <el-button type="primary" @click="submitForm17">提 交</el-button>
           </div>
@@ -2258,7 +2271,7 @@
       <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"
@@ -2268,9 +2281,9 @@
         <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`"
@@ -2995,6 +3008,9 @@ export default {
       if (this.form7.isIdVerification  == null){
         this.form7.isIdVerification = 0;
       }
+      if (this.form7.payPostage  == null){
+        this.form7.payPostage = 0;
+      }
     if(this.form7.isIdVerification != 1){
       this.form7.API_URL = null;
       this.form7.HASHCODE = null;
@@ -3335,7 +3351,7 @@ export default {
     validateMinAmount(rule, value, callback) {
       // debugger;
       // const maxAmount = this.form29.rules[].maxAmount
-      
+
       const index = rule.index;
       const maxAmount = this.form29.rules[index].maxAmount;
 
@@ -3372,24 +3388,24 @@ export default {
     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;
     },
@@ -3432,7 +3448,7 @@ export default {
   /* 关键改动:添加以下两行 */
   align-items: center;    /* 垂直居中对齐 */
   justify-content: flex-start; /* 水平方向从左到右排列(默认值,可显式写出) */
-  
+
   padding: 10px;
   border-bottom: 1px solid #ebeef5;
 }