Selaa lähdekoodia

Merge remote-tracking branch 'origin/master'

yuhongqi 3 päivää sitten
vanhempi
commit
07ba3539eb

+ 3 - 0
.env.prod-hst

@@ -50,3 +50,6 @@ VUE_CLI_BABEL_TRANSPILE_MODULES = true
 
 #上面地址是为了解决跨越,用nginx进行了转发
 VUE_APP_LIVE_WS_URL = wss://websocket.schstyl.cn/ws
+
+# 鸿森堂
+VUE_APP_FS_USER_INFO = 'hst'

+ 8 - 1
src/api/company/companyUser.js

@@ -16,6 +16,13 @@ export function getAllUserlist(query) {
   })
 }
 
+export function getAllCompanyUserListNoParams() {
+  return request({
+    url: '/company/companyUser/getAllCompanyUserListNoParams',
+    method: 'get',
+  })
+}
+
 export function getUserListByDeptId(query) {
   return request({
     url: '/company/companyUser/getUserListByDeptId',
@@ -131,4 +138,4 @@ export function getCompanyListByCorpId(id) {
     url: '/company/companyUser/getCompanyList/'+id,
     method: 'get'
   })
-}
+}

+ 10 - 2
src/views/his/company/index.vue

@@ -201,6 +201,7 @@
             type="text"
             icon="el-icon-edit"
             @click="handleRedRecharge(scope.row)"
+            v-if="hstInfo !== 'hst'"
             v-hasPermi="['his:company:redRecharge']"
           >红包充值
           </el-button>
@@ -210,6 +211,7 @@
             icon="el-icon-edit"
             @click="handleRedDeduct(scope.row)"
             v-hasPermi="['his:company:redDeduct']"
+            v-if="hstInfo !== 'hst'"
           >红包扣款
           </el-button>
           <el-button
@@ -218,19 +220,21 @@
             icon="el-icon-edit"
             @click="handleRevenue(scope.row)"
             v-hasPermi="['company:company:revenue']"
+            v-if="hstInfo !== 'hst'"
           >分账配置</el-button>
 
           <el-button
-            v-if="!scope.row.accountId"
+            v-if="!scope.row.accountId && hstInfo !== 'hst'"
             size="mini"
             type="text"
             icon="el-icon-link"
             @click="handleBindXsy(scope.row)"
+            
           >绑定销售易
           </el-button>
 
           <el-button
-            v-if="scope.row.accountId"
+            v-if="scope.row.accountId && hstInfo !== 'hst'"
             size="mini"
             type="text"
             icon="el-icon-close"
@@ -245,6 +249,7 @@
             icon="el-icon-edit"
             @click="handleRedPacket(scope.row)"
             v-hasPermi="['company:company:redPacket']"
+            v-if="hstInfo !== 'hst'"
           >绑定红包商户</el-button>
 
           <el-button
@@ -253,6 +258,7 @@
             icon="el-icon-edit"
             @click="handleAppRedPacket(scope.row)"
             v-hasPermi="['company:company:AppRedPacket']"
+            v-if="hstInfo !== 'hst'"
           >绑定app提现商户</el-button>
 
           <el-button
@@ -260,6 +266,7 @@
             type="text"
             icon="el-icon-edit"
             @click="handleSidebarChange(scope.row)"
+            v-if="hstInfo !== 'hst'"
           >侧边栏封面设置</el-button>
 
 
