EmployeeQueryTab.vue 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. <template>
  2. <div class="employee-query-tab">
  3. <!-- 查询表单 -->
  4. <div class="query-form">
  5. <el-form :model="queryParams" inline>
  6. <el-form-item label="企微昵称">
  7. <el-input v-model="queryParams.qwUserName" placeholder="请输入企微昵称" clearable />
  8. </el-form-item>
  9. <el-form-item label="员工">
  10. <el-select
  11. v-model="queryParams.userId"
  12. filterable
  13. clearable
  14. placeholder="请选择员工"
  15. :loading="staffOptionsLoading"
  16. >
  17. <el-option
  18. v-for="item in staffOptions"
  19. :key="item.userId"
  20. :label="item.nickName || item.qwUserName"
  21. :value="item.userId"
  22. />
  23. </el-select>
  24. </el-form-item>
  25. <el-form-item>
  26. <el-button type="primary" @click="handleQuery">查询</el-button>
  27. <el-button @click="resetQuery">重置</el-button>
  28. </el-form-item>
  29. </el-form>
  30. </div>
  31. <!-- 员工列表(后端分页) -->
  32. <el-table
  33. v-loading="tableLoading"
  34. :data="staffList"
  35. border
  36. stripe
  37. style="width: 100%"
  38. >
  39. <el-table-column prop="qwUserName" label="企微昵称" />
  40. <el-table-column prop="nickName" label="员工" />
  41. <el-table-column prop="departmentName" label="部门" />
  42. <el-table-column label="操作" width="100">
  43. <template slot-scope="scope">
  44. <el-button type="text" @click="openDrawer(scope.row)">会话详情</el-button>
  45. </template>
  46. </el-table-column>
  47. </el-table>
  48. <!-- 分页组件 -->
  49. <pagination
  50. v-show="total > 0"
  51. :total="total"
  52. :page.sync="queryParams.pageNum"
  53. :limit.sync="queryParams.pageSize"
  54. @pagination="getStaffList"
  55. ></pagination>
  56. <!-- 抽屉:客户列表 + 会话详情 -->
  57. <el-drawer
  58. :visible.sync="drawerVisible"
  59. :title="(currentStaff && (currentStaff.nickName || currentStaff.qwUserName) ? (currentStaff.nickName || currentStaff.qwUserName) : '') + ' 的客户会话'"
  60. direction="rtl"
  61. size="80%"
  62. destroy-on-close
  63. >
  64. <div class="drawer-container">
  65. <div class="customer-list">
  66. <el-table
  67. :data="filteredCustomerList"
  68. v-loading="customerLoading"
  69. height="100%"
  70. highlight-current-row
  71. @row-click="handleCustomerClick"
  72. >
  73. <el-table-column label="头像" width="60">
  74. <template slot-scope="scope">
  75. <img :src="scope.row.avatar" style="width:40px;height:40px;border-radius:50%;" />
  76. </template>
  77. </el-table-column>
  78. <el-table-column prop="name" label="客户名称" />
  79. </el-table>
  80. <pagination
  81. v-show="customerTotal > 0"
  82. :total="customerTotal"
  83. :page.sync="customerPageNum"
  84. :limit.sync="customerPageSize"
  85. @pagination="loadCustomerList"
  86. layout="prev, pager, next"
  87. ></pagination>
  88. </div>
  89. <div class="conversation-panel">
  90. <ConversationPanelPure
  91. :corpId="currentStaff && currentStaff.corpId"
  92. :customerId="selectedCustomerId"
  93. :staffUserId="currentStaffQwUserId"
  94. @logout="handleLogout"
  95. />
  96. </div>
  97. </div>
  98. </el-drawer>
  99. </div>
  100. </template>
  101. <script>
  102. import { getAllCompanyUserListNoParams } from "@/api/company/companyUser";
  103. import { selectQwUserListByCondition } from "@/api/qw/qwUser";
  104. import { listExternalContact } from "@/api/qw/externalContact";
  105. import ConversationPanelPure from "./ConversationPanelPure.vue";
  106. import Pagination from "@/components/Pagination";
  107. export default {
  108. name: "EmployeeQueryTab",
  109. components: { ConversationPanelPure, Pagination },
  110. props: {
  111. corp: {
  112. type: Object,
  113. default: null
  114. }
  115. },
  116. watch: {
  117. corp(newVal) {
  118. if (newVal && newVal.corpId) {
  119. this.queryParams.userId = null; // 清空已选员工
  120. this.queryParams.pageNum = 1;
  121. this.getStaffList();
  122. }
  123. }
  124. },
  125. data() {
  126. return {
  127. // 员工下拉选项(全量,用于查询条件)
  128. staffOptions: [],
  129. staffOptionsLoading: false,
  130. // 员工查询参数(后端分页)
  131. queryParams: {
  132. userId: null,
  133. qwUserName: "",
  134. pageNum: 1,
  135. pageSize: 10
  136. },
  137. staffList: [], // 当前页数据
  138. total: 0, // 总记录数
  139. tableLoading: false,
  140. // 抽屉相关
  141. drawerVisible: false,
  142. currentStaff: null,
  143. currentStaffQwUserId: '', // 企业微信官方员工ID,从客户列表响应中提取
  144. customerList: [],
  145. customerTotal: 0,
  146. customerPageNum: 1,
  147. customerPageSize: 10,
  148. customerLoading: false,
  149. customerKeyword: "",
  150. selectedCustomerId: null
  151. };
  152. },
  153. computed: {
  154. filteredCustomerList() {
  155. if (!this.customerKeyword) return this.customerList;
  156. return this.customerList.filter(c => c.name && c.name.includes(this.customerKeyword));
  157. }
  158. },
  159. mounted() {
  160. this.loadStaffOptions();
  161. this.getStaffList();
  162. },
  163. methods: {
  164. // 加载所有员工用于下拉选项(无公司限制)
  165. async loadStaffOptions() {
  166. this.staffOptionsLoading = true;
  167. try {
  168. const res = await getAllCompanyUserListNoParams();
  169. let data = [];
  170. if (res.code === 200) {
  171. data = res.data || [];
  172. } else if (Array.isArray(res)) {
  173. data = res;
  174. } else if (res.data && Array.isArray(res.data)) {
  175. data = res.data;
  176. }
  177. this.staffOptions = data.map(item => ({
  178. userId: item.userId,
  179. nickName: item.nickName,
  180. qwUserName: item.qwUserName,
  181. qwUserId: item.id,
  182. departmentName: item.departmentName,
  183. corpId: item.corpId
  184. }));
  185. } catch (e) {
  186. console.error("加载员工下拉选项失败", e);
  187. this.staffOptions = [];
  188. } finally {
  189. this.staffOptionsLoading = false;
  190. }
  191. },
  192. // 获取员工列表(后端分页)
  193. async getStaffList() {
  194. this.tableLoading = true;
  195. try {
  196. // 构建请求参数,不传 companyId 让后端返回所有公司的员工(需后端支持)
  197. const params = {
  198. userId: this.queryParams.userId,
  199. qwUserName: this.queryParams.qwUserName,
  200. corpId: this.corp ? this.corp.corpId : null,
  201. pageNum: this.queryParams.pageNum,
  202. pageSize: this.queryParams.pageSize
  203. };
  204. const res = await selectQwUserListByCondition(params);
  205. if (res.code === 200 && res.data) {
  206. this.staffList = res.data.list || [];
  207. this.total = res.data.total || 0;
  208. } else {
  209. this.staffList = [];
  210. this.total = 0;
  211. }
  212. } catch (e) {
  213. console.error("获取员工列表失败", e);
  214. this.staffList = [];
  215. this.total = 0;
  216. } finally {
  217. this.tableLoading = false;
  218. }
  219. },
  220. // 查询按钮
  221. handleQuery() {
  222. this.queryParams.pageNum = 1;
  223. this.getStaffList();
  224. },
  225. // 重置查询条件
  226. resetQuery() {
  227. this.queryParams = {
  228. userId: null,
  229. qwUserName: "",
  230. pageNum: 1,
  231. pageSize: 10
  232. };
  233. this.getStaffList();
  234. },
  235. // 打开抽屉,加载客户列表
  236. openDrawer(row) {
  237. this.currentStaff = { ...row, corpId: this.corp?.corpId };
  238. this.drawerVisible = true;
  239. this.selectedCustomerId = null;
  240. this.currentStaffQwUserId = '';
  241. this.customerPageNum = 1;
  242. this.loadCustomerList();
  243. },
  244. // 加载客户列表(使用员工自身的corpId)
  245. async loadCustomerList() {
  246. if (!this.currentStaff) return;
  247. this.customerLoading = true;
  248. try {
  249. const params = {
  250. pageNum: this.customerPageNum,
  251. pageSize: this.customerPageSize,
  252. qwUserId: this.currentStaff.id,
  253. corpId: this.corp?.corpId
  254. };
  255. const res = await listExternalContact(params);
  256. if (res.rows) {
  257. this.customerList = res.rows;
  258. this.customerTotal = res.total || 0;
  259. // 从客户列表提取企业微信官方员工ID(用于会话查询,非SCRM内部ID)
  260. if (!this.currentStaffQwUserId && res.rows.length > 0 && res.rows[0].qwUserId) {
  261. this.currentStaffQwUserId = res.rows[0].qwUserId;
  262. }
  263. } else {
  264. this.customerList = [];
  265. this.customerTotal = 0;
  266. }
  267. } catch (e) {
  268. console.error("获取客户列表失败", e);
  269. this.customerList = [];
  270. this.customerTotal = 0;
  271. } finally {
  272. this.customerLoading = false;
  273. }
  274. },
  275. // 选中客户
  276. handleCustomerClick(row) {
  277. this.selectedCustomerId = row.externalUserId;
  278. },
  279. // 会话登录失效处理
  280. handleLogout() {
  281. this.$message.warning("会话登录已失效,请刷新页面重试");
  282. }
  283. }
  284. };
  285. </script>
  286. <style scoped>
  287. .employee-query-tab {
  288. height: 100%;
  289. display: flex;
  290. flex-direction: column;
  291. }
  292. .query-form {
  293. background: #fff;
  294. padding: 16px;
  295. margin-bottom: 16px;
  296. border-radius: 4px;
  297. }
  298. .drawer-container {
  299. display: flex;
  300. height: 100%;
  301. gap: 16px;
  302. }
  303. .customer-list {
  304. width: 360px;
  305. flex-shrink: 0;
  306. display: flex;
  307. flex-direction: column;
  308. overflow: hidden;
  309. margin-bottom: 22px;
  310. margin-left: 20px;
  311. }
  312. .conversation-panel {
  313. flex: 1;
  314. background: #f5f6f7;
  315. border-radius: 8px;
  316. overflow: hidden;
  317. }
  318. </style>