ybCallLog.vue 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784
  1. <template>
  2. <div class="tels-callLog-container layout-padding">
  3. <div class="layout-padding-auto layout-padding-view pd20">
  4. <el-tabs v-model="state.queryParams.type" @tab-change="changeTba">
  5. <el-tab-pane name="0" label="全部" :disabled="state.loading"></el-tab-pane>
  6. <el-tab-pane name="1" label="呼入已接" :disabled="state.loading"></el-tab-pane>
  7. <el-tab-pane name="2" label="呼出已接" :disabled="state.loading"></el-tab-pane>
  8. <el-tab-pane name="3" label="未接" :disabled="state.loading"></el-tab-pane>
  9. <el-tab-pane name="4" label="智能应答" :disabled="state.loading"></el-tab-pane>
  10. <el-tab-pane name="5" label="呼入白名单" :disabled="state.loading"></el-tab-pane>
  11. <el-tab-pane name="6" label="呼入黑名单" :disabled="state.loading"></el-tab-pane>
  12. </el-tabs>
  13. <el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent inline>
  14. <el-form-item prop="CPN" label="主叫号码">
  15. <el-input v-model="state.queryParams.CPN" placeholder="主叫号码" clearable @keyup.enter="handleQuery" class="keyword-input" />
  16. </el-form-item>
  17. <el-form-item prop="CDPN" label="被叫号码">
  18. <el-input v-model="state.queryParams.CDPN" placeholder="被叫号码" clearable @keyup.enter="handleQuery" class="keyword-input" />
  19. </el-form-item>
  20. <el-form-item prop="IsSensitiveWord" v-if="['1', '2'].includes(state.queryParams.type)">
  21. <el-checkbox v-model="IsSensitiveWord" border @change="changeIsSensitiveWord">是否敏感通话</el-checkbox>
  22. </el-form-item>
  23. <el-form-item>
  24. <el-button type="primary" @click="handleQuery" :loading="state.loading"> <SvgIcon name="ele-Search" class="mr5" />查询 </el-button>
  25. <el-button @click="drawer = true" class="default-button" :loading="state.loading">
  26. <SvgIcon name="ele-Search" class="mr5" />更多查询</el-button
  27. >
  28. </el-form-item>
  29. </el-form>
  30. <vxe-toolbar
  31. ref="toolbarRef"
  32. :loading="state.loading"
  33. custom
  34. :refresh="{
  35. queryMethod: handleQuery,
  36. }"
  37. :tools="[{ toolRender: { name: 'exportCurrent' } }, { toolRender: { name: 'exportAll' } }]"
  38. >
  39. <template #buttons>
  40. <el-button
  41. type="primary"
  42. @click="onTransferOrder"
  43. :disabled="isChecked"
  44. :loading="state.loading"
  45. v-auth="'tels:callLog:recordTransfer'"
  46. v-if="['1', '2', '4'].includes(state.queryParams.type)"
  47. >录音转写</el-button
  48. >
  49. </template>
  50. </vxe-toolbar>
  51. <div style="overflow: hidden; width: 100%; height: 100%; flex: 1">
  52. <vxe-table
  53. border
  54. :loading="state.loading"
  55. :data="state.tableData"
  56. :sort-config="{ remote: true }"
  57. :column-config="{ resizable: true }"
  58. :row-config="{ isCurrent: true, isHover: true, height: 30,useKey: true }"
  59. ref="tableRef"
  60. height="auto"
  61. auto-resize
  62. show-overflow
  63. :print-config="{}"
  64. :scrollY="{ enabled: true, gt: 100 }"
  65. id="telsCallLog"
  66. showHeaderOverflow
  67. :custom-config="{
  68. storage: true,
  69. checkMethod({ column }) {
  70. switch (state.queryParams.type) {
  71. case '0': //全部
  72. return !['checkbox', 'transliterationStateText', 'sensitiveText'].includes(column.field);
  73. case '1': //呼入已接
  74. return !['onStateText', 'callDirectionText'].includes(column.field);
  75. case '2': //呼出已接
  76. return ![
  77. 'order.no',
  78. 'order.title',
  79. 'mobileAreaName',
  80. 'onStateText',
  81. 'callDirectionText',
  82. 'beginIvrTime',
  83. 'endIvrTime',
  84. 'beginQueueTime',
  85. 'endQueueTime',
  86. 'beginRingTime',
  87. 'endRingTimg',
  88. ].includes(column.field);
  89. case '3': //未接
  90. return ![
  91. 'checkbox',
  92. 'order.no',
  93. 'order.title',
  94. 'onStateText',
  95. 'transliterationStateText',
  96. 'sensitiveText',
  97. 'answeredTime',
  98. 'duration',
  99. 'endByText',
  100. ].includes(column.field);
  101. case '4': //智能应答
  102. return ![
  103. 'userName',
  104. 'onStateText',
  105. 'callDirectionText',
  106. 'beginQueueTime',
  107. 'endQueueTime',
  108. 'beginRingTime',
  109. 'endRingTimg',
  110. 'sensitiveText',
  111. 'telNo',
  112. ].includes(column.field);
  113. case '5': //呼入白名单
  114. return !['checkbox', 'order.no', 'order.title', 'callDirectionText', 'transliterationStateText', 'sensitiveText'].includes(
  115. column.field
  116. );
  117. case '6': //呼入黑名单
  118. return ![
  119. 'checkbox',
  120. 'order.no',
  121. 'order.title',
  122. 'userName',
  123. 'callDirectionText',
  124. 'transliterationStateText',
  125. 'sensitiveText',
  126. 'onStateText',
  127. 'beginIvrTime',
  128. 'endIvrTime',
  129. 'beginQueueTime',
  130. 'endQueueTime',
  131. 'beginRingTime',
  132. 'endRingTimg',
  133. 'answeredTime',
  134. ].includes(column.field);
  135. }
  136. },
  137. }"
  138. :checkbox-config="checkboxConfig"
  139. @checkbox-all="selectAllChangeEvent"
  140. @checkbox-change="selectChangeEvent"
  141. :params="{ exportMethod: callLogPagedExport, exportParams: requestParams }"
  142. >
  143. <vxe-column
  144. type="checkbox"
  145. width="50"
  146. align="center"
  147. field="checkbox"
  148. :visible="['1', '2', '4'].includes(state.queryParams.type)"
  149. ></vxe-column>
  150. <vxe-column field="cpn" title="主叫号码" min-width="120"></vxe-column>
  151. <vxe-column field="cdpn" title="被叫号码" min-width="120"></vxe-column>
  152. <vxe-column field="order.no" title="工单编码" width="140" :visible="['0', '1','4'].includes(state.queryParams.type)"></vxe-column>
  153. <vxe-column field="order.title" title="工单标题" width="200" :visible="['0', '1','4'].includes(state.queryParams.type)">
  154. <template #default="{ row }">
  155. <order-detail :order="row.order" @updateList="queryList">{{ row.order?.title }}</order-detail>
  156. </template>
  157. </vxe-column>
  158. <vxe-column field="telNo" title="响应分机" width="100" :visible="['0', '1', '2', '3', '5'].includes(state.queryParams.type)"></vxe-column>
  159. <vxe-column field="gateway" title="中继号码" width="100"></vxe-column>
  160. <vxe-column
  161. field="mobileAreaName"
  162. title="号码归属地"
  163. min-width="120"
  164. :visible="['0', '1', '3', '4', '5', '6'].includes(state.queryParams.type)"
  165. ></vxe-column>
  166. <vxe-column field="userName" title="话务员" width="120" :visible="['0', '1', '2', '3', '5'].includes(state.queryParams.type)"></vxe-column>
  167. <vxe-column
  168. field="duration"
  169. title="通话时间(秒)"
  170. width="110"
  171. :visible="['0', '1', '2', '4', '5', '6'].includes(state.queryParams.type)"
  172. ></vxe-column>
  173. <vxe-column field="onStateText" title="通话结果" width="100" :visible="['0', '5'].includes(state.queryParams.type)"></vxe-column>
  174. <vxe-column field="callDirectionText" title="电话方向" width="100" :visible="['0', '3'].includes(state.queryParams.type)"></vxe-column>
  175. <vxe-column
  176. field="endByText"
  177. title="挂机类型"
  178. width="100"
  179. :visible="['0', '1', '2', '4', '5', '6'].includes(state.queryParams.type)"
  180. ></vxe-column>
  181. <vxe-column
  182. field="transliterationStateText"
  183. title="录音转写状态"
  184. width="110"
  185. :visible="['1', '2', '4'].includes(state.queryParams.type)"
  186. ></vxe-column>
  187. <vxe-column field="sensitiveText" title="敏感词" width="110" :visible="['1', '2'].includes(state.queryParams.type)"></vxe-column>
  188. <vxe-column field="beginIvrTime" title="ivr开始时间" width="160" :visible="['0', '1', '3', '4', '5'].includes(state.queryParams.type)">
  189. <template #default="{ row }">
  190. {{ formatDate(row.beginIvrTime, 'YYYY-mm-dd HH:MM:SS') }}
  191. </template>
  192. </vxe-column>
  193. <vxe-column field="endIvrTime" title="ivr结束时间" width="160" :visible="['0', '1', '3', '4', '5'].includes(state.queryParams.type)">
  194. <template #default="{ row }">
  195. {{ formatDate(row.endIvrTime, 'YYYY-mm-dd HH:MM:SS') }}
  196. </template>
  197. </vxe-column>
  198. <vxe-column field="beginQueueTime" title="队列开始时间" width="160" :visible="['0', '1', '3', '5'].includes(state.queryParams.type)">
  199. <template #default="{ row }">
  200. {{ formatDate(row.beginQueueTime, 'YYYY-mm-dd HH:MM:SS') }}
  201. </template>
  202. </vxe-column>
  203. <vxe-column field="endQueueTime" title="队列结束时间" width="160" :visible="['0', '1', '3', '5'].includes(state.queryParams.type)">
  204. <template #default="{ row }">
  205. {{ formatDate(row.endQueueTime, 'YYYY-mm-dd HH:MM:SS') }}
  206. </template>
  207. </vxe-column>
  208. <vxe-column field="beginRingTime" title="开始振铃时间" width="160" :visible="['0', '1', '3', '5'].includes(state.queryParams.type)">
  209. <template #default="{ row }">
  210. {{ formatDate(row.beginRingTime, 'YYYY-mm-dd HH:MM:SS') }}
  211. </template>
  212. </vxe-column>
  213. <vxe-column field="endRingTimg" title="结束振铃时间" width="160" :visible="['0', '1', '3', '5'].includes(state.queryParams.type)">
  214. <template #default="{ row }">
  215. {{ formatDate(row.endRingTimg, 'YYYY-mm-dd HH:MM:SS') }}
  216. </template>
  217. </vxe-column>
  218. <vxe-column field="createdTime" title="开始时间" width="160">
  219. <template #default="{ row }">
  220. {{ formatDate(row.createdTime, 'YYYY-mm-dd HH:MM:SS') }}
  221. </template>
  222. </vxe-column>
  223. <vxe-column field="answeredTime" title="接通时间" width="160" :visible="['0', '1', '2', '4', '5'].includes(state.queryParams.type)">
  224. <template #default="{ row }">
  225. {{ formatDate(row.answeredTime, 'YYYY-mm-dd HH:MM:SS') }}
  226. </template>
  227. </vxe-column>
  228. <vxe-column field="overTime" title="挂断结束时间" width="160">
  229. <template #default="{ row }">
  230. {{ formatDate(row.overTime, 'YYYY-mm-dd HH:MM:SS') }}
  231. </template>
  232. </vxe-column>
  233. <vxe-column title="操作" fixed="right" width="160" align="center" v-if="['0', '2', '5'].includes(state.queryParams.type)">
  234. <template #default="{ row }">
  235. <el-button type="primary" @click="onPlaySoundRecording(row)" title="播放录音" link v-if="row.recordingAbsolutePath && row.otherAccept"
  236. >播放录音</el-button
  237. >
  238. <el-button link type="primary" @click="onDownload(row)" title="下载录音" v-if="row.recordingAbsolutePath && row.otherAccept">
  239. 下载录音
  240. </el-button>
  241. </template>
  242. </vxe-column>
  243. <vxe-column title="操作" fixed="right" width="310" align="center" v-if="['1'].includes(state.queryParams.type)">
  244. <template #default="{ row }">
  245. <el-button link type="primary" @click="onCreate(row)" title="创建失联工单" v-auth="'tels:callLog:connectOrder'" v-if="!row.externalId">
  246. 失联工单
  247. </el-button>
  248. <el-button link type="primary" @click="onConnect(row)" title="关联业务" v-auth="'tels:callLog:connect'" v-if="!row.externalId">
  249. 关联业务
  250. </el-button>
  251. <el-button type="primary" @click="onPlaySoundRecording(row)" title="播放录音" link v-if="row.recordingAbsolutePath && row.otherAccept"
  252. >播放录音</el-button
  253. >
  254. <el-button link type="primary" @click="onDownload(row)" title="下载录音" v-if="row.recordingAbsolutePath && row.otherAccept">
  255. 下载录音
  256. </el-button>
  257. </template>
  258. </vxe-column>
  259. <vxe-column title="操作" fixed="right" width="220" align="center" v-if="['4'].includes(state.queryParams.type)">
  260. <template #default="{ row }">
  261. <el-button type="primary" @click="onPlaySoundRecording(row)" title="播放录音" link v-if="row.recordingAbsolutePath && row.otherAccept"
  262. >播放录音</el-button
  263. >
  264. <el-button link type="primary" @click="onDownload(row)" title="下载录音" v-if="row.recordingAbsolutePath && row.otherAccept">
  265. 下载录音
  266. </el-button>
  267. <el-button
  268. link
  269. type="primary"
  270. @click="transferOrder(row)"
  271. title="转写工单"
  272. v-auth="'tels:callLog:transferOrder'"
  273. >
  274. 转工单
  275. </el-button>
  276. </template>
  277. </vxe-column>
  278. </vxe-table>
  279. </div>
  280. <pagination
  281. @pagination="queryList"
  282. :total="state.total"
  283. v-model:current-page="state.queryParams.PageIndex"
  284. v-model:page-size="state.queryParams.PageSize"
  285. :disabled="state.loading"
  286. />
  287. </div>
  288. <el-drawer v-model="drawer" title="更多查询" size="500px">
  289. <el-form :model="state.queryParams" ref="drawerRuleFormRef" @submit.native.prevent label-width="100px">
  290. <el-form-item prop="SensitiveWord" label="敏感词" v-show="['1', '2'].includes(state.queryParams.type)">
  291. <el-input v-model="state.queryParams.SensitiveWord" placeholder="敏感词" clearable @keyup.enter="handleQuery" />
  292. </el-form-item>
  293. <el-form-item prop="TelNo" label="响应分机" v-show="['0', '1', '2', '3', '5', '6'].includes(state.queryParams.type)">
  294. <el-input v-model="state.queryParams.TelNo" placeholder="响应分机" clearable @keyup.enter="handleQuery" />
  295. </el-form-item>
  296. <el-form-item prop="UserName" label="话务员名称" v-show="['0', '1', '2', '3', '5'].includes(state.queryParams.type)">
  297. <el-input v-model="state.queryParams.UserName" placeholder="话务员名称" clearable @keyup.enter="handleQuery" />
  298. </el-form-item>
  299. <el-form-item prop="gateway" label="中继号码" v-show="['4', '6'].includes(state.queryParams.type)">
  300. <el-input v-model="state.queryParams.gateway" placeholder="中继号码" clearable @keyup.enter="handleQuery" />
  301. </el-form-item>
  302. <el-form-item prop="CallDirection" label="电话方向" v-show="['0', '3'].includes(state.queryParams.type)">
  303. <el-select v-model="state.queryParams.CallDirection" placeholder="电话方向" clearable class="w100" @change="handleQuery">
  304. <el-option v-for="item in state.callDirection" :value="item.key" :key="item.key" :label="item.value" />
  305. </el-select>
  306. </el-form-item>
  307. <el-form-item prop="OnState" label="通话结果" v-show="['0'].includes(state.queryParams.type)">
  308. <el-select v-model="state.queryParams.OnState" placeholder="通话结果" clearable class="w100" @change="handleQuery">
  309. <el-option v-for="item in state.onState" :value="item.key" :key="item.key" :label="item.value" />
  310. </el-select>
  311. </el-form-item>
  312. <el-form-item prop="EndBy" label="挂机类型" v-show="['4'].includes(state.queryParams.type)">
  313. <el-select v-model="state.queryParams.EndBy" placeholder="挂机类型" clearable class="w100" @change="handleQuery">
  314. <el-option v-for="item in state.endByOptions" :value="item.key" :key="item.key" :label="item.value" />
  315. </el-select>
  316. </el-form-item>
  317. <el-form-item prop="beginIvrTime" v-show="['0', '1', '3', '4', '5'].includes(state.queryParams.type)" label="ivr开始">
  318. <el-date-picker
  319. v-model="state.queryParams.beginIvrTime"
  320. type="datetimerange"
  321. unlink-panels
  322. range-separator="至"
  323. start-placeholder="ivr开始开始时间"
  324. end-placeholder="ivr开始结束时间"
  325. :shortcuts="shortcuts"
  326. @change="handleQuery"
  327. value-format="YYYY-MM-DD[T]HH:mm:ss"
  328. :default-time="defaultTimeStartEnd"
  329. />
  330. </el-form-item>
  331. <el-form-item prop="endIvrTime" v-show="['0', '1', '3', '4', '5'].includes(state.queryParams.type)" label="ivr结束">
  332. <el-date-picker
  333. v-model="state.queryParams.endIvrTime"
  334. type="datetimerange"
  335. unlink-panels
  336. range-separator="至"
  337. start-placeholder="ivr结束开始时间"
  338. end-placeholder="ivr结束结束时间"
  339. :shortcuts="shortcuts"
  340. @change="handleQuery"
  341. value-format="YYYY-MM-DD[T]HH:mm:ss"
  342. :default-time="defaultTimeStartEnd"
  343. />
  344. </el-form-item>
  345. <el-form-item prop="beginQueueTime" v-show="['0', '1', '3', '5'].includes(state.queryParams.type)" label="队列开始">
  346. <el-date-picker
  347. v-model="state.queryParams.beginQueueTime"
  348. type="datetimerange"
  349. unlink-panels
  350. range-separator="至"
  351. start-placeholder="队列开始开始时间"
  352. end-placeholder="队列开始结束时间"
  353. :shortcuts="shortcuts"
  354. @change="handleQuery"
  355. value-format="YYYY-MM-DD[T]HH:mm:ss"
  356. :default-time="defaultTimeStartEnd"
  357. />
  358. </el-form-item>
  359. <el-form-item prop="endQueueTime" v-show="['0', '1', '3', '5'].includes(state.queryParams.type)" label="队列结束">
  360. <el-date-picker
  361. v-model="state.queryParams.endQueueTime"
  362. type="datetimerange"
  363. unlink-panels
  364. range-separator="至"
  365. start-placeholder="队列结束开始时间"
  366. end-placeholder="队列结束结束时间"
  367. :shortcuts="shortcuts"
  368. @change="handleQuery"
  369. value-format="YYYY-MM-DD[T]HH:mm:ss"
  370. :default-time="defaultTimeStartEnd"
  371. />
  372. </el-form-item>
  373. <el-form-item prop="beginRingTime" v-show="['0', '1', '3', '5'].includes(state.queryParams.type)" label="振铃开始">
  374. <el-date-picker
  375. v-model="state.queryParams.beginRingTime"
  376. type="datetimerange"
  377. unlink-panels
  378. range-separator="至"
  379. start-placeholder="振铃开始开始时间"
  380. end-placeholder="振铃开始结束时间"
  381. :shortcuts="shortcuts"
  382. @change="handleQuery"
  383. value-format="YYYY-MM-DD[T]HH:mm:ss"
  384. :default-time="defaultTimeStartEnd"
  385. />
  386. </el-form-item>
  387. <el-form-item prop="endRingTime" v-show="['0', '1', '3', '5'].includes(state.queryParams.type)" label="振铃结束">
  388. <el-date-picker
  389. v-model="state.queryParams.endRingTime"
  390. type="datetimerange"
  391. unlink-panels
  392. range-separator="至"
  393. start-placeholder="振铃结束开始时间"
  394. end-placeholder="振铃结束结束时间"
  395. :shortcuts="shortcuts"
  396. @change="handleQuery"
  397. value-format="YYYY-MM-DD[T]HH:mm:ss"
  398. :default-time="defaultTimeStartEnd"
  399. />
  400. </el-form-item>
  401. <el-form-item prop="callTime" label="开始时间">
  402. <el-date-picker
  403. v-model="state.queryParams.callTime"
  404. type="datetimerange"
  405. unlink-panels
  406. range-separator="至"
  407. start-placeholder="开始开始时间"
  408. end-placeholder="开始结束时间"
  409. :shortcuts="shortcuts"
  410. @change="handleQuery"
  411. value-format="YYYY-MM-DD[T]HH:mm:ss"
  412. :default-time="defaultTimeStartEnd"
  413. />
  414. </el-form-item>
  415. <el-form-item prop="answeredTime" v-show="['0', '1', '2', '4', '5'].includes(state.queryParams.type)" label="接通时间">
  416. <el-date-picker
  417. v-model="state.queryParams.answeredTime"
  418. type="datetimerange"
  419. unlink-panels
  420. range-separator="至"
  421. start-placeholder="接通开始时间"
  422. end-placeholder="接通结束时间"
  423. :shortcuts="shortcuts"
  424. @change="handleQuery"
  425. value-format="YYYY-MM-DD[T]HH:mm:ss"
  426. :default-time="defaultTimeStartEnd"
  427. />
  428. </el-form-item>
  429. <el-form-item prop="overTime" label="挂断时间">
  430. <el-date-picker
  431. v-model="state.queryParams.overTime"
  432. type="datetimerange"
  433. unlink-panels
  434. range-separator="至"
  435. start-placeholder="挂断开始时间"
  436. end-placeholder="挂断结束时间"
  437. :shortcuts="shortcuts"
  438. @change="handleQuery"
  439. value-format="YYYY-MM-DD[T]HH:mm:ss"
  440. :default-time="defaultTimeStartEnd"
  441. />
  442. </el-form-item>
  443. <el-form-item prop="OrderNo" label="工单编码" v-show="['0', '1'].includes(state.queryParams.type)">
  444. <el-input v-model="state.queryParams.OrderNo" placeholder="工单编码" clearable @keyup.enter="handleQuery" />
  445. </el-form-item>
  446. <el-form-item prop="Title" label="工单标题" v-show="['0', '1'].includes(state.queryParams.type)">
  447. <el-input v-model="state.queryParams.Title" placeholder="工单标题" clearable @keyup.enter="handleQuery" />
  448. </el-form-item>
  449. </el-form>
  450. <template #footer>
  451. <el-button type="primary" @click="handleQuery" :loading="state.loading"> <SvgIcon name="ele-Search" class="mr5" />查询 </el-button>
  452. <el-button @click="resetQuery(drawerRuleFormRef)" class="default-button"> <SvgIcon name="ele-Refresh" class="mr5" />重置 </el-button>
  453. </template>
  454. </el-drawer>
  455. <!-- 播放录音 -->
  456. <play-record ref="playRecordRef" />
  457. <!-- 业务关联 -->
  458. <connect-business ref="connectBusinessRef" @updateList="queryList" />
  459. </div>
  460. </template>
  461. <script lang="tsx" setup name="callLog">
  462. import { defineAsyncComponent, onMounted, reactive, ref, onActivated, onBeforeUnmount, computed } from 'vue';
  463. import type { FormInstance } from 'element-plus';
  464. import { ElMessage, ElMessageBox } from 'element-plus';
  465. import { debounce, downloadFileByStream } from '@/utils/tools';
  466. import { callBaseData, callLogPaged, callLogTranscription, callLogPagedExport } from '@/api/tels/callLog';
  467. import { formatDate } from '@/utils/formatTime';
  468. import { defaultTimeStartEnd, shortcuts } from '@/utils/constants';
  469. import { useRouter } from 'vue-router';
  470. import { fileDownload } from '@/api/public/file';
  471. import mittBus from '@/utils/mitt';
  472. import Other from '@/utils/other';
  473. import { Local } from '@/utils/storage';
  474. import { useThemeConfig } from '@/stores/themeConfig';
  475. import { storeToRefs } from 'pinia';
  476. // 引入组件
  477. const PlayRecord = defineAsyncComponent(() => import('@/components/PlayRecord/index.vue')); // 播放录音
  478. const ConnectBusiness = defineAsyncComponent(() => import('@/views/tels/callLog/components/Connect-business.vue')); // 关联工单还是回访
  479. const OrderDetail = defineAsyncComponent(() => import('@/components/OrderDetail/index.vue')); // 工单详情
  480. const pagination = defineAsyncComponent(() => import('@/components/ProTable/components/Pagination.vue')); // 分页
  481. // 定义变量内容
  482. const state = reactive<any>({
  483. queryParams: {
  484. PageIndex: 1, // 当前页
  485. PageSize: 20, // 每页条数
  486. type: '0',
  487. StaffNo: null, // 分机号
  488. CPN: null, // 主叫号码
  489. CDPN: null, // 被叫号码
  490. TelNo: null, // 响应分机
  491. CallDirection: null, // 呼叫类型
  492. UserName: null, // 话务员名称
  493. gateway: null, //中继号码
  494. OnState: null, // 结果
  495. IsAiAnswered: null, // 是否智能应答
  496. callTime: [], // 通话时间
  497. answeredTime: [], // 接通时间
  498. overTime: [], // 挂断时间段
  499. beginIvrTime: [], // ivr开始时间段
  500. endIvrTime: [], // ivr结束时间段
  501. beginQueueTime: [], // 队列开始时间段
  502. endQueueTime: [], // 队列结束时间段
  503. beginRingTime: [], // 振铃开始时间段
  504. endRingTime: [], // 振铃结束时间段
  505. CallTimeStart: null, // 通话开始时间
  506. CallTimeEnd: null, // 通话结束时间
  507. OverTimeStart: null, // 挂断开始时间
  508. OverTimeEnd: null, // 挂断结束时间
  509. AnsweredTimeStart: null, // 接通开始时间
  510. AnsweredTimeEnd: null, // 接通结束时间
  511. BeginIvrTimeStart: null, // ivr开始开始时间
  512. BeginIvrTimeEnd: null, // ivr开始结束时间
  513. EndIvrTimeStart: null, // ivr结束开始时间
  514. EndIvrTimeEnd: null, // ivr结束结束时间
  515. BeginQueueTimeStart: null, // 队列开始开始时间
  516. BeginQueueTimeEnd: null, // 队列开始结束时间
  517. EndQueueTimeStart: null, // 队列结束开始时间
  518. EndQueueTimeEnd: null, // 队列结束结束时间
  519. BeginRingTimeStart: null, // 振铃开始开始时间
  520. BeginRingTimeEnd: null, // 振铃开始结束时间
  521. EndRingTimeStart: null, // 振铃结束开始时间
  522. EndRingTimeEnd: null, // 振铃结束结束时间
  523. SensitiveWord: null, // 敏感词
  524. IsSensitiveWord: null, // 是否敏感通话
  525. },
  526. tableData: [], // 列表数据
  527. loading: false, // 加载
  528. total: 0, // 总条数
  529. callDirection: [],
  530. onState: [],
  531. endByOptions: [],
  532. });
  533. const ruleFormRef = ref<FormInstance>(); // 表单ref
  534. const changeTba = () => {
  535. ruleFormRef.value?.resetFields();
  536. handleQuery();
  537. };
  538. // 手动查询,将页码设置为1
  539. const handleQuery = () => {
  540. state.queryParams.PageIndex = 1;
  541. queryList();
  542. };
  543. /** 通话记录列表 */
  544. const requestParams = ref<EmptyObjectType>({});
  545. const queryList = async () => {
  546. state.loading = true;
  547. try {
  548. requestParams.value = Other.deepClone(state.queryParams);
  549. requestParams.value.CallTimeStart = state.queryParams.callTime === null ? null : state.queryParams.callTime[0];
  550. requestParams.value.CallTimeEnd = state.queryParams.callTime === null ? null : state.queryParams.callTime[1];
  551. Reflect.deleteProperty(requestParams.value, 'callTime'); // 通话开始和结束时间段
  552. requestParams.value.AnsweredTimeStart = state.queryParams.answeredTime === null ? null : state.queryParams.answeredTime[0];
  553. requestParams.value.AnsweredTimeEnd = state.queryParams.answeredTime === null ? null : state.queryParams.answeredTime[1];
  554. Reflect.deleteProperty(requestParams.value, 'answeredTime'); //接通开始和结束时间段
  555. requestParams.value.OverTimeStart = state.queryParams.overTime === null ? null : state.queryParams.overTime[0];
  556. requestParams.value.OverTimeEnd = state.queryParams.overTime === null ? null : state.queryParams.overTime[1];
  557. Reflect.deleteProperty(requestParams.value, 'overTime'); //挂断开始和结束时间段
  558. requestParams.value.BeginIvrTimeStart = state.queryParams.beginIvrTime === null ? null : state.queryParams.beginIvrTime[0];
  559. requestParams.value.BeginIvrTimeEnd = state.queryParams.beginIvrTime === null ? null : state.queryParams.beginIvrTime[1];
  560. Reflect.deleteProperty(requestParams.value, 'beginIvrTime'); //IVR开始时间段
  561. requestParams.value.EndIvrTimeStart = state.queryParams.endIvrTime === null ? null : state.queryParams.endIvrTime[0];
  562. requestParams.value.EndIvrTimeEnd = state.queryParams.endIvrTime === null ? null : state.queryParams.endIvrTime[1];
  563. Reflect.deleteProperty(requestParams.value, 'endIvrTime'); //IVR结束时间段
  564. requestParams.value.BeginQueueTimeStart = state.queryParams.beginQueueTime === null ? null : state.queryParams.beginQueueTime[0];
  565. requestParams.value.BeginQueueTimeEnd = state.queryParams.beginQueueTime === null ? null : state.queryParams.beginQueueTime[1];
  566. Reflect.deleteProperty(requestParams.value, 'beginQueueTime'); //队列开始时间段
  567. requestParams.value.EndQueueTimeStart = state.queryParams.endQueueTime === null ? null : state.queryParams.endQueueTime[0];
  568. requestParams.value.EndQueueTimeEnd = state.queryParams.endQueueTime === null ? null : state.queryParams.endQueueTime[1];
  569. Reflect.deleteProperty(requestParams.value, 'endQueueTime'); //队列结束时间段
  570. requestParams.value.BeginRingTimeStart = state.queryParams.beginRingTime === null ? null : state.queryParams.beginRingTime[0];
  571. requestParams.value.BeginRingTimeEnd = state.queryParams.beginRingTime === null ? null : state.queryParams.beginRingTime[1];
  572. Reflect.deleteProperty(requestParams.value, 'beginRingTime'); //振铃开始和结束时间段
  573. requestParams.value.EndRingTimeStart = state.queryParams.endRingTime === null ? null : state.queryParams.endRingTime[0];
  574. requestParams.value.EndRingTimeEnd = state.queryParams.endRingTime === null ? null : state.queryParams.endRingTime[1];
  575. Reflect.deleteProperty(requestParams.value, 'endRingTime'); //振铃结束时间段
  576. Reflect.deleteProperty(requestParams.value, 'type'); // 删除无用的参数
  577. switch (state.queryParams.type) {
  578. case '0': // 全部
  579. requestParams.value.IsAiAnswered = false;
  580. break;
  581. case '1': // 呼入已接
  582. requestParams.value.CallDirection = 0;
  583. requestParams.value.OnState = 1;
  584. requestParams.value.IsAiAnswered = false;
  585. break;
  586. case '2': // 呼出已接
  587. requestParams.value.CallDirection = 1;
  588. requestParams.value.OnState = 1;
  589. requestParams.value.IsAiAnswered = false;
  590. break;
  591. case '3': // 未接
  592. requestParams.value.OnState = 2;
  593. break;
  594. case '4': // 智能应答
  595. requestParams.value.IsAiAnswered = true;
  596. requestParams.value.OnState = 1;
  597. requestParams.value.CallDirection = 0;
  598. break;
  599. case '5': // 呼入白名单
  600. requestParams.value.PhoneTypes = 1; // 呼入VIP
  601. requestParams.value.CallDirection = 0; // 来电
  602. requestParams.value.IsAiAnswered = false;
  603. break;
  604. case '6': // 呼入黑名单
  605. requestParams.value.PhoneTypes = 2; // 呼入黑名单
  606. requestParams.value.IsAiAnswered = false;
  607. break;
  608. default:
  609. break;
  610. }
  611. const response = await callLogPaged(requestParams.value);
  612. state.tableData = response.result?.items ?? [];
  613. state.total = response.result?.total ?? 0;
  614. state.loading = false;
  615. tableRef.value.clearCheckboxRow()
  616. } catch (e) {
  617. state.loading = false;
  618. console.log(e);
  619. tableRef.value.clearCheckboxRow();
  620. }
  621. };
  622. // 是否敏感通话
  623. const IsSensitiveWord = ref(false);
  624. const changeIsSensitiveWord = (val: any) => {
  625. if (!val) state.queryParams.IsSensitiveWord = null;
  626. else state.queryParams.IsSensitiveWord = val;
  627. handleQuery();
  628. };
  629. /** 重置按钮操作 */
  630. const drawer = ref(false);
  631. const drawerRuleFormRef = ref();
  632. const resetQuery = (formEl: FormInstance | undefined) => {
  633. if (!formEl) return;
  634. formEl.resetFields();
  635. ruleFormRef.value?.resetFields();
  636. IsSensitiveWord.value = false;
  637. queryList();
  638. };
  639. // 播放录音
  640. const playRecordRef = ref<RefType>();
  641. const storesThemeConfig = useThemeConfig();
  642. const { themeConfig } = storeToRefs(storesThemeConfig);
  643. const onPlaySoundRecording = (val: any) => {
  644. playRecordRef.value.playRecord(val.otherAccept);
  645. };
  646. // 下载录音(因为天润无法配置跨域所以需要后端代理请求一次)
  647. const onDownload = (row: any) => {
  648. ElMessageBox.confirm(`您确定要下载此录音吗?`, '提示', {
  649. confirmButtonText: '确认',
  650. cancelButtonText: '取消',
  651. type: 'warning',
  652. draggable: true,
  653. cancelButtonClass: 'default-button',
  654. autofocus: false,
  655. })
  656. .then(() => {
  657. fileDownload({ path: themeConfig.value.recordDownLoadPrefix + row.recordingAbsolutePath }).then((res: any) => {
  658. downloadFileByStream(res, row.recordingFileName);
  659. });
  660. })
  661. .catch(() => {});
  662. };
  663. // 关联业务
  664. const connectBusinessRef = ref<RefType>();
  665. const onConnect = (row: any) => {
  666. connectBusinessRef.value.openDialog(row);
  667. };
  668. // 失联工单
  669. const router = useRouter();
  670. const onCreate = (row: any) => {
  671. router.push({
  672. name: 'orderAccept',
  673. query: {
  674. createBy: 'tel',
  675. fromTel: row.cpn,
  676. callId: row.otherAccept,
  677. transfer: row.gateway,
  678. telArea: '',
  679. },
  680. });
  681. };
  682. // 创建转写任务
  683. const onTransferOrder = () => {
  684. const ids = checkTable.value.map((item: any) => item.otherAccept);
  685. ElMessageBox.confirm(`您确定录音转写选中的录音吗?`, '提示', {
  686. confirmButtonText: '确认',
  687. cancelButtonText: '取消',
  688. type: 'warning',
  689. draggable: true,
  690. cancelButtonClass: 'default-button',
  691. autofocus: false,
  692. })
  693. .then(() => {
  694. state.loading = true;
  695. callLogTranscription({ ids })
  696. .then(() => {
  697. ElMessage({
  698. message: '转写任务创建成功',
  699. type: 'success',
  700. });
  701. queryList();
  702. })
  703. .catch((e) => {
  704. console.log(e);
  705. state.loading = false;
  706. tableRef.value.clearCheckboxRow();
  707. });
  708. })
  709. .catch(() => {
  710. });
  711. };
  712. // 转写工单
  713. const transferOrder = (row: any) => {
  714. router.push({
  715. name: 'orderAccept',
  716. query: {
  717. createBy: 'transfer',
  718. fromTel: row.cpn,
  719. callId: row.otherAccept,
  720. transfer: row.gateway,
  721. telArea: '',
  722. },
  723. });
  724. Local.set(`${row.otherAccept}`, row.transliterationContent);
  725. };
  726. // 基础信息
  727. const getBaseData = async () => {
  728. const response = await callBaseData();
  729. state.callDirection = response.result.callDirection;
  730. state.onState = response.result.onState;
  731. state.endByOptions = response.result.endBy;
  732. };
  733. const tableRef = ref<RefType>();
  734. const checkTable = ref<EmptyArrayType>([]);
  735. const selectAllChangeEvent = ({ checked }) => {
  736. if (tableRef.value) {
  737. const records = tableRef.value.getCheckboxRecords();
  738. checkTable.value = records;
  739. console.log(checked ? '所有勾选事件' : '所有取消事件', records);
  740. }
  741. };
  742. const selectChangeEvent = ({ checked }) => {
  743. if (tableRef.value) {
  744. const records = tableRef.value.getCheckboxRecords();
  745. checkTable.value = records;
  746. console.log(checked ? '勾选事件' : '取消事件', records);
  747. }
  748. };
  749. const isChecked = computed(() => {
  750. return !Boolean(checkTable.value.length);
  751. });
  752. const checkboxConfig = reactive<any>({
  753. // labelField: 'id',
  754. // 未转写和转写失败才能选择
  755. checkMethod: ({ row }) => {
  756. return [0, 3].includes(row.transliterationState);
  757. },
  758. });
  759. const toolbarRef = ref<RefType>();
  760. onMounted(() => {
  761. getBaseData();
  762. queryList();
  763. if (tableRef.value && toolbarRef.value) {
  764. tableRef.value.connect(toolbarRef.value);
  765. }
  766. });
  767. const handleEvent = debounce(() => {
  768. handleQuery();
  769. }, 1000);
  770. onActivated(() => {
  771. mittBus.on('clearCachePage', () => {
  772. //清除缓存
  773. handleEvent();
  774. });
  775. });
  776. onBeforeUnmount(() => {
  777. mittBus.off('clearCachePage');
  778. });
  779. </script>