@@ -964,6 +971,7 @@ export default {
   name: 'Company',
   data() {
     return {
+      hstInfo: process.env.VUE_APP_FS_USER_INFO,
       uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
       redPacketConfigOpen:{
         open: false,

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

@@ -104,7 +104,6 @@
           placeholder="选择结束时间"
           value-format="yyyy-MM-dd HH:mm:ss"
           size="small"
-          :disabled="!queryParams.startTimeS"
         ></el-date-picker>
       </el-form-item>
       <el-form-item label="上下架" prop="isShow">
@@ -293,6 +292,14 @@
             v-hasPermi="['live:live:edit', 'live:live:operation']"
             >修改</el-button
           >
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleSelect(scope.row)"
+            v-hasPermi="['live:live:edit', 'live:live:operation']"
+          >查看标签配置</el-button
+          >
           <el-button
             size="mini"
             type="text"
@@ -623,6 +630,69 @@
         <el-button @click="cancel">取 消</el-button>
       </div>
     </el-dialog>
+
+    <!-- 查看标签配置 -->
+    <el-dialog :title="selectTitle" :visible.sync="selectOpen" width="900px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="直播标签配置" prop="liveTagList">
+          <el-table
+            :data="form.liveTagList"
+            border
+            style="width: 100%; margin-bottom: 10px"
+          >
+            <el-table-column label="序号" type="index" width="60" />
+            <el-table-column label="公司" prop="tagName">
+              <template slot-scope="scope">
+                <el-select
+                  v-model="scope.row.companyId"
+                  placeholder="请选择公司"
+                  @change="selectCompanyChange(scope.row, scope.$index)"
+                >
+                  <el-option
+                    v-for="item in comapnyDropList"
+                    :key="item.companyId"
+                    :label="item.companyName"
+                    :value="item.companyId"
+                  />
+                </el-select>
+              </template>
+            </el-table-column>
+            <el-table-column label="所属企微主体" prop="corpId">
+              <template slot-scope="scope">
+                <el-select
+                  v-model="scope.row.corpId"
+                  placeholder="请选择企微主体"
+                >
+                  <el-option
+                    v-for="item in scope.row.corpDropList"
+                    :key="item.dictValue"
+                    :label="item.dictLabel"
+                    :value="item.dictValue"
+                  />
+                </el-select>
+              </template>
+            </el-table-column>
+            <el-table-column label="标记行为类型" prop="markType">
+              <template slot-scope="scope">
+                <el-select
+                  v-model="scope.row.markType"
+                  placeholder="请选择标记行为类型"
+                  clearable
+                  size="small"
+                >
+                  <el-option
+                    v-for="item in markTypeDropList"
+                    :key="item.dictValue"
+                    :label="item.dictLabel"
+                    :value="item.dictValue"
+                  />
+                </el-select>
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-form-item>
+      </el-form>
+    </el-dialog>
     <el-dialog title="提示" :visible.sync="rtmpUrlVisible" width="30%">
       <div>服务器地址:{{ serverName }}</div>
       <div>推流码:{{ livingCode }}</div>
@@ -747,8 +817,10 @@ export default {
       ],
       // 弹出层标题
       title: "",
+      selectTitle: "",
       // 是否显示弹出层
       open: false,
+      selectOpen: false,
       // 查询参数
       queryParams: {
         pageNum: 1,
@@ -1178,6 +1250,44 @@ export default {
         this.title = "修改直播间";
       });
     },
+
+    /**
+    * 查询标签设置
+    */
+    handleSelect(row){
+      this.reset();
+      const liveId = row.liveId || this.ids;
+      getLive(liveId).then((response) => {
+        this.form = response.data;
+        this.videoUrl = this.form.videoUrl;
+        if (this.form.duration) {
+          this.form.durationTime = this.secondsToTime(this.form.duration);
+        }
+        //初始化
+        if (
+          this.form.liveTagList == undefined ||
+          this.form.liveTagList == null
+        ) {
+          this.form.liveTagList = [];
+        }else{
+          for(let index =0 ;index<this.form.liveTagList.length;index ++){
+            this.form.liveTagList[index].markType = this.form.liveTagList[index].markType +"";
+            this.selectCompanyChange(this.form.liveTagList[index],index);
+          }
+        }
+        setTimeout(() => {
+          if (this.form.liveDesc == null) {
+            this.$refs.myeditor.setText("");
+          } else {
+            this.$refs.myeditor.setText(this.form.liveDesc);
+          }
+          this.form.videoUrl = row.videoUrl;
+        }, 1);
+        this.selectOpen = true;
+        this.selectTitle = "查看标签配置";
+      });
+    },
+
     /** 提交按钮 */
     submitForm() {
       if (this.form.liveId != null) {

+ 60 - 17
src/views/live/liveConfig/watchReward.vue

@@ -25,8 +25,9 @@
         <!-- 参与条件 -->
         <el-form-item label="参与条件" prop="participateCondition">
           <el-radio-group v-model="watchRewardForm.participateCondition">
-            <el-radio label="1">达到指定观看时长</el-radio>
-            <el-radio label="2">启用完课积分</el-radio>
+            <el-radio label="1">达到指定观看时长发积分</el-radio>
+            <el-radio label="2" v-if="this.projectFrom!=='颐安心'">启用完课积分</el-radio>
+            <el-radio label="3">启动直播完课奖励</el-radio>
           </el-radio-group>
         </el-form-item>
 
@@ -38,11 +39,11 @@
         </el-form-item>
 
         <!-- 完课率要求 -->
-        <el-form-item label="完课率要求" prop="completionRate" v-if="watchRewardForm.participateCondition === '2'">
-          <el-input-number 
-            v-model="watchRewardForm.completionRate" 
-            :min="1" 
-            :max="100" 
+        <el-form-item label="完课率要求" prop="completionRate" v-if="watchRewardForm.participateCondition === '2' || watchRewardForm.participateCondition === '3'">
+          <el-input-number
+            v-model="watchRewardForm.completionRate"
+            :min="1"
+            :max="100"
             :precision="0"
             placeholder="请输入完课率"
             style="width: 200px;"
@@ -50,14 +51,16 @@
           <span style="margin-left: 10px; color: #909399;">%(观看时长占直播总时长的比例)</span>
         </el-form-item>
 
+
+
         <!-- 连续完课积分配置 -->
         <el-form-item label="连续完课积分" prop="pointsConfig" v-if="watchRewardForm.participateCondition === '2'">
           <div style="display: flex; flex-direction: column; gap: 10px;">
             <div v-for="(point, index) in watchRewardForm.pointsConfig" :key="index" style="display: flex; align-items: center;">
               <span style="width: 80px;">第{{ index + 1 }}天:</span>
-              <el-input-number 
-                v-model="watchRewardForm.pointsConfig[index]" 
-                :min="0" 
+              <el-input-number
+                v-model="watchRewardForm.pointsConfig[index]"
+                :min="0"
                 :precision="0"
                 placeholder="请输入积分值"
                 style="width: 200px;"
@@ -84,6 +87,34 @@
 <!--          <el-input v-model="watchRewardForm.receivePrompt" placeholder="请输入领取提示语"></el-input>-->
 <!--        </el-form-item>-->
 
+
+        <div v-if="watchRewardForm.participateCondition === '3'">
+          <div class="section-title">直播红包设置</div>
+          <!-- 红包金额 -->
+          <el-form-item label="红包金额" prop="redPacketAmount" v-if="watchRewardForm.participateCondition === '3'">
+            <el-input-number v-model="watchRewardForm.redPacketAmount" :min="0.1" :max="10" :step="0.1"></el-input-number>
+          </el-form-item>
+          <!--   红包余额是否扣减开关      -->
+          <el-form-item label="红包余额是否扣减开关">
+            <el-radio-group v-model="watchRewardForm.isRedPackageBalanceDeduction">
+              <el-radio label="1">开</el-radio>
+              <el-radio label="2">关</el-radio>
+            </el-radio-group>
+          </el-form-item>
+        </div>
+
+        <div v-if="watchRewardForm.participateCondition === '3'">
+          <div class="section-title">录播积分设置</div>
+          <!-- 积分值 -->
+          <el-form-item label="积分值" prop="scoreAmount">
+            <el-input
+              v-model="watchRewardForm.scoreAmount"
+              placeholder="请输入积分值"                style="width: 300px;"
+            ></el-input>
+          </el-form-item>
+        </div>
+
+
         <!-- 红包设置(仅在达到指定观看时长时显示) -->
         <div v-if="watchRewardForm.participateCondition === '1'">
           <div class="section-title">红包设置</div>
@@ -202,6 +233,7 @@ import {addConfig, getConfig, updateConfig} from "@/api/live/liveQuestionLive";
 export default {
   data() {
     return {
+      projectFrom:process.env.VUE_APP_TITLE_INDEX,
       loading: true,
       liveId: null,
       watchRewardForm: {
@@ -221,6 +253,8 @@ export default {
         redPacketType: '1',
         // 红包金额
         redPacketAmount: '',
+        //是否开启红包扣减
+        isRedPackageBalanceDeduction: '2',
         // 红包发放数量
         redPacketCount: '',
         // 红包领取方式
@@ -248,7 +282,7 @@ export default {
           { required: true, message: '请选择参与条件', trigger: 'change'}
         ],
         watchDuration:[
-          { 
+          {
             validator: (rule, value, callback) => {
               if (this.watchRewardForm.participateCondition === '1') {
                 if (!value) {
@@ -259,12 +293,12 @@ export default {
               } else {
                 callback();
               }
-            }, 
+            },
             trigger: 'blur'
           }
         ],
         completionRate:[
-          { 
+          {
             validator: (rule, value, callback) => {
               if (this.watchRewardForm.participateCondition === '2') {
                 if (!value && value !== 0) {
@@ -277,12 +311,12 @@ export default {
               } else {
                 callback();
               }
-            }, 
+            },
             trigger: 'blur'
           }
         ],
         action:[
-          { 
+          {
             validator: (rule, value, callback) => {
               if (this.watchRewardForm.participateCondition === '1') {
                 if (!value) {
@@ -293,7 +327,7 @@ export default {
               } else {
                 callback();
               }
-            }, 
+            },
             trigger: 'change'
           }
         ],
@@ -306,6 +340,9 @@ export default {
         redPacketAmount:[
           { required: true, message: '请输入红包金额', trigger: 'blur'}
         ],
+        scoreAmount:[
+          { required: true, message: '请输入积分数量', trigger: 'blur'}
+        ],
         receiveMethod:[
           { required: true, message: '请选择红包领取方式', trigger: 'blur'}
         ],
@@ -360,7 +397,13 @@ export default {
     getLiveConfig(){
       getConfig(this.liveId).then(response => {
         if(response.code === 200 && response.data != null && response.data.length > 0){
-          this.watchRewardForm = JSON.parse(response.data)
+          const serverData = JSON.parse(response.data)
+          this.watchRewardForm = Object.assign({}, this.watchRewardForm, serverData)
+          if(this.watchRewardForm.isRedPackageBalanceDeduction == null){
+            this.watchRewardForm.isRedPackageBalanceDeduction = '1'
+          } else {
+            this.watchRewardForm.isRedPackageBalanceDeduction = String(this.watchRewardForm.isRedPackageBalanceDeduction)
+          }
         }
         this.loading = false
       })

+ 261 - 45
src/views/qw/conversationNew/ContentSearchTab.vue

@@ -1,7 +1,8 @@
 <template>
   <div class="content-search-tab">
+    <!-- 搜索表单 -->
     <div class="search-form">
-      <el-form :model="searchForm" ref="searchForm" label-width="100px">
+      <el-form :model="searchForm" inline>
         <el-form-item label="聊天内容" required>
           <el-input v-model="searchForm.queryWord" placeholder="请输入关键词(至少2个字符)" clearable />
         </el-form-item>
@@ -23,34 +24,106 @@
           />
         </el-form-item>
         <el-form-item>
-          <el-button type="primary" @click="doSearch" :loading="searching">查询</el-button>
+          <el-button type="primary" @click="handleSearch" :loading="searching">查询</el-button>
           <el-button @click="resetForm">重置</el-button>
         </el-form-item>
       </el-form>
     </div>
 
-    <div class="search-result">
-      <!-- 暂时显示空白,后续可展示搜索结果列表 -->
-      <div class="result-placeholder">
-        <i class="el-icon-search"></i>
-        <p>查询结果将在这里展示(后续版本完善)</p>
-      </div>
-      <!--
-        后续可加 ConversationPanel 展示会话详情,需传递员工ID和客户ID
-        但搜索接口返回的是消息列表,需要聚合按会话分组,暂不实现
-      -->
+    <!-- 搜索结果表格(无限滚动) -->
+    <div class="result-table-wrapper" ref="scrollContainer" @scroll="handleScroll">
+      <el-table
+        v-loading="tableLoading"
+        :data="messageList"
+        border
+        stripe
+        style="width: 100%"
+      >
+        <el-table-column prop="senderTypeName" label="发送人类型" width="100" />
+        <el-table-column prop="senderName" label="发送人" min-width="150" />
+        <el-table-column prop="receiverNames" label="接收人" min-width="200">
+          <template slot-scope="scope">
+            {{ scope.row.receiverNames.join('、') }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="sendTimeStr" label="发送时间" width="180" />
+        <el-table-column prop="msgTypeName" label="消息类型" width="100" />
+        <el-table-column label="聊天内容" min-width="200">
+          <template slot-scope="scope">
+            <span v-if="scope.row.msgtype === 1">{{ scope.row.contentPreview || '[文本消息]' }}</span>
+            <span v-else>{{ scope.row.msgTypeName }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="120">
+          <template slot-scope="scope">
+            <el-button type="text" @click="openContextDrawer(scope.row)">查看上下文</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div v-if="loadingMore" class="loading-more">加载更多...</div>
+      <div v-if="!hasMore && messageList.length > 0" class="no-more">没有更多了</div>
+      <div v-if="searchedNoData" class="no-more">未找到相关消息</div>
     </div>
+
+    <!-- 抽屉:会话上下文 -->
+    <el-drawer
+      :visible.sync="drawerVisible"
+      title="会话上下文"
+      direction="rtl"
+      size="80%"
+      destroy-on-close
+    >
+      <div class="drawer-container">
+        <ConversationPanelPure
+          :corpId="corpId"
+          :customerId="currentCustomerId"
+          :staffUserId="currentStaffUserId"
+          @logout="handleLogout"
+        />
+      </div>
+    </el-drawer>
   </div>
 </template>
 
 <script>
 import { qwSearchMsg } from "@/api/qw/companySession";
-import ConversationPanelPure from './ConversationPanelPure.vue';
-import Pagination from "@/components/Pagination";
+import ConversationPanelPure from "./ConversationPanelPure.vue";
+
+const MSG_TYPE_MAP = {
+  0: '暂不支持',
+  1: '文本',
+  2: '图片',
+  3: '表情',
+  4: '链接',
+  5: '小程序',
+  6: '语音',
+  7: '视频',
+  8: '文件',
+  9: '名片',
+  10: '转发消息',
+  11: '视频号',
+  12: '日程',
+  13: '红包',
+  14: '地理位置',
+  15: '快速会议',
+  16: '待办',
+  17: '投票',
+  18: '在线文档',
+  19: '图文消息',
+  20: '图文混合消息',
+  21: '音频存档',
+  22: '音视频通话',
+  23: '微盘文件',
+  24: '同意会话存档',
+  25: '拒绝会话存档',
+  26: '群接龙',
+  27: 'markdown',
+  28: '笔记'
+};
 
 export default {
   name: "ContentSearchTab",
-  components: { ConversationPanelPure, Pagination },
+  components: { ConversationPanelPure },
   props: {
     corpId: { type: String, required: true },
   },
@@ -64,15 +137,25 @@ export default {
       },
       dateRange: [],
       searching: false,
+      tableLoading: false,
+      loadingMore: false,
+      messageList: [],
+      cursor: null,
+      hasMore: false,
+      searchedNoData: false,
+      drawerVisible: false,
+      currentStaffUserId: null,
+      currentCustomerId: null,
       pickerOptions: {
         disabledDate(time) {
-          // 只能选择当前日期到30天前
+          // 只能选择最近31天内的日期
           const today = new Date();
           today.setHours(0, 0, 0, 0);
-          const thirtyDaysAgo = new Date();
-          thirtyDaysAgo.setDate(today.getDate() - 30);
-          thirtyDaysAgo.setHours(0, 0, 0, 0);
-          return time.getTime() > today.getTime() || time.getTime() < thirtyDaysAgo.getTime();
+          const thirtyOneDaysAgo = new Date();
+          thirtyOneDaysAgo.setDate(today.getDate() - 31);
+          thirtyOneDaysAgo.setHours(0, 0, 0, 0);
+          // 禁止选择今天之后和31天前的日期
+          return time.getTime() > today.getTime() || time.getTime() < thirtyOneDaysAgo.getTime();
         },
       },
     };
@@ -80,16 +163,13 @@ export default {
   watch: {
     dateRange(val) {
       if (val && val.length === 2) {
-        this.searchForm.startTime = val[0];
-        this.searchForm.endTime = val[1];
-      } else {
         this.searchForm.startTime = null;
         this.searchForm.endTime = null;
       }
     },
   },
   methods: {
-    async doSearch() {
+    async handleSearch() {
       if (!this.searchForm.queryWord || this.searchForm.queryWord.length < 2) {
         this.$message.warning("聊天内容关键词至少2个字符");
         return;
@@ -98,31 +178,125 @@ export default {
         this.$message.warning("请先选择企微主体");
         return;
       }
-      this.searching = true;
+
+      // 计算当前时间和31天前的时间戳(秒)
+      const nowSec = Math.floor(Date.now() / 1000);
+      const thirtyOneDaysAgoSec = nowSec - 31 * 24 * 3600;
+
+      let startTime = null;
+      let endTime = null;
+
+      if (this.dateRange && this.dateRange.length === 2) {
+        // 用户选择了日期范围
+        startTime = Math.floor(this.dateRange[0] / 1000);
+        let rawEndTime = Math.floor(this.dateRange[1] / 1000);
+
+        // 强制 end_time 不能晚于当前时间
+        if (rawEndTime > nowSec) {
+          this.$message.warning("结束时间不能晚于当前时间,已自动调整");
+          rawEndTime = nowSec;
+        }
+        endTime = rawEndTime;
+
+        // 确保 start_time < end_time
+        if (startTime >= endTime) {
+          this.$message.error("开始时间必须早于结束时间,请重新选择");
+          return;
+        }
+      } else {
+        // 用户未选日期范围:默认查询31天前到当前时间
+        startTime = thirtyOneDaysAgoSec;
+        endTime = nowSec;
+        this.$message.info("未选择时间范围,默认查询最近31天内的消息");
+      }
+
+      // 保存到 searchForm 中,供后续滚动加载使用
+      this.searchForm.startTime = startTime;
+      this.searchForm.endTime = endTime;
+
+      // 重置状态
+      this.messageList = [];
+      this.cursor = null;
+      this.hasMore = false;
+      this.searchedNoData = false;
+      this.tableLoading = true;
+      await this.loadMoreMessages(true);
+      this.tableLoading = false;
+    },
+
+    async loadMoreMessages(reset = false) {
+      if (this.loadingMore) return;
+      if (!reset && !this.hasMore) return;
+      this.loadingMore = true;
       try {
         const params = {
           corpId: this.corpId,
           queryWord: this.searchForm.queryWord,
           chatType: this.searchForm.chatType || undefined,
-          startTime: this.searchForm.startTime ? Math.floor(this.searchForm.startTime / 1000) : undefined,
-          endTime: this.searchForm.endTime ? Math.floor(this.searchForm.endTime / 1000) : undefined,
+          startTime: this.searchForm.startTime,
+          endTime: this.searchForm.endTime,
           limit: 50,
+          cursor: reset ? null : this.cursor,
         };
-        // 仅调用接口验证,暂不处理返回结果
         const res = await qwSearchMsg(params);
-        if (res.code === 200) {
-          this.$message.success(`搜索完成,共 ${res.data?.data?.length || 0} 条消息`);
-          // 后续可在此处理结果展示,目前仅为演示
+        if (res.code === 200 && res.data) {
+          const newMessages = res.data.data || [];
+          if (reset && newMessages.length === 0) {
+            this.searchedNoData = true;
+          } else {
+            this.searchedNoData = false;
+          }
+          const enriched = this.enrichMessages(newMessages);
+          if (reset) {
+            this.messageList = enriched;
+          } else {
+            this.messageList = this.messageList.concat(enriched);
+          }
+          this.cursor = res.data.nextCursor || null;
+          this.hasMore = res.data.hasMore === 1;
         } else {
           this.$message.error(res.msg || "搜索失败");
+          if (reset) this.messageList = [];
         }
       } catch (e) {
         console.error("搜索异常", e);
         this.$message.error("搜索异常");
+        if (reset) this.messageList = [];
       } finally {
-        this.searching = false;
+        this.loadingMore = false;
       }
     },
+
+    enrichMessages(messages) {
+      return messages.map(msg => {
+        let senderTypeName = '';
+        if (msg.sender.type === 1) senderTypeName = '员工';
+        else if (msg.sender.type === 2) senderTypeName = '客户';
+        else if (msg.sender.type === 3) senderTypeName = '机器人';
+        else senderTypeName = '未知';
+
+        const senderName = msg.sender.id;
+        const receiverNames = (msg.receiver_list || []).map(r => {
+          if (r.type === 1) return `员工:${r.id}`;
+          if (r.type === 2) return `客户:${r.id}`;
+          return r.id;
+        });
+        const msgTypeName = MSG_TYPE_MAP[msg.msgtype] || '未知类型';
+        let contentPreview = '';
+        if (msg.msgtype === 1) {
+          contentPreview = '[文本消息,内容需后端解密]';
+        }
+        return {
+          ...msg,
+          senderTypeName,
+          senderName,
+          receiverNames,
+          msgTypeName,
+          contentPreview,
+        };
+      });
+    },
+
     resetForm() {
       this.searchForm = {
         queryWord: "",
@@ -131,6 +305,44 @@ export default {
         endTime: null,
       };
       this.dateRange = [];
+      this.messageList = [];
+      this.cursor = null;
+      this.hasMore = false;
+      this.searchedNoData = false;
+    },
+
+    handleScroll(e) {
+      const container = e.target;
+      if (container.scrollHeight - container.scrollTop - container.clientHeight < 10) {
+        if (this.hasMore && !this.loadingMore && !this.tableLoading) {
+          this.loadMoreMessages();
+        }
+      }
+    },
+
+    openContextDrawer(row) {
+      if (row.chatid) {
+        this.$message.info("暂不支持群聊上下文查看");
+        return;
+      }
+      let staffId = null, customerId = null;
+      if (row.sender.type === 1) staffId = row.sender.id;
+      if (row.sender.type === 2) customerId = row.sender.id;
+      for (let recv of (row.receiver_list || [])) {
+        if (recv.type === 1 && !staffId) staffId = recv.id;
+        if (recv.type === 2 && !customerId) customerId = recv.id;
+      }
+      if (staffId && customerId) {
+        this.currentStaffUserId = staffId;
+        this.currentCustomerId = customerId;
+        this.drawerVisible = true;
+      } else {
+        this.$message.warning("无法确定会话双方");
+      }
+    },
+
+    handleLogout() {
+      this.$message.warning("会话登录已失效,请刷新页面重试");
     },
   },
 };
@@ -139,29 +351,33 @@ export default {
 <style scoped>
 .content-search-tab {
   height: 100%;
+  display: flex;
+  flex-direction: column;
 }
+
 .search-form {
   background: #fff;
   padding: 16px;
   border-radius: 4px;
   margin-bottom: 16px;
 }
-.search-result {
+
+.result-table-wrapper {
+  flex: 1;
+  overflow-y: auto;
   background: #fff;
   border-radius: 4px;
-  padding: 24px;
-  min-height: 400px;
+  padding: 16px;
 }
-.result-placeholder {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
-  color: #c0c4cc;
-  padding: 60px 0;
+
+.loading-more, .no-more {
+  text-align: center;
+  padding: 12px;
+  color: #999;
+  font-size: 14px;
 }
-.result-placeholder i {
-  font-size: 64px;
-  margin-bottom: 16px;
+
+.drawer-container {
+  height: 100%;
 }
 </style>

+ 71 - 127
src/views/qw/conversationNew/EmployeeQueryTab.vue

@@ -1,35 +1,15 @@
 <template>
   <div class="employee-query-tab">
-    <!-- 公司选择 -->
+    <!-- 查询表单 -->
     <div class="query-form">
       <el-form :model="queryParams" inline>
-        <el-form-item label="所属公司" required>
-          <el-select
-            v-model="selectedCompanyId"
-            placeholder="请选择公司"
-            clearable
-            filterable
-            @change="handleCompanyChange"
-            :loading="companyLoading"
-          >
-            <el-option
-              v-for="company in companyList"
-              :key="company.companyId"
-              :label="company.companyName"
-              :value="company.companyId"
-            />
-          </el-select>
-        </el-form-item>
         <el-form-item label="员工">
           <el-select
             v-model="queryParams.userId"
             filterable
-            remote
-            reserve-keyword
-            placeholder="请输入员工名称"
-            :remote-method="remoteSearchStaff"
-            :loading="staffLoading"
             clearable
+            placeholder="请选择员工"
+            :loading="staffOptionsLoading"
           >
             <el-option
               v-for="item in staffOptions"
@@ -112,7 +92,7 @@
         </div>
         <div class="conversation-panel">
           <ConversationPanelPure
-            :corpId="corp && corp.corpId"
+            :corpId="currentStaff && currentStaff.corpId"
             :customerId="selectedCustomerId"
             :customerAvatar="selectedCustomerAvatar"
             :staffUserId="currentStaff && currentStaff.qwUserId"
@@ -125,27 +105,22 @@
 </template>
 
 <script>
-import { getAllUserlist } from "@/api/company/companyUser";
+import { getAllCompanyUserListNoParams } from "@/api/company/companyUser";
 import { selectQwUserListByCondition } from "@/api/qw/qwUser";
 import { listExternalContact } from "@/api/qw/externalContact";
-import { queryCompanyListByCompanyIds } from "@/api/company/company";
 import ConversationPanelPure from "./ConversationPanelPure.vue";
 import Pagination from "@/components/Pagination";
 
 export default {
   name: "EmployeeQueryTab",
   components: { ConversationPanelPure, Pagination },
-  props: {
-    corp: { type: Object, default: null } // { corpId, companyIds, corpName }
-  },
   data() {
     return {
-      // 公司相关
-      companyList: [],
-      selectedCompanyId: null,
-      companyLoading: false,
+      // 员工下拉选项(全量,用于查询条件)
+      staffOptions: [],
+      staffOptionsLoading: false,
 
-      // 员工查询参数(分页)
+      // 员工查询参数(后端分页)
       queryParams: {
         userId: null,
         qwUserName: "",
@@ -155,8 +130,6 @@ export default {
       staffList: [],        // 当前页数据
       total: 0,             // 总记录数
       tableLoading: false,
-      staffOptions: [],     // 下拉框选项(远程搜索用)
-      staffLoading: false,
 
       // 抽屉相关
       drawerVisible: false,
@@ -171,114 +144,53 @@ export default {
       selectedCustomerAvatar: ""
     };
   },
-  watch: {
-    corp: {
-      immediate: true,
-      handler(newCorp) {
-        if (newCorp && newCorp.companyIds) {
-          this.loadCompanyList(newCorp.companyIds);
-        } else {
-          this.companyList = [];
-          this.selectedCompanyId = null;
-          this.clearStaffData();
-        }
-      }
-    }
-  },
   computed: {
     filteredCustomerList() {
       if (!this.customerKeyword) return this.customerList;
       return this.customerList.filter(c => c.name && c.name.includes(this.customerKeyword));
     }
   },
+  mounted() {
+    this.loadStaffOptions();
+    this.getStaffList();
+  },
   methods: {
-    // 根据 companyIds 加载公司列表
-    async loadCompanyList(companyIdsStr) {
-      if (!companyIdsStr) return;
-      const ids = companyIdsStr.split(',').map(Number).filter(id => !isNaN(id));
-      if (ids.length === 0) return;
-      this.companyLoading = true;
+    // 加载所有员工用于下拉选项(无公司限制)
+    async loadStaffOptions() {
+      this.staffOptionsLoading = true;
       try {
-        const res = await queryCompanyListByCompanyIds(ids);
+        const res = await getAllCompanyUserListNoParams();
+        let data = [];
         if (res.code === 200) {
-          this.companyList = res.companyList || [];
-          if (this.companyList.length > 0) {
-            this.selectedCompanyId = this.companyList[0].companyId;
-            await this.onCompanyChanged();
-          }
-        } else {
-          this.companyList = [];
+          data = res.data || [];
+        } else if (Array.isArray(res)) {
+          data = res;
+        } else if (res.data && Array.isArray(res.data)) {
+          data = res.data;
         }
-      } catch (e) {
-        console.error("加载公司列表失败", e);
-      } finally {
-        this.companyLoading = false;
-      }
-    },
-
-    // 切换公司时调用
-    async handleCompanyChange(companyId) {
-      if (!companyId) {
-        this.clearStaffData();
-        return;
-      }
-      this.queryParams = { userId: null, qwUserName: "", pageNum: 1, pageSize: 10 };
-      await this.onCompanyChanged();
-    },
-
-    // 公司变更后的核心处理:重新加载员工下拉选项,并刷新列表第一页
-    async onCompanyChanged() {
-      if (!this.selectedCompanyId) return;
-      await this.loadStaffOptions();
-      await this.getStaffList();
-    },
 
-    // 加载全部员工(用于下拉选项)
-    async loadStaffOptions() {
-      if (!this.selectedCompanyId) return;
-      this.staffLoading = true;
-      try {
-        const res = await getAllUserlist({ companyId: this.selectedCompanyId });
-        const data = res.data || [];
         this.staffOptions = data.map(item => ({
           userId: item.userId,
           nickName: item.nickName,
           qwUserName: item.qwUserName,
-          qwUserId: item.qwUserId
+          qwUserId: item.id,
+          departmentName: item.departmentName,
+          corpId: item.corpId
         }));
       } catch (e) {
         console.error("加载员工下拉选项失败", e);
         this.staffOptions = [];
       } finally {
-        this.staffLoading = false;
-      }
-    },
-
-    // 远程搜索员工(前端过滤)
-    async remoteSearchStaff(query) {
-      if (!this.selectedCompanyId) return;
-      if (!query) {
-        await this.loadStaffOptions();
-        return;
+        this.staffOptionsLoading = false;
       }
-      const filtered = this.staffOptions.filter(item =>
-        (item.nickName && item.nickName.includes(query)) ||
-        (item.qwUserName && item.qwUserName.includes(query))
-      );
-      this.staffOptions = filtered;
     },
 
     // 获取员工列表(后端分页)
     async getStaffList() {
-      if (!this.selectedCompanyId) {
-        this.staffList = [];
-        this.total = 0;
-        return;
-      }
       this.tableLoading = true;
       try {
+        // 构建请求参数,不传 companyId 让后端返回所有公司的员工(需后端支持)
         const params = {
-          companyId: this.selectedCompanyId,
           userId: this.queryParams.userId,
           qwUserName: this.queryParams.qwUserName,
           pageNum: this.queryParams.pageNum,
@@ -301,11 +213,13 @@ export default {
       }
     },
 
+    // 查询按钮
     handleQuery() {
       this.queryParams.pageNum = 1;
       this.getStaffList();
     },
 
+    // 重置查询条件
     resetQuery() {
       this.queryParams = {
         userId: null,
@@ -314,16 +228,9 @@ export default {
         pageSize: 10
       };
       this.getStaffList();
-      this.loadStaffOptions();
-    },
-
-    clearStaffData() {
-      this.staffOptions = [];
-      this.staffList = [];
-      this.total = 0;
-      this.queryParams = {userId: null, qwUserName: "", pageNum: 1, pageSize: 10};
     },
 
+    // 打开抽屉,加载客户列表
     openDrawer(row) {
       this.currentStaff = row;
       this.drawerVisible = true;
@@ -333,6 +240,7 @@ export default {
       this.loadCustomerList();
     },
 
+    // 加载客户列表(使用员工自身的corpId)
     async loadCustomerList() {
       if (!this.currentStaff) return;
       this.customerLoading = true;
@@ -340,8 +248,8 @@ export default {
         const params = {
           pageNum: this.customerPageNum,
           pageSize: this.customerPageSize,
-          qwUserId: this.currentStaff.id,
-          corpId: this.corp && this.corp.corpId
+          qwUserId: this.currentStaff.it,
+          corpId: this.currentStaff.corpId
         };
         const res = await listExternalContact(params);
         if (res.rows) {
@@ -353,16 +261,20 @@ export default {
         }
       } catch (e) {
         console.error("获取客户列表失败", e);
+        this.customerList = [];
+        this.customerTotal = 0;
       } finally {
         this.customerLoading = false;
       }
     },
 
+    // 选中客户
     handleCustomerClick(row) {
       this.selectedCustomerId = row.externalUserId;
       this.selectedCustomerAvatar = row.avatar;
     },
 
+    // 会话登录失效处理
     handleLogout() {
       this.$message.warning("会话登录已失效,请刷新页面重试");
     }
@@ -371,5 +283,37 @@ export default {
 </script>
 
 <style scoped>
-/* 原有样式 */
+.employee-query-tab {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+}
+
+.query-form {
+  background: #fff;
+  padding: 16px;
+  margin-bottom: 16px;
+  border-radius: 4px;
+}
+
+.drawer-container {
+  display: flex;
+  height: 100%;
+  gap: 16px;
+}
+
+.customer-list {
+  width: 360px;
+  flex-shrink: 0;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+}
+
+.conversation-panel {
+  flex: 1;
+  background: #f5f6f7;
+  border-radius: 8px;
+  overflow: hidden;
+}
 </style>

+ 4 - 1
src/views/system/config/config.vue

@@ -3079,7 +3079,9 @@
           <el-form-item label="回调地址" prop="notifyUrl">
             <el-input v-model="form36.notifyUrl" placeholder="请输入回调地址"></el-input>
           </el-form-item>
-
+          <el-form-item label="直播红包回调地址" prop="LiveNotifyUrl">
+            <el-input v-model="form36.LiveNotifyUrl" placeholder="请输入直播红包回调地址"></el-input>
+          </el-form-item>
           <div class="footer">
             <el-button type="primary" @click="submitForm36">提 交</el-button>
           </div>
@@ -3982,6 +3984,7 @@ export default {
               isNew: '1',
               mchId: '',
               notifyUrl: '',
+              LiveNotifyUrl: '',
               mchKey: '',
               keyPath: '',
               apiV3Key: '',