EmployeeQueryTab.vue 9.3 KB

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