xw há 19 horas atrás
pai
commit
2ca612656e

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

@@ -414,3 +414,12 @@ export function getMiniProgramList() {
     method: 'get'
   })
 }
+
+// 一键发货 - 将所有待发货订单标记为已发货并同步到微信物流
+export function batchDeliveryAllPendingOrders(shipmentType = 4) {
+  return request({
+    url: '/store/store/storeOrder/batchDeliveryAllPendingOrders',
+    method: 'post',
+    data: { shipmentType }
+  })
+}

+ 17 - 0
src/api/qw/qwCompany.js

@@ -55,4 +55,21 @@ export function exportQwCompany(query) {
     method: 'get',
     params: query
   })
+}
+
+// 批量更换小程序ID
+export function batchUpdateMiniAppId(data) {
+  return request({
+    url: '/qw/qwCompany/batchUpdateMiniAppId',
+    method: 'post',
+    data: data
+  })
+}
+
+// 获取小程序列表
+export function getMiniAppList() {
+  return request({
+    url: '/qw/qwCompany/getMiniAppList',
+    method: 'get'
+  })
 }

+ 9 - 0
src/api/store/storeOrder.js

@@ -273,3 +273,12 @@ export function auditPayRemain(data) {
     data: data
   })
 }
+
+// 一键发货 - 将所有待发货订单标记为已发货并同步到微信物流
+export function batchDeliveryAllPendingOrders(shipmentType = 4) {
+  return request({
+    url: '/store/store/storeOrder/batchDeliveryAllPendingOrders',
+    method: 'post',
+    data: { shipmentType }
+  })
+}

+ 115 - 1
src/views/course/coursePlaySourceConfig/index.vue

@@ -332,6 +332,53 @@
           <el-input v-model="form.msgDataFormat" placeholder="请输入msgDataFormat" />
         </el-form-item>
 
+        <el-divider content-position="left">自定义授权配置</el-divider>
+
+        <el-form-item label="自定义授权" prop="customAuthEnabled">
+          <el-switch
+            v-model="form.customAuthEnabled"
+            :active-value="1"
+            :inactive-value="0"
+            active-text="启用"
+            inactive-text="关闭"
+          />
+          <div class="form-item-tip">
+            启用后可独立配置该小程序的授权方式,不使用全局配置
+          </div>
+        </el-form-item>
+
+        <el-form-item 
+          v-if="form.customAuthEnabled === 1"
+          label="授权方式" 
+          prop="miniAppAuthType"
+        >
+          <el-radio-group v-model="form.miniAppAuthType">
+            <el-radio :label="1">小程序授权</el-radio>
+            <el-radio :label="2">服务号授权</el-radio>
+          </el-radio-group>
+          <div class="form-item-tip">
+            小程序授权:使用微信小程序原生授权组件获取用户信息
+          </div>
+          <div class="form-item-tip">
+            服务号授权:跳转H5页面通过服务号授权获取用户信息
+          </div>
+        </el-form-item>
+
+        <el-form-item 
+          v-if="form.customAuthEnabled === 1 && form.miniAppAuthType === 2"
+          label="授权域名" 
+          prop="userCourseAuthDomain"
+        >
+          <el-input
+            v-model="form.userCourseAuthDomain"
+            placeholder="请输入服务号授权域名,例如:https://scrm.syyxevip.cn/weixinOauth?appid=wx014d7c4fff877ea0"
+            clearable
+          />
+          <div class="form-item-tip">
+            看课时跳转H5服务号授权的域名地址
+          </div>
+        </el-form-item>
+
       </el-form>
       <div slot="footer" class="dialog-footer">
         <el-button type="primary" @click="submitForm">确 定</el-button>
@@ -509,6 +556,36 @@ export default {
         ],
         msgDataFormat: [
           { required: true, message: "msgDataFormat不能为空", trigger: "blur" }
+        ],
+        miniAppAuthType: [
+          { 
+            validator: (rule, value, callback) => {
+              if (this.form.customAuthEnabled === 1 && !value) {
+                callback(new Error('开启自定义授权时必须选择授权方式'));
+              } else {
+                callback();
+              }
+            }, 
+            trigger: 'change' 
+          }
+        ],
+        userCourseAuthDomain: [
+          { 
+            validator: (rule, value, callback) => {
+              if (this.form.customAuthEnabled === 1 && this.form.miniAppAuthType === 2) {
+                if (!value) {
+                  callback(new Error('选择服务号授权时必须填写授权域名'));
+                } else if (!/^https?:\/\/.+/.test(value)) {
+                  callback(new Error('请输入完整的URL地址'));
+                } else {
+                  callback();
+                }
+              } else {
+                callback();
+              }
+            }, 
+            trigger: 'blur' 
+          }
         ]
       }
     }
@@ -530,6 +607,23 @@ export default {
     });
     this.getList();
   },
+  watch: {
+    // 监听自定义授权开关变化
+    'form.customAuthEnabled'(newVal) {
+      if (newVal === 0) {
+        // 关闭自定义授权时,清空相关字段
+        this.form.miniAppAuthType = null;
+        this.form.userCourseAuthDomain = null;
+      }
+    },
+    // 监听授权方式变化
+    'form.miniAppAuthType'(newVal) {
+      if (newVal === 1) {
+        // 选择小程序原生授权时,清空域名字段
+        this.form.userCourseAuthDomain = null;
+      }
+    }
+  },
   methods: {
     resetForm,
     resetOption(){
@@ -799,7 +893,10 @@ export default {
         aesKey: 'HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E',
         msgDataFormat: 'JSON',
         type: '1',
-        status: 0
+        status: 0,
+        customAuthEnabled: 0,
+        miniAppAuthType: null,
+        userCourseAuthDomain: null
       }
       this.resetForm("form");
     },
@@ -817,5 +914,22 @@ export default {
 </script>
 
 <style scoped>
+/* 表单提示文字样式 */
+.form-item-tip {
+  font-size: 12px;
+  color: #909399;
+  line-height: 1.5;
+  margin-top: 4px;
+}
 
+/* 强调文字样式 */
+.emphasis-text {
+  font-weight: bold;
+  color: #E6A23C;
+}
+
+/* 分隔线样式 */
+.el-divider {
+  margin: 20px 0;
+}
 </style>

+ 233 - 20
src/views/hisStore/storeOrder/healthStoreList.vue

@@ -331,7 +331,7 @@
         >导出发货单
         </el-button>
       </el-col>
-      <el-col :span="1.5">
+      <!-- <el-col :span="1.5">
         <el-button
           icon="el-icon-upload2"
           plain
@@ -342,6 +342,16 @@
           @click="openDeliveryNote"
         >{{ orderUpload.isUploading ? '导入中...' : '导入发货单' }}
         </el-button>
+      </el-col> -->
+      <el-col :span="1.5">
+        <el-button
+          icon="el-icon-truck"
+          plain
+          size="mini"
+          type="warning"
+          @click="handleBatchDelivery"
+        >一键自提
+        </el-button>
       </el-col>
       <el-col :span="1.5">
         <el-button
@@ -842,7 +852,7 @@
       <span slot="footer" class="dialog-footer">
         <!-- 小程序Appid选择 -->
         <el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="120px" class="demo-ruleForm">
-          <el-form-item label="小程序:" prop="miniAppId">
+          <!-- <el-form-item label="小程序:" prop="miniAppId">
             <el-select
               v-model="ruleForm.miniAppId"
               clearable
@@ -856,7 +866,7 @@
                 :value="item.appId"
               />
             </el-select>
-          </el-form-item>
+          </el-form-item> -->
           <el-form-item label="发货类型:" prop="shipmentType">
             <el-radio-group v-model="ruleForm.shipmentType">
               <el-radio :label="1">线上发货</el-radio>
@@ -1063,6 +1073,95 @@
         >确认导出</el-button>
       </div>
     </el-dialog>
+
+    <!-- 导出发货单对话框 -->
+    <el-dialog 
+      :title="exportShippingDialog.title" 
+      :visible.sync="exportShippingDialog.open" 
+      width="500px" 
+      append-to-body
+      :close-on-click-modal="false"
+    >
+      <el-form label-width="120px">
+        <el-form-item label="发货时间范围" required>
+          <el-date-picker
+            v-model="exportShippingDialog.deliverySendTimeRange"
+            type="daterange"
+            range-separator="至"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+            value-format="yyyy-MM-dd"
+            :clearable="true"
+            style="width: 100%"
+            :picker-options="{
+              shortcuts: [
+                {
+                  text: '今天',
+                  onClick(picker) {
+                    const end = new Date();
+                    const start = new Date();
+                    picker.$emit('pick', [start, end]);
+                  }
+                },
+                {
+                  text: '昨天',
+                  onClick(picker) {
+                    const end = new Date();
+                    const start = new Date();
+                    start.setTime(start.getTime() - 3600 * 1000 * 24);
+                    end.setTime(end.getTime() - 3600 * 1000 * 24);
+                    picker.$emit('pick', [start, end]);
+                  }
+                },
+                {
+                  text: '最近7天',
+                  onClick(picker) {
+                    const end = new Date();
+                    const start = new Date();
+                    start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
+                    picker.$emit('pick', [start, end]);
+                  }
+                },
+                {
+                  text: '最近30天',
+                  onClick(picker) {
+                    const end = new Date();
+                    const start = new Date();
+                    start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
+                    picker.$emit('pick', [start, end]);
+                  }
+                }
+              ]
+            }"
+          />
+        </el-form-item>
+        
+        <el-alert
+          title="提示"
+          type="info"
+          :closable="false"
+          show-icon
+          style="margin-bottom: 15px;"
+        >
+          <div style="line-height: 1.6;">
+            <p style="margin: 5px 0;">• 默认导出<strong>已发货(待收货)</strong>状态的订单</p>
+            <p style="margin: 5px 0;">• 可以选择快捷时间范围:今天、昨天、最近7天等</p>
+            <p style="margin: 5px 0;">• 不选择时间将导出所有已发货订单(建议选择时间导出哈,避免卡顿)</p>
+          </div>
+        </el-alert>
+      </el-form>
+
+      <div slot="footer" class="dialog-footer">      
+        <el-button @click="cancelExportShippingDialog">取 消</el-button>
+        <el-button
+          type="primary"
+          @click="confirmExportShippingOrder"
+          :loading="exportShippingDialog.loading"
+          icon="el-icon-download"
+        >导出发货单</el-button>
+      </div>
+    </el-dialog>
+
   </div>
 </template>
 
@@ -1087,7 +1186,8 @@ import {
   editErpPhone,
   batchCreateErpOrder,
   batchSetErpOrder,
-  importShippingOrderTemplate
+  importShippingOrderTemplate,
+  batchDeliveryAllPendingOrders
 } from '@/api/hisStore/storeOrder'
 import { getUserList } from '@/api/hisStore/user'
 import { getAddressList } from '@/api/hisStore/userAddress'
@@ -1380,6 +1480,9 @@ export default {
         miniAppId: null,
         shipmentType: 4, 
       },
+      batchDeliveryForm: {
+        shipmentType: 2
+      },
       productQuery: {
         pageNum:1,
         pageSize:10,
@@ -1387,6 +1490,13 @@ export default {
       },
       options:[],
       showFinanceTableField: false,
+      // 导出发货单对话框
+      exportShippingDialog: {
+        open: false,
+        title: '导出发货单',
+        loading: false,
+        deliverySendTimeRange: [] // 发货时间范围
+      },
     }
   },
   computed: {
@@ -2738,41 +2848,144 @@ export default {
       })
     },
     handleExportShippingOrder() {
-      if (this.queryParams.status == '00') {
-        this.queryParams.status = null
+      console.log('=== 点击导出发货单按钮 ===')
+      console.log('exportShippingDialog:', this.exportShippingDialog)
+      // 打开导出发货单对话框
+      this.exportShippingDialog.open = true
+      console.log('exportShippingDialog.open 设置为:', this.exportShippingDialog.open)
+      // 默认使用筛选表单中的发货时间
+      this.exportShippingDialog.deliverySendTimeRange = this.deliverySendTimeRange ? [...this.deliverySendTimeRange] : []
+      console.log('deliverySendTimeRange:', this.exportShippingDialog.deliverySendTimeRange)
+    },
+    
+    // 确认导出发货单
+    confirmExportShippingOrder() {
+      // 构建导出参数
+      const params = { ...this.queryParams }
+      
+      // 处理状态参数:默认导出已发货订单(status=2)
+      if (params.status == '00') {
+        params.status = '2' // 默认导出待收货/已发货状态
+      } else if (!params.status) {
+        params.status = '2' // 如果没有选择状态,默认为待收货
       }
+      
+      // 处理下单时间范围
       if (this.createTimeRange != null && this.createTimeRange.length == 2) {
-        this.queryParams.createTimeRange = this.createTimeRange[0] + '--' + this.createTimeRange[1]
+        params.createTimeRange = this.createTimeRange[0] + '--' + this.createTimeRange[1]
       } else {
-        this.queryParams.createTimeRange = null
+        params.createTimeRange = null
       }
+      
+      // 处理支付时间范围
       if (this.payTimeRange != null && this.payTimeRange.length == 2) {
-        this.queryParams.payTimeRange = this.payTimeRange[0] + '--' + this.payTimeRange[1]
+        params.payTimeRange = this.payTimeRange[0] + '--' + this.payTimeRange[1]
       } else {
-        this.queryParams.payTimeRange = null
+        params.payTimeRange = null
       }
+      
+      // 处理导入时间范围(回单时间)
       if (this.deliveryImportTimeRange != null && this.deliveryImportTimeRange.length == 2) {
-        this.queryParams.deliveryImportTimeRange = this.deliveryImportTimeRange[0] + '--' + this.deliveryImportTimeRange[1]
+        params.deliveryImportTimeRange = this.deliveryImportTimeRange[0] + '--' + this.deliveryImportTimeRange[1]
       } else {
-        this.queryParams.deliveryImportTimeRange = null
+        params.deliveryImportTimeRange = null
       }
-      if (this.deliverySendTimeRange != null && this.deliverySendTimeRange.length == 2) {
-        this.queryParams.deliverySendTimeRange = this.deliverySendTimeRange[0] + '--' + this.deliverySendTimeRange[1]
+      
+      // 处理发货时间范围 - 使用对话框中输入的时间,转换为后端需要的格式 yyyyMMdd--yyyyMMdd
+      if (this.exportShippingDialog.deliverySendTimeRange != null && this.exportShippingDialog.deliverySendTimeRange.length == 2) {
+        // 将 yyyy-MM-dd 格式转换为 yyyyMMdd 格式
+        const startDate = this.exportShippingDialog.deliverySendTimeRange[0].replace(/-/g, '')
+        const endDate = this.exportShippingDialog.deliverySendTimeRange[1].replace(/-/g, '')
+        params.deliverySendTimeRange = startDate + '--' + endDate
       } else {
-        this.queryParams.deliverySendTimeRange = null
+        params.deliverySendTimeRange = null
       }
-      const queryParams = this.addDateRange(this.queryParams, this.dateRange)
-      this.$confirm('是否确认导出所有订单明细数据项?', '警告', {
+      
+      // 移除空值参数
+      Object.keys(params).forEach(key => {
+        if (params[key] === '' || params[key] === undefined || params[key] === null) {
+          delete params[key]
+        }
+      })
+      
+      // 构建确认消息
+      let confirmMessage = '是否确认导出发货单数据?'
+      if (params.deliverySendTimeRange) {
+        const dateRange = this.exportShippingDialog.deliverySendTimeRange[0] + ' 至 ' + this.exportShippingDialog.deliverySendTimeRange[1]
+        confirmMessage = `是否确认导出 ${dateRange} 期间的发货单数据?`
+      } else {
+        confirmMessage = '未选择发货时间,将导出所有已发货订单,是否确认?'
+      }
+      
+      this.$confirm(confirmMessage, '导出发货单', {
         confirmButtonText: '确定',
         cancelButtonText: '取消',
         type: 'warning'
-      }).then(function() {
-        return healthExportShippingOrder(queryParams)
+      }).then(() => {
+        this.exportShippingDialog.loading = true
+        return healthExportShippingOrder(params)
       }).then(response => {
         this.download(response.msg)
-      }).catch(function() {
+        this.$modal.msgSuccess('发货单导出成功')
+        // 关闭对话框
+        this.exportShippingDialog.open = false
+      }).catch(error => {
+        if (error !== 'cancel') {
+          console.error('导出发货单失败:', error)
+          this.$modal.msgError('导出发货单失败')
+        }
+      }).finally(() => {
+        this.exportShippingDialog.loading = false
       })
     },
+    
+    // 取消导出发货单
+    cancelExportShippingDialog() {
+      this.exportShippingDialog.open = false
+      this.exportShippingDialog.deliverySendTimeRange = []
+    },
+    
+    // 一键发货功能
+    async handleBatchDelivery() {
+      this.$confirm(
+        '此操作将把所有待发货订单标记为已发货并同步到微信物流,操作不可逆,是否继续?',
+        '确认一键发货',
+        {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning',
+          dangerouslyUseHTMLString: true,
+          customClass: 'batch-delivery-confirm'
+        }
+      ).then(async () => {
+        try {
+          this.loading = true;
+          const response = await batchDeliveryAllPendingOrders(this.batchDeliveryForm.shipmentType);
+          
+          if (response.code === 200) {
+            this.$message.success(`一键发货成功:${response.msg || '已成功处理待发货订单'}`);
+            // 刷新数据
+            this.getList();
+          } else {
+            this.$message.error(`一键发货失败:${response.msg || '服务器返回错误'}`);
+          }
+        } catch (error) {
+          console.error('一键发货失败:', error);
+          let errorMessage = '一键发货失败,请稍后重试';
+          if (error.response && error.response.data && error.response.data.msg) {
+            errorMessage = error.response.data.msg;
+          } else if (error.message) {
+            errorMessage = error.message;
+          }
+          this.$message.error(errorMessage);
+        } finally {
+          this.loading = false;
+        }
+      }).catch(() => {
+        // 用户取消操作
+        this.$message.info('已取消一键发货');
+      });
+    },
 
   }
 }

+ 231 - 20
src/views/hisStore/storeOrder/index.vue

@@ -353,7 +353,7 @@
         >导出发货单
         </el-button>
       </el-col>
-      <el-col :span="1.5">
+      <!-- <el-col :span="1.5">
         <el-button
           icon="el-icon-upload2"
           plain
@@ -364,6 +364,16 @@
           @click="openDeliveryNote"
         >{{ orderUpload.isUploading ? '导入中...' : '导入发货单' }}
         </el-button>
+      </el-col> -->
+      <el-col :span="1.5">
+        <el-button
+          icon="el-icon-truck"
+          plain
+          size="mini"
+          type="warning"
+          @click="handleBatchDelivery"
+        >一键自提
+        </el-button>
       </el-col>
       <el-col :span="1.5">
         <el-button
@@ -920,7 +930,7 @@
       <span slot="footer" class="dialog-footer">
         <!-- 小程序Appid选择 -->
         <el-form ref="ruleForm" :model="ruleForm" :rules="rules" class="demo-ruleForm" label-width="120px">
-          <el-form-item label="小程序:" prop="miniAppId">
+          <!-- <el-form-item label="小程序:" prop="miniAppId">
             <el-select
               v-model="ruleForm.miniAppId"
               clearable
@@ -934,7 +944,7 @@
                 :value="item.appId"
               />
             </el-select>
-          </el-form-item>
+          </el-form-item> -->
           <el-form-item label="发货类型:" prop="shipmentType">
             <el-radio-group v-model="ruleForm.shipmentType">
               <el-radio :label="1">线上发货</el-radio>
@@ -1149,6 +1159,94 @@
       </div>
     </el-dialog>
 
+    <!-- 导出发货单对话框 -->
+    <el-dialog 
+      :title="exportShippingDialog.title" 
+      :visible.sync="exportShippingDialog.open" 
+      width="500px" 
+      append-to-body
+      :close-on-click-modal="false"
+    >
+      <el-form label-width="120px">
+        <el-form-item label="发货时间范围" required>
+          <el-date-picker
+            v-model="exportShippingDialog.deliverySendTimeRange"
+            type="daterange"
+            range-separator="至"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+            value-format="yyyy-MM-dd"
+            :clearable="true"
+            style="width: 100%"
+            :picker-options="{
+              shortcuts: [
+                {
+                  text: '今天',
+                  onClick(picker) {
+                    const end = new Date();
+                    const start = new Date();
+                    picker.$emit('pick', [start, end]);
+                  }
+                },
+                {
+                  text: '昨天',
+                  onClick(picker) {
+                    const end = new Date();
+                    const start = new Date();
+                    start.setTime(start.getTime() - 3600 * 1000 * 24);
+                    end.setTime(end.getTime() - 3600 * 1000 * 24);
+                    picker.$emit('pick', [start, end]);
+                  }
+                },
+                {
+                  text: '最近7天',
+                  onClick(picker) {
+                    const end = new Date();
+                    const start = new Date();
+                    start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
+                    picker.$emit('pick', [start, end]);
+                  }
+                },
+                {
+                  text: '最近30天',
+                  onClick(picker) {
+                    const end = new Date();
+                    const start = new Date();
+                    start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
+                    picker.$emit('pick', [start, end]);
+                  }
+                }
+              ]
+            }"
+          />
+        </el-form-item>
+        
+        <el-alert
+          title="提示"
+          type="info"
+          :closable="false"
+          show-icon
+          style="margin-bottom: 15px;"
+        >
+          <div style="line-height: 1.6;">
+            <p style="margin: 5px 0;">• 默认导出<strong>已发货(待收货)</strong>状态的订单</p>
+            <p style="margin: 5px 0;">• 可以选择快捷时间范围:今天、昨天、最近7天等</p>
+            <p style="margin: 5px 0;">• 不选择时间将导出所有已发货订单</p>
+          </div>
+        </el-alert>
+      </el-form>
+
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="cancelExportShippingDialog">取 消</el-button>
+        <el-button
+          type="primary"
+          @click="confirmExportShippingOrder"
+          :loading="exportShippingDialog.loading"
+          icon="el-icon-download"
+        >导出发货单</el-button>
+      </div>
+    </el-dialog>
+
   </div>
 </template>
 
@@ -1167,7 +1265,8 @@ import {
   queryErpPhone,
   saveErpPhone,editErpPhone,batchCreateErpOrder,batchSetErpOrder,
   importShippingOrderTemplate,
-  getMiniProgramList
+  getMiniProgramList,
+  batchDeliveryAllPendingOrders
 } from '@/api/hisStore/storeOrder'
 import { getUserList } from '@/api/hisStore/user'
 import { getAddressList } from '@/api/hisStore/userAddress'
@@ -1463,6 +1562,9 @@ export default {
         miniAppId: null,
         shipmentType: 4 
       },
+      batchDeliveryForm: {
+        shipmentType: 2
+      },
       productQuery: {
         pageNum:1,
         pageSize:10,
@@ -1470,6 +1572,13 @@ export default {
       },
       options:[],
       showFinanceTableField: false,
+      // 导出发货单对话框
+      exportShippingDialog: {
+        open: false,
+        title: '导出发货单',
+        loading: false,
+        deliverySendTimeRange: [] // 发货时间范围
+      },
     }
   },
   computed: {
@@ -2563,41 +2672,102 @@ export default {
       })
     },
     handleExportShippingOrder() {
-      if (this.queryParams.status == '00') {
-        this.queryParams.status = null
+      console.log('=== 点击导出发货单按钮 ===')
+      console.log('exportShippingDialog:', this.exportShippingDialog)
+      // 打开导出发货单对话框
+      this.exportShippingDialog.open = true
+      console.log('exportShippingDialog.open 设置为:', this.exportShippingDialog.open)
+      // 默认使用筛选表单中的发货时间
+      this.exportShippingDialog.deliverySendTimeRange = this.deliverySendTimeRange ? [...this.deliverySendTimeRange] : []
+      console.log('deliverySendTimeRange:', this.exportShippingDialog.deliverySendTimeRange)
+    },
+    
+    // 确认导出发货单
+    confirmExportShippingOrder() {
+      // 构建导出参数
+      const params = { ...this.queryParams }
+      
+      // 处理状态参数:默认导出已发货订单(status=2)
+      if (params.status == '00') {
+        params.status = '2' // 默认导出待收货/已发货状态
+      } else if (!params.status) {
+        params.status = '2' // 如果没有选择状态,默认为待收货
       }
+      
+      // 处理下单时间范围
       if (this.createTimeRange != null && this.createTimeRange.length == 2) {
-        this.queryParams.createTimeRange = this.createTimeRange[0] + '--' + this.createTimeRange[1]
+        params.createTimeRange = this.createTimeRange[0] + '--' + this.createTimeRange[1]
       } else {
-        this.queryParams.createTimeRange = null
+        params.createTimeRange = null
       }
+      
+      // 处理支付时间范围
       if (this.payTimeRange != null && this.payTimeRange.length == 2) {
-        this.queryParams.payTimeRange = this.payTimeRange[0] + '--' + this.payTimeRange[1]
+        params.payTimeRange = this.payTimeRange[0] + '--' + this.payTimeRange[1]
       } else {
-        this.queryParams.payTimeRange = null
+        params.payTimeRange = null
       }
+      
+      // 处理导入时间范围(回单时间)
       if (this.deliveryImportTimeRange != null && this.deliveryImportTimeRange.length == 2) {
-        this.queryParams.deliveryImportTimeRange = this.deliveryImportTimeRange[0] + '--' + this.deliveryImportTimeRange[1]
+        params.deliveryImportTimeRange = this.deliveryImportTimeRange[0] + '--' + this.deliveryImportTimeRange[1]
       } else {
-        this.queryParams.deliveryImportTimeRange = null
+        params.deliveryImportTimeRange = null
       }
-      if (this.deliverySendTimeRange != null && this.deliverySendTimeRange.length == 2) {
-        this.queryParams.deliverySendTimeRange = this.deliverySendTimeRange[0] + '--' + this.deliverySendTimeRange[1]
+      
+      // 处理发货时间范围 - 使用对话框中输入的时间,转换为后端需要的格式 yyyyMMdd--yyyyMMdd
+      if (this.exportShippingDialog.deliverySendTimeRange != null && this.exportShippingDialog.deliverySendTimeRange.length == 2) {
+        // 将 yyyy-MM-dd 格式转换为 yyyyMMdd 格式
+        const startDate = this.exportShippingDialog.deliverySendTimeRange[0].replace(/-/g, '')
+        const endDate = this.exportShippingDialog.deliverySendTimeRange[1].replace(/-/g, '')
+        params.deliverySendTimeRange = startDate + '--' + endDate
       } else {
-        this.queryParams.deliverySendTimeRange = null
+        params.deliverySendTimeRange = null
       }
-      const queryParams = this.addDateRange(this.queryParams, this.dateRange)
-      this.$confirm('是否确认导出所有订单明细数据项?', '警告', {
+      
+      // 移除空值参数
+      Object.keys(params).forEach(key => {
+        if (params[key] === '' || params[key] === undefined || params[key] === null) {
+          delete params[key]
+        }
+      })
+      
+      // 构建确认消息
+      let confirmMessage = '是否确认导出发货单数据?'
+      if (params.deliverySendTimeRange) {
+        const dateRange = this.exportShippingDialog.deliverySendTimeRange[0] + ' 至 ' + this.exportShippingDialog.deliverySendTimeRange[1]
+        confirmMessage = `是否确认导出 ${dateRange} 期间的发货单数据?`
+      } else {
+        confirmMessage = '未选择发货时间,将导出所有已发货订单,是否确认?'
+      }
+      
+      this.$confirm(confirmMessage, '导出发货单', {
         confirmButtonText: '确定',
         cancelButtonText: '取消',
         type: 'warning'
-      }).then(function() {
-        return exportShippingOrder(queryParams)
+      }).then(() => {
+        this.exportShippingDialog.loading = true
+        return exportShippingOrder(params)
       }).then(response => {
         this.download(response.msg)
-      }).catch(function() {
+        this.$modal.msgSuccess('发货单导出成功')
+        // 关闭对话框
+        this.exportShippingDialog.open = false
+      }).catch(error => {
+        if (error !== 'cancel') {
+          console.error('导出发货单失败:', error)
+          this.$modal.msgError('导出发货单失败')
+        }
+      }).finally(() => {
+        this.exportShippingDialog.loading = false
       })
     },
+    
+    // 取消导出发货单
+    cancelExportShippingDialog() {
+      this.exportShippingDialog.open = false
+      this.exportShippingDialog.deliverySendTimeRange = []
+    },
     handleImport() {
       this.upload.title = '导入银行回单'
       this.upload.open = true
@@ -2842,6 +3012,47 @@ export default {
         this.$refs.upload.clearFiles()
       }
     },
+    // 一键发货功能
+    async handleBatchDelivery() {
+      this.$confirm(
+        '此操作将把所有待发货订单标记为已发货并同步到微信物流,操作不可逆,是否继续?',
+        '确认一键发货',
+        {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning',
+          dangerouslyUseHTMLString: true,
+          customClass: 'batch-delivery-confirm'
+        }
+      ).then(async () => {
+        try {
+          this.loading = true;
+          const response = await batchDeliveryAllPendingOrders(this.batchDeliveryForm.shipmentType);
+          
+          if (response.code === 200) {
+            this.$message.success(`一键发货成功:${response.msg || '已成功处理待发货订单'}`);
+            // 刷新数据
+            this.getList();
+          } else {
+            this.$message.error(`一键发货失败:${response.msg || '服务器返回错误'}`);
+          }
+        } catch (error) {
+          console.error('一键发货失败:', error);
+          let errorMessage = '一键发货失败,请稍后重试';
+          if (error.response && error.response.data && error.response.data.msg) {
+            errorMessage = error.response.data.msg;
+          } else if (error.message) {
+            errorMessage = error.message;
+          }
+          this.$message.error(errorMessage);
+        } finally {
+          this.loading = false;
+        }
+      }).catch(() => {
+        // 用户取消操作
+        this.$message.info('已取消一键发货');
+      });
+    },
     getProduct(value){
       //获取商品列表
       this.productQuery.productName = value;

+ 224 - 2
src/views/live/liveConsole/LiveConsole.vue

@@ -102,6 +102,55 @@
     <div class="middle-panel">
       <h2>消息管理</h2>
 
+      <!-- 新增:用户行为记录区域 -->
+      <div class="behavior-records">
+        <h3>用户行为记录</h3>
+        <div class="message-container" @click="handleBehaviorBoxClick">
+          <el-scrollbar class="custom-scrollbar" style="height: 300px; width: 100%;" ref="behaviorScrollRef">
+            <div v-if="behaviorList.length === 0" style="text-align: center; padding: 40px 0; color: #999;">
+              <i class="el-icon-info" style="font-size: 48px; margin-bottom: 10px;"></i>
+              <p>暂无行为记录</p>
+            </div>
+            <el-row v-else v-for="behavior in behaviorList" :key="behavior.id" style="padding: 8px 0" type="flex" align="top">
+              <el-col :span="3" style="margin-left: 10px">
+                <el-avatar :src="behavior.avatar" :size="40"></el-avatar>
+              </el-col>
+              <el-col :span="20">
+                <el-row style="margin-left: 10px">
+                  <el-col>
+                    <div style="font-size: 12px; color: #999; margin-bottom: 3px;">{{ behavior.nickName }}</div>
+                  </el-col>
+                  <el-col :span="24">
+                    <div style="white-space: normal; word-wrap: break-word;background-color: #f0f2f5; padding: 8px; border-radius: 5px;font-size: 14px;">
+                      <i :class="getBehaviorIcon(behavior.behaviorType)" :style="{ color: getBehaviorColor(behavior.behaviorType) }"></i>
+                      <span style="margin-left: 5px; font-weight: bold;">{{ behavior.behaviorDesc }}</span>
+                      <span v-if="behavior.resourceName" style="margin-left: 8px; color: #606266;">「{{ behavior.resourceName }}」</span>
+                    </div>
+                  </el-col>
+                  <el-col style="margin-top: 5px;">
+                    <span style="font-size: 12px; color: #999;">{{ formatBehaviorTime(behavior.behaviorTime) }}</span>
+                    <span v-if="behavior.deviceInfo" style="margin-left: 10px; font-size: 12px; color: #999;">【{{ behavior.deviceInfo }}】</span>
+                  </el-col>
+                </el-row>
+              </el-col>
+            </el-row>
+            <!-- 底部留白 -->
+            <div style="height: 20px;"></div>
+          </el-scrollbar>
+          <!-- 加载最新行为按钮 -->
+          <el-button
+            v-if="showLoadLatestBehaviorBtn"
+            class="load-latest-btn"
+            type="primary"
+            size="small"
+            @click.stop="loadLatestBehaviors"
+            :disabled="isLoadingLatestBehavior"
+            :loading="isLoadingLatestBehavior"
+            icon="el-icon-refresh">
+            加载最新行为
+          </el-button>
+        </div>
+      </div>
 
       <div class="discussion-messages">
         <h3>讨论区消息</h3>
@@ -416,6 +465,29 @@ export default {
         pageNum: 1,
         pageSize: 30,
         liveId: null
+      },
+      // 新增:行为记录相关
+      behaviorList: [], // 行为列表
+      showLoadLatestBehaviorBtn: false, // 显示加载最新行为按钮
+      isLoadingLatestBehavior: false, // 是否正在加载最新行为
+      isAutoBehaviorScroll: true, // 是否启用行为自动滚动
+      // 行为类型映射(后端定义的 behaviorType)
+      behaviorTypeMap: {
+        1: '进入直播间',
+        2: '退出直播间',
+        3: '点赞',
+        4: '发送消息',
+        5: '点击商品',
+        6: '加入购物车',
+        7: '购买商品',
+        8: '收藏商品',
+        9: '关注主播',
+        10: '领取优惠券',
+        11: '参与抽奖',
+        12: '领取红包',
+        13: '分享直播间',
+        14: '观看时长记录',
+        15: '完课记录'
       }
     };
   },
@@ -711,9 +783,12 @@ export default {
     },
     handleWsMessage(event) {
       let { code, data } = JSON.parse(event.data)
-      if (code === 200) {
+      if (code === 200 || code === 0) { // 兼容后端返回 code: 0
         let { cmd } = data
-        if (cmd === 'sendMsg') {
+        // 新增:处理行为记录(后端推送的 cmd: 'behaviorTrack')
+        if (cmd === 'behaviorTrack') {
+          this.handleBehaviorRecord(data.data); // data.data 是真正的行为数据
+        } else if (cmd === 'sendMsg') {
           let message = JSON.parse(data.data)
 
           let user = this.alDisplayList.find(u => u.userId === message.userId)
@@ -1458,6 +1533,137 @@ export default {
         this.loadLiveTask(); // 重新加载任务列表
       }
     },
+    // 新增:处理接收到的行为记录(WebSocket推送)
+    handleBehaviorRecord(behaviorData) {
+      if (!behaviorData) return;
+      
+      const behavior = {
+        id: behaviorData.id || Date.now(),
+        userId: behaviorData.userId,
+        nickName: this.getUserName(behaviorData.userId),
+        avatar: this.getUserAvatar(behaviorData.userId),
+        behaviorType: behaviorData.behaviorType,
+        behaviorDesc: behaviorData.behaviorDesc || this.getBehaviorText(behaviorData.behaviorType),
+        behaviorTime: behaviorData.behaviorTime || new Date().getTime(),
+        resourceName: this.getResourceName(behaviorData),
+        deviceInfo: behaviorData.deviceInfo || ''
+      };
+      
+      // 添加到行为列表头部(最新的在上面)
+      if (this.behaviorList.length >= 50) {
+        this.behaviorList.pop();
+      }
+      this.behaviorList.unshift(behavior);
+      
+      // 如果启用自动滚动,滚动到顶部
+      if (this.isAutoBehaviorScroll) {
+        this.$nextTick(() => {
+          if (this.$refs.behaviorScrollRef && this.$refs.behaviorScrollRef.wrap) {
+            this.$refs.behaviorScrollRef.wrap.scrollTop = 0;
+          }
+        });
+      } else {
+        this.showLoadLatestBehaviorBtn = true;
+      }
+    },
+    // 新增:获取用户头像
+    getUserAvatar(userId) {
+      const user = this.alDisplayList.find(u => u.userId === userId);
+      return user?.avatar || 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png';
+    },
+    // 新增:获取用户昵称
+    getUserName(userId) {
+      const user = this.alDisplayList.find(u => u.userId === userId);
+      return user?.nickName || `用户${userId}`;
+    },
+    // 新增:获取资源名称
+    getResourceName(behaviorData) {
+      try {
+        if (behaviorData.extData) {
+          const extData = JSON.parse(behaviorData.extData);
+          if ([5, 6, 7, 8].includes(behaviorData.behaviorType)) {
+            return extData.goodsName || '';
+          } else if (behaviorData.behaviorType === 10) {
+            return extData.couponName || '';
+          }
+        }
+      } catch (e) {
+        console.error('解析extData失败:', e);
+      }
+      return '';
+    },
+    // 新增:点击行为框
+    handleBehaviorBoxClick() {
+      this.isAutoBehaviorScroll = false;
+      this.$nextTick(() => {
+        if (this.$refs.behaviorScrollRef && this.$refs.behaviorScrollRef.wrap) {
+          const wrap = this.$refs.behaviorScrollRef.wrap;
+          const scrollHeight = wrap.scrollHeight;
+          const clientHeight = wrap.clientHeight;
+          const currentScrollTop = wrap.scrollTop;
+          const maxScrollTop = scrollHeight - clientHeight;
+          if (currentScrollTop < maxScrollTop - 50) {
+            this.showLoadLatestBehaviorBtn = true;
+          }
+        }
+      });
+    },
+    // 新增:加载最新行为
+    loadLatestBehaviors() {
+      if (this.isLoadingLatestBehavior) return;
+      this.isLoadingLatestBehavior = false;
+      this.showLoadLatestBehaviorBtn = false;
+      this.isAutoBehaviorScroll = true;
+      this.$nextTick(() => {
+        if (this.$refs.behaviorScrollRef && this.$refs.behaviorScrollRef.wrap) {
+          this.$refs.behaviorScrollRef.wrap.scrollTop = 0;
+        }
+      });
+    },
+    // 新增:格式化行为时间
+    formatBehaviorTime(timestamp) {
+      if (!timestamp) return '';
+      const date = new Date(timestamp);
+      const now = new Date();
+      const diff = now - date;
+      if (diff < 60000) return '刚刚';
+      if (diff < 3600000) return Math.floor(diff / 60000) + '分钟前';
+      if (date.toDateString() === now.toDateString()) {
+        const hours = String(date.getHours()).padStart(2, '0');
+        const minutes = String(date.getMinutes()).padStart(2, '0');
+        return `${hours}:${minutes}`;
+      }
+      const month = String(date.getMonth() + 1).padStart(2, '0');
+      const day = String(date.getDate()).padStart(2, '0');
+      const hours = String(date.getHours()).padStart(2, '0');
+      const minutes = String(date.getMinutes()).padStart(2, '0');
+      return `${month}-${day} ${hours}:${minutes}`;
+    },
+    // 新增:获取行为文本
+    getBehaviorText(behaviorType) {
+      return this.behaviorTypeMap[behaviorType] || '未知操作';
+    },
+    // 新增:获取行为图标
+    getBehaviorIcon(behaviorType) {
+      const iconMap = {
+        1: 'el-icon-user', 2: 'el-icon-back', 3: 'el-icon-star-on',
+        4: 'el-icon-chat-dot-round', 5: 'el-icon-view', 6: 'el-icon-shopping-cart-1',
+        7: 'el-icon-shopping-cart-2', 8: 'el-icon-star-off', 9: 'el-icon-plus',
+        10: 'el-icon-discount', 11: 'el-icon-trophy', 12: 'el-icon-present',
+        13: 'el-icon-share', 14: 'el-icon-video-play', 15: 'el-icon-success'
+      };
+      return iconMap[behaviorType] || 'el-icon-info';
+    },
+    // 新增:获取行为颜色
+    getBehaviorColor(behaviorType) {
+      const colorMap = {
+        1: '#67C23A', 2: '#909399', 3: '#FF6B9D', 4: '#409EFF',
+        5: '#43e97b', 6: '#ffa94d', 7: '#F56C6C', 8: '#ff6b6b',
+        9: '#f59e0b', 10: '#ffd93d', 11: '#c084fc', 12: '#fb923c',
+        13: '#38bdf8', 14: '#a78bfa', 15: '#34d399'
+      };
+      return colorMap[behaviorType] || '#909399';
+    }
   },
   beforeDestroy() {
     if (this.autoMsgTimer != null) {
@@ -1786,4 +1992,20 @@ export default {
   z-index: 10;
   box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
 }
+
+/* 新增:行为记录样式 */
+.behavior-records {
+  margin-bottom: 20px;
+  background: #fff;
+  padding: 15px;
+  border-radius: 8px;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+}
+
+.behavior-records h3 {
+  margin: 0 0 15px 0;
+  font-size: 16px;
+  font-weight: 600;
+  color: #303133;
+}
 </style>

+ 137 - 1
src/views/qw/qwCompany/index.vue

@@ -20,6 +20,22 @@
         />
       </el-form-item>
 
+      <el-form-item label="关联公司" prop="companyIds">
+        <el-select 
+          v-model="queryParams.companyIds" 
+          placeholder="请选择公司" 
+          clearable 
+          filterable
+          size="small"
+        >
+          <el-option
+            v-for="item in companys"
+            :key="item.companyId"
+            :label="item.companyName"
+            :value="String(item.companyId)"
+          />
+        </el-select>
+      </el-form-item>
 
       <el-form-item label="状态" prop="status">
         <el-select v-model="queryParams.status" placeholder="请选择状态" clearable size="small">
@@ -81,6 +97,19 @@
           v-hasPermi="['qw:qwCompany:export']"
         >导出</el-button>
       </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-s-operation"
+          size="mini"
+          @click="handleBatchUpdateMiniAppId"
+          v-hasPermi="['qw:qwCompany:edit']"
+        >批量更换小程序</el-button>
+      </el-col>
+      <el-col :span="1.5" v-if="ids.length > 0">
+        <span style="color: #909399; font-size: 14px; line-height: 32px;">已选中: {{ ids.length }}项</span>
+      </el-col>
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
@@ -228,11 +257,42 @@
         <el-button @click="cancel">取 消</el-button>
       </div>
     </el-dialog>
+
+    <!-- 批量更换小程序对话框 -->
+    <el-dialog title="批量更换小程序" :visible.sync="batchMiniAppDialogVisible" width="400px" append-to-body>
+      <el-form ref="batchMiniAppForm" :model="batchMiniAppForm" :rules="batchMiniAppRules" label-width="100px">
+        <el-form-item label="选择小程序" prop="miniAppId">
+          <el-select 
+            v-model="batchMiniAppForm.miniAppId" 
+            placeholder="请选择小程序" 
+            clearable 
+            filterable
+            style="width: 100%;"
+          >
+            <el-option
+              v-for="item in batchMiniAppOptions"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            />
+          </el-select>
+        </el-form-item>
+        <el-alert
+          :title="`此操作将更新已选中的 ${ids.length} 个企微主体的小程序配置`"
+          type="warning"
+          :closable="false"
+        />
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitBatchMiniAppForm" :loading="batchUpdateLoading">确 定</el-button>
+        <el-button @click="cancelBatchMiniApp">取 消</el-button>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
 <script>
-import { listQwCompany, getQwCompany, delQwCompany, addQwCompany, updateQwCompany, exportQwCompany } from "@/api/qw/qwCompany";
+import { listQwCompany, getQwCompany, delQwCompany, addQwCompany, updateQwCompany, exportQwCompany, batchUpdateMiniAppId, getMiniAppList } from "@/api/qw/qwCompany";
 import { getCompanyList } from "@/api/company/company";
 import { listAll } from '@/api/course/coursePlaySourceConfig'
 export default {
@@ -258,6 +318,8 @@ export default {
       loading: true,
       // 导出遮罩层
       exportLoading: false,
+      // 批量更新加载状态
+      batchUpdateLoading: false,
       // 选中数组
       ids: [],
       // 非单个禁用
@@ -274,6 +336,8 @@ export default {
       title: "",
       // 是否显示弹出层
       open: false,
+      // 批量更换小程序对话框显示状态
+      batchMiniAppDialogVisible: false,
       // 状态字典
       statusOptions: [],
       // 查询参数
@@ -299,6 +363,12 @@ export default {
       },
       // 表单参数
       form: {},
+      // 批量更换小程序表单参数
+      batchMiniAppForm: {
+        miniAppId: ''
+      },
+      // 批量更换小程序选项列表
+      batchMiniAppOptions: [],
       companys:[],
       maAppList:[],
       miniAppList:[],
@@ -309,6 +379,12 @@ export default {
         realmNameUrl: [{ required: true, message: '请输入域名地址', trigger: 'blur' }],
         corpName: [{ required: true, message: '请输入名称', trigger: 'blur' }],
 
+      },
+      // 批量更换小程序表单校验
+      batchMiniAppRules: {
+        miniAppId: [
+          { required: true, message: '请选择小程序', trigger: 'change' }
+        ]
       }
     };
   },
@@ -471,6 +547,66 @@ export default {
           this.download(response.msg);
           this.exportLoading = false;
         }).catch(() => {});
+    },
+    /** 打开批量更换小程序对话框 */
+    handleBatchUpdateMiniAppId() {
+      // 校验是否勾选了主体
+      if (!this.ids || this.ids.length === 0) {
+        this.$message.warning('请先勾选需要更换的主体');
+        return;
+      }
+      // 获取小程序列表
+      getMiniAppList().then(response => {
+        if (response.code === 200 && response.data) {
+          this.batchMiniAppOptions = response.data;
+          this.batchMiniAppForm = {
+            miniAppId: ''
+          };
+          this.batchMiniAppDialogVisible = true;
+          this.$nextTick(() => {
+            this.$refs.batchMiniAppForm.clearValidate();
+          });
+        }
+      }).catch(() => {
+        this.msgError("获取小程序列表失败");
+      });
+    },
+    /** 取消批量更换小程序 */
+    cancelBatchMiniApp() {
+      this.batchMiniAppDialogVisible = false;
+      this.batchMiniAppForm = {
+        miniAppId: ''
+      };
+      this.$refs.batchMiniAppForm.resetFields();
+    },
+    /** 提交批量更换小程序 */
+    submitBatchMiniAppForm() {
+      this.$refs.batchMiniAppForm.validate(valid => {
+        if (valid) {
+          this.$confirm(`确定要将已选中的 ${this.ids.length} 个企微主体的小程序更换为所选小程序吗?`, '提示', {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning'
+          }).then(() => {
+            this.batchUpdateLoading = true;
+            const data = {
+              miniAppId: this.batchMiniAppForm.miniAppId,
+              ids: this.ids
+            };
+            batchUpdateMiniAppId(data).then(response => {
+              this.msgSuccess(response.msg || "批量更新完成");
+              this.batchMiniAppDialogVisible = false;
+              this.batchUpdateLoading = false;
+              // 清空选中状态
+              this.ids = [];
+              // 刷新列表
+              this.getList();
+            }).catch(() => {
+              this.batchUpdateLoading = false;
+            });
+          }).catch(() => {});
+        }
+      });
     }
   }
 };