index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  1. <template>
  2. <div>
  3. <Card :bordered="false" dis-hover class="ivu-mt">
  4. <Row type="flex" class="mb20">
  5. <Col span="24">
  6. <Button v-auth="['setting-store_service-add']" type="primary" icon="md-add" @click="add" class="mr10"
  7. >添加客服</Button
  8. >
  9. </Col>
  10. </Row>
  11. <Table
  12. :columns="columns1"
  13. :data="tableList"
  14. :loading="loading"
  15. highlight-row
  16. no-userFrom-text="暂无数据"
  17. no-filtered-userFrom-text="暂无筛选结果"
  18. >
  19. <template slot-scope="{ row, index }" slot="avatar">
  20. <div class="tabBox_img" v-viewer>
  21. <img v-lazy="row.avatar" />
  22. </div>
  23. </template>
  24. <template slot-scope="{ row, index }" slot="status">
  25. <i-switch
  26. v-model="row.status"
  27. :value="row.status"
  28. :true-value="1"
  29. :false-value="0"
  30. @on-change="onchangeIsShow(row)"
  31. size="large"
  32. >
  33. <span slot="open">开启</span>
  34. <span slot="close">关闭</span>
  35. </i-switch>
  36. </template>
  37. <template slot-scope="{ row, index }" slot="stop_time">
  38. <span> {{ row.stop_time | formatDate }}</span>
  39. </template>
  40. <template slot-scope="{ row, index }" slot="action">
  41. <a @click="edit(row)">编辑</a>
  42. <Divider type="vertical" />
  43. <a @click="del(row, '删除客服', index)">删除</a>
  44. <Divider type="vertical" v-if="row.status" />
  45. <a @click="goChat(row)" v-if="row.status">进入工作台</a>
  46. </template>
  47. </Table>
  48. <div class="acea-row row-right page">
  49. <Page :total="total" show-elevator show-total @on-change="pageChange" :page-size="tableFrom.limit" />
  50. </div>
  51. </Card>
  52. <!--添加客户-->
  53. <!--<Modal v-model="modals" scrollable closable title="添加客服" width="1000" @on-cancel="cancel">-->
  54. <!--<Form ref="formValidate" :model="formValidate" :label-width="labelWidth" :label-position="labelPosition" @submit.native.prevent>-->
  55. <!--<Row :gutter="24" type="flex">-->
  56. <!--<Col span="24" class="ivu-text-left">-->
  57. <!--<FormItem label="选择时间:">-->
  58. <!--<RadioGroup v-model="formValidate.data" type="button" @on-change="selectChange(formValidate.data)"-->
  59. <!--class="mr">-->
  60. <!--<Radio :label=item.val v-for="(item,i) in fromList.fromTxt" :key="i">{{item.text}}</Radio>-->
  61. <!--</RadioGroup>-->
  62. <!--<DatePicker @on-change="onchangeTime" :value="timeVal" format="yyyy/MM/dd" type="daterange"-->
  63. <!--placement="bottom-end" placeholder="请选择时间" style="width: 200px;"></DatePicker>-->
  64. <!--</FormItem>-->
  65. <!--</Col>-->
  66. <!--<Col span="12" class="ivu-text-left">-->
  67. <!--<FormItem label="用户名称:" >-->
  68. <!--<Input search enter-button placeholder="请输入用户名称" v-model="formValidate.nickname" style="width: 90%;" @on-search="userSearchs"></Input>-->
  69. <!--</FormItem>-->
  70. <!--</Col>-->
  71. <!--<Col span="12" class="ivu-text-left">-->
  72. <!--<FormItem label="用户类型:" >-->
  73. <!--<Select v-model="formValidate.type" style="width:90%;" @on-change="userSearchs">-->
  74. <!--<Option value="">全部用户</Option>-->
  75. <!--<Option value="wechat">公众号</Option>-->
  76. <!--<Option value="routine">小程序</Option>-->
  77. <!--</Select>-->
  78. <!--</FormItem>-->
  79. <!--</Col>-->
  80. <!--</Row>-->
  81. <!--</Form>-->
  82. <!--<Table :loading="loading2" highlight-row no-userFrom-text="暂无数据" max-height="400"-->
  83. <!--@on-selection-change="onSelectTab"-->
  84. <!--no-filtered-userFrom-text="暂无筛选结果" ref="selection" :columns="columns4" :data="tableList2">-->
  85. <!--<template slot-scope="{ row, index }" slot="headimgurl">-->
  86. <!--<viewer>-->
  87. <!--<div class="tabBox_img">-->
  88. <!--<img v-lazy="row.headimgurl">-->
  89. <!--</div>-->
  90. <!--</viewer>-->
  91. <!--</template>-->
  92. <!--<template slot-scope="{ row, index }" slot="user_type">-->
  93. <!--<span>{{ row.user_type | typeFilter }}</span>-->
  94. <!--</template>-->
  95. <!--<template slot-scope="{ row, index }" slot="sex">-->
  96. <!--<span v-show="row.sex ===1">男</span>-->
  97. <!--<span v-show="row.sex ===2">女</span>-->
  98. <!--<span v-show="row.sex ===0">保密</span>-->
  99. <!--</template>-->
  100. <!--<template slot-scope="{ row, index }" slot="country">-->
  101. <!--<span>{{row.country + row.province + row.city}}</span>-->
  102. <!--</template>-->
  103. <!--<template slot-scope="{ row, index }" slot="subscribe">-->
  104. <!--<span v-text="row.subscribe === 1?'关注':'未关注'"></span>-->
  105. <!--</template>-->
  106. <!--</Table>-->
  107. <!--<div class="acea-row row-right page">-->
  108. <!--<Page :total="total2" :current="formValidate.page" show-elevator show-total @on-change="pageChange2"-->
  109. <!--:page-size="formValidate.limit"/>-->
  110. <!--</div>-->
  111. <!--<div slot="footer">-->
  112. <!--<Button type="primary" @click="putRemark">提交</Button>-->
  113. <!--</div>-->
  114. <!--</Modal>-->
  115. <!--聊天记录-->
  116. <Modal v-model="modals3" footer-hide scrollable closable title="聊天记录" width="700">
  117. <div v-if="isChat" class="modelBox">
  118. <Table
  119. :loading="loading3"
  120. highlight-row
  121. no-userFrom-text="暂无数据"
  122. no-filtered-userFrom-text="暂无筛选结果"
  123. :columns="columns3"
  124. :data="tableList3"
  125. >
  126. <template slot-scope="{ row, index }" slot="headimgurl">
  127. <div class="tabBox_img" v-viewer>
  128. <img v-lazy="row.headimgurl" />
  129. </div>
  130. </template>
  131. <template slot-scope="{ row, index }" slot="action">
  132. <a @click="look(row)">查看对话</a>
  133. </template>
  134. </Table>
  135. <div class="acea-row row-right page">
  136. <Page :total="total3" show-elevator show-total @on-change="pageChange3" :page-size="formValidate3.limit" />
  137. </div>
  138. </div>
  139. <div v-if="!isChat">
  140. <Button type="primary" @click="isChat = true">返回聊天记录</Button>
  141. <Table
  142. :loading="loading5"
  143. highlight-row
  144. no-userFrom-text="暂无数据"
  145. class="mt20"
  146. no-filtered-userFrom-text="暂无筛选结果"
  147. :columns="columns5"
  148. :data="tableList5"
  149. >
  150. <template slot-scope="{ row, index }" slot="avatar">
  151. <div class="tabBox_img" v-viewer>
  152. <img v-lazy="row.avatar" />
  153. </div>
  154. </template>
  155. <template slot-scope="{ row, index }" slot="action">
  156. <a @click="look(row)">查看对话</a>
  157. </template>
  158. </Table>
  159. <div class="acea-row row-right page">
  160. <Page :total="total5" show-elevator show-total @on-change="pageChange5" :page-size="formValidate5.limit" />
  161. </div>
  162. </div>
  163. </Modal>
  164. </div>
  165. </template>
  166. <script>
  167. import { mapState } from 'vuex';
  168. import { setCookies } from '@/libs/util';
  169. import {
  170. kefuListApi,
  171. kefucreateApi,
  172. kefuaddApi,
  173. kefuAddApi,
  174. kefusetStatusApi,
  175. kefuEditApi,
  176. kefuRecordApi,
  177. kefuChatlistApi,
  178. kefuLogin,
  179. } from '@/api/setting';
  180. export default {
  181. name: 'index',
  182. filters: {
  183. typeFilter(status) {
  184. const statusMap = {
  185. wechat: '微信用户',
  186. routine: '小程序用户',
  187. };
  188. return statusMap[status];
  189. },
  190. },
  191. computed: {
  192. ...mapState('media', ['isMobile']),
  193. ...mapState('userLevel', ['categoryId']),
  194. labelWidth() {
  195. return this.isMobile ? undefined : 80;
  196. },
  197. labelPosition() {
  198. return this.isMobile ? 'top' : 'left';
  199. },
  200. },
  201. data() {
  202. return {
  203. isChat: true,
  204. formValidate3: {
  205. page: 1,
  206. limit: 15,
  207. },
  208. total3: 0,
  209. loading3: false,
  210. modals3: false,
  211. tableList3: [],
  212. columns3: [
  213. {
  214. title: '用户名称',
  215. key: 'nickname',
  216. width: 200,
  217. },
  218. {
  219. title: '客服头像',
  220. slot: 'headimgurl',
  221. },
  222. {
  223. title: '操作',
  224. slot: 'action',
  225. },
  226. ],
  227. formValidate5: {
  228. page: 1,
  229. limit: 15,
  230. uid: 0,
  231. to_uid: 0,
  232. id: 0,
  233. },
  234. total5: 0,
  235. loading5: false,
  236. tableList5: [],
  237. columns5: [
  238. {
  239. title: '用户名称',
  240. key: 'nickname',
  241. width: 200,
  242. },
  243. {
  244. title: '用户头像',
  245. slot: 'avatar',
  246. },
  247. {
  248. title: '发送消息',
  249. key: 'msn',
  250. width: 250,
  251. },
  252. {
  253. title: '发送时间',
  254. key: 'add_time',
  255. },
  256. ],
  257. FromData: null,
  258. formValidate: {
  259. page: 1,
  260. limit: 15,
  261. data: '',
  262. type: '',
  263. nickname: '',
  264. },
  265. tableList2: [],
  266. modals: false,
  267. total: 0,
  268. tableFrom: {
  269. page: 1,
  270. limit: 15,
  271. },
  272. timeVal: [],
  273. fromList: {
  274. title: '选择时间',
  275. custom: true,
  276. fromTxt: [
  277. { text: '全部', val: '' },
  278. { text: '今天', val: 'today' },
  279. { text: '昨天', val: 'yesterday' },
  280. { text: '最近7天', val: 'lately7' },
  281. { text: '最近30天', val: 'lately30' },
  282. { text: '本月', val: 'month' },
  283. { text: '本年', val: 'year' },
  284. ],
  285. },
  286. loading: false,
  287. tableList: [],
  288. columns1: [
  289. {
  290. title: 'ID',
  291. key: 'id',
  292. width: 80,
  293. },
  294. {
  295. title: '微信用户名称',
  296. key: 'nickname',
  297. minWidth: 120,
  298. },
  299. {
  300. title: '客服头像',
  301. slot: 'avatar',
  302. minWidth: 60,
  303. },
  304. {
  305. title: '客服名称',
  306. key: 'wx_name',
  307. minWidth: 120,
  308. },
  309. {
  310. title: '客服状态',
  311. slot: 'status',
  312. minWidth: 120,
  313. },
  314. {
  315. title: '添加时间',
  316. key: 'add_time',
  317. minWidth: 120,
  318. },
  319. {
  320. title: '操作',
  321. slot: 'action',
  322. fixed: 'right',
  323. minWidth: 150,
  324. },
  325. ],
  326. columns4: [
  327. {
  328. type: 'selection',
  329. width: 60,
  330. align: 'center',
  331. },
  332. {
  333. title: 'ID',
  334. key: 'uid',
  335. width: 80,
  336. },
  337. {
  338. title: '微信用户名称',
  339. key: 'nickname',
  340. minWidth: 160,
  341. },
  342. {
  343. title: '客服头像',
  344. slot: 'headimgurl',
  345. minWidth: 60,
  346. },
  347. {
  348. title: '用户类型',
  349. slot: 'user_type',
  350. width: 100,
  351. },
  352. {
  353. title: '性别',
  354. slot: 'sex',
  355. minWidth: 60,
  356. },
  357. {
  358. title: '地区',
  359. slot: 'country',
  360. minWidth: 120,
  361. },
  362. {
  363. title: '是否关注公众号',
  364. slot: 'subscribe',
  365. minWidth: 120,
  366. },
  367. ],
  368. loading2: false,
  369. total2: 0,
  370. addFrom: {
  371. uids: [],
  372. },
  373. selections: [],
  374. rows: {},
  375. rowRecord: {},
  376. };
  377. },
  378. created() {
  379. this.getList();
  380. },
  381. methods: {
  382. // 进入工作台
  383. goChat(item) {
  384. kefuLogin(item.id)
  385. .then((res) => {
  386. var url = '';
  387. if (res.data.token) {
  388. let expires = this.getExpiresTime(res.data.exp_time);
  389. setCookies('kefu_token', res.data.token, expires);
  390. setCookies('kefu_uuid', res.data.kefuInfo.uid, expires);
  391. setCookies('kefu_expires_time', res.data.exp_time, expires);
  392. setCookies('kefuInfo', res.data.kefuInfo, expires);
  393. if (this.$store.state.media.isMobile) {
  394. url = window.location.protocol + '//' + window.location.host + '/kefu/mobile_list';
  395. } else {
  396. url = window.location.protocol + '//' + window.location.host + '/kefu/pc_list';
  397. }
  398. window.open(url, '_blank');
  399. }
  400. })
  401. .catch((error) => {
  402. this.$Message.error(error.msg);
  403. });
  404. },
  405. getExpiresTime(expiresTime) {
  406. let nowTimeNum = Math.round(new Date() / 1000);
  407. let expiresTimeNum = expiresTime - nowTimeNum;
  408. return parseFloat(parseFloat(parseFloat(expiresTimeNum / 60) / 60) / 24);
  409. },
  410. cancel() {
  411. this.formValidate = {
  412. page: 1,
  413. limit: 10,
  414. data: '',
  415. type: '',
  416. nickname: '',
  417. };
  418. },
  419. handleReachBottom() {
  420. return new Promise((resolve) => {
  421. this.formValidate.page = this.formValidate.page + 1;
  422. setTimeout(() => {
  423. // this.loading2 = true;
  424. kefucreateApi(this.formValidate)
  425. .then(async (res) => {
  426. let data = res.data;
  427. // this.tableList2 = data.list;
  428. if (data.list.length > 0) {
  429. for (let i = 0; i < data.list.length; i++) {
  430. this.tableList2.push(data.list[i]);
  431. }
  432. }
  433. this.total2 = data.count;
  434. this.loading2 = false;
  435. })
  436. .catch((res) => {
  437. this.loading2 = false;
  438. this.$Message.error(res.msg);
  439. });
  440. resolve();
  441. }, 2000);
  442. });
  443. },
  444. // 查看对话
  445. look(row) {
  446. this.isChat = false;
  447. this.rowRecord = row;
  448. this.getChatlist();
  449. },
  450. // 查看对话列表
  451. getChatlist() {
  452. this.loading5 = true;
  453. this.formValidate5.uid = this.rows.uid;
  454. this.formValidate5.to_uid = this.rowRecord.uid;
  455. this.formValidate5.id = this.rows.id;
  456. kefuChatlistApi(this.formValidate5)
  457. .then(async (res) => {
  458. let data = res.data;
  459. this.tableList5 = data.list;
  460. this.total5 = data.count;
  461. this.loading5 = false;
  462. })
  463. .catch((res) => {
  464. this.loading5 = false;
  465. this.$Message.error(res.msg);
  466. });
  467. },
  468. pageChange5(index) {
  469. this.formValidate5.page = index;
  470. this.getChatlist();
  471. },
  472. // 修改成功
  473. submitFail() {
  474. this.getList();
  475. },
  476. // 聊天记录
  477. record(row) {
  478. this.rows = row;
  479. this.modals3 = true;
  480. this.isChat = true;
  481. this.getListRecord();
  482. },
  483. // 聊天记录列表
  484. getListRecord() {
  485. this.loading3 = true;
  486. kefuRecordApi(this.formValidate3, this.rows.id)
  487. .then(async (res) => {
  488. let data = res.data;
  489. this.tableList3 = data.list ? data.list : [];
  490. this.total3 = data.count;
  491. this.loading3 = false;
  492. })
  493. .catch((res) => {
  494. this.loading3 = false;
  495. this.$Message.error(res.msg);
  496. });
  497. },
  498. pageChange3(index) {
  499. this.formValidate3.page = index;
  500. this.getListRecord();
  501. },
  502. // 编辑
  503. edit(row) {
  504. this.$modalForm(kefuEditApi(row.id)).then(() => this.getList());
  505. },
  506. // 添加
  507. add() {
  508. // this.modals = true;
  509. // this.formValidate.data = '';
  510. // this.getListService();
  511. this.$modalForm(kefuaddApi()).then(() => this.getList());
  512. },
  513. // 全选
  514. onSelectTab(selection) {
  515. this.selections = selection;
  516. let data = [];
  517. this.selections.map((item) => {
  518. data.push(item.uid);
  519. });
  520. this.addFrom.uids = data;
  521. },
  522. // 具体日期
  523. onchangeTime(e) {
  524. this.timeVal = e;
  525. this.formValidate.data = this.timeVal.join('-');
  526. this.formValidate.page = 1;
  527. this.getListService();
  528. },
  529. // 选择时间
  530. selectChange(tab) {
  531. this.formValidate.data = tab;
  532. this.timeVal = [];
  533. this.formValidate.page = 1;
  534. this.getListService();
  535. },
  536. // 客服列表
  537. getListService() {
  538. this.loading2 = true(this.formValidate)
  539. .then(async (res) => {
  540. let data = res.data;
  541. this.tableList2 = data.list;
  542. this.total2 = data.count;
  543. this.tableList2.map((item) => {
  544. item._isChecked = false;
  545. });
  546. this.loading2 = false;
  547. })
  548. .catch((res) => {
  549. tkefucreateApihis.loading2 = false;
  550. this.$Message.error(res.msg);
  551. });
  552. },
  553. pageChange2(pageIndex) {
  554. this.formValidate.page = pageIndex;
  555. this.getListService();
  556. this.addFrom.uids = [];
  557. },
  558. // 搜索
  559. userSearchs() {
  560. this.formValidate.page = 1;
  561. this.getListService();
  562. },
  563. // 删除
  564. del(row, tit, num) {
  565. let delfromData = {
  566. title: tit,
  567. num: num,
  568. url: `app/wechat/kefu/${row.id}`,
  569. method: 'DELETE',
  570. ids: '',
  571. };
  572. this.$modalSure(delfromData)
  573. .then((res) => {
  574. this.$Message.success(res.msg);
  575. this.tableList.splice(num, 1);
  576. })
  577. .catch((res) => {
  578. this.$Message.error(res.msg);
  579. });
  580. },
  581. // 列表
  582. getList() {
  583. this.loading = true;
  584. kefuListApi(this.tableFrom)
  585. .then(async (res) => {
  586. let data = res.data;
  587. this.tableList = data.list;
  588. this.total = res.data.count;
  589. this.loading = false;
  590. })
  591. .catch((res) => {
  592. this.loading = false;
  593. this.$Message.error(res.msg);
  594. });
  595. },
  596. pageChange(index) {
  597. this.tableFrom.page = index;
  598. this.getList();
  599. },
  600. // 修改是否显示
  601. onchangeIsShow(row) {
  602. let data = {
  603. id: row.id,
  604. status: row.status,
  605. };
  606. kefusetStatusApi(data)
  607. .then(async (res) => {
  608. this.$Message.success(res.msg);
  609. this.getList();
  610. })
  611. .catch((res) => {
  612. this.$Message.error(res.msg);
  613. });
  614. },
  615. // 添加客服
  616. putRemark() {
  617. if (this.addFrom.uids.length === 0) {
  618. return this.$Message.warning('请选择要添加的客服');
  619. }
  620. kefuAddApi(this.addFrom)
  621. .then(async (res) => {
  622. this.$Message.success(res.msg);
  623. this.modals = false;
  624. this.getList();
  625. })
  626. .catch((res) => {
  627. this.loading = false;
  628. this.$Message.error(res.msg);
  629. });
  630. },
  631. },
  632. };
  633. </script>
  634. <style scoped lang="stylus">
  635. .tabBox_img
  636. width 36px
  637. height 36px
  638. border-radius:4px;
  639. cursor pointer
  640. img
  641. width 100%
  642. height 100%
  643. .modelBox
  644. >>>
  645. .ivu-table-header
  646. width 100% !important
  647. .trees-coadd
  648. width: 100%;
  649. height: 385px;
  650. .scollhide
  651. width: 100%;
  652. height: 100%;
  653. overflow-x: hidden;
  654. overflow-y: scroll;
  655. // margin-left: 18px;
  656. .scollhide::-webkit-scrollbar {
  657. display: none;
  658. }
  659. </style>