index.vue 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. <script setup>
  2. import { useRouter } from 'vue-router'
  3. import { workbenchApi } from '@/api/voice/workbench';
  4. import SearchItemWrapper from '@/components/SearchItemWrapper';
  5. import AudioPlayer from '@/components/AudioPlayer';
  6. import useTableHeight from '@/composables/useTableHeight';
  7. const router = useRouter();
  8. const { proxy } = getCurrentInstance();
  9. const { tableContainer, tableMaxHeight } = useTableHeight();
  10. const dataPickerValue = ref([]);
  11. const loading = ref(false);
  12. const tableData = ref([]);
  13. const total = ref(0);
  14. const agentList = ref([]);
  15. const queryParams = ref({
  16. pageNum: 1,
  17. pageSize: 10,
  18. userId: '',
  19. status: '',
  20. category: '',
  21. phone: '',
  22. })
  23. // 清除检索条件
  24. const handleCleanOptions = () => {
  25. queryParams.value = {
  26. pageNum: 1,
  27. pageSize: 10,
  28. userId: '',
  29. status: '',
  30. category: '',
  31. phone: '',
  32. };
  33. dataPickerValue.value = [];
  34. getList();
  35. }
  36. const jumpDetails = ({ id }) => {
  37. router.push({
  38. path: '/voice/call/details',
  39. query: { id }
  40. })
  41. }
  42. // 音频加载完成
  43. const onAudioLoadDone = ({ durationTime, id }) => {
  44. tableData.value.map(item => {
  45. if (item.id == id) {
  46. item.times = durationTime || ''
  47. }
  48. })
  49. }
  50. // 批量下载
  51. const handleBatchDownload = () => {
  52. const [timeBegin, timeEnd] = dataPickerValue.value;
  53. proxy.getDownload("/business/record/downloadBatchByCondition", {
  54. ...queryParams.value, timeBegin, timeEnd
  55. }, `${new Date().getTime()}.zip`);
  56. }
  57. // 单独下载
  58. const handleDownload = ({ id }) => {
  59. proxy.getDownload("/business/record/downloadById", { id }, `${new Date().getTime()}.wav`);
  60. }
  61. const getList = () => {
  62. const [timeBegin, timeEnd] = dataPickerValue.value || [];
  63. loading.value = true;
  64. workbenchApi.getCallRecordList({...queryParams.value, timeBegin, timeEnd}).then(({ rows, total:t }) => {
  65. const typeEnum = { 0: '白名单', 1: 'AI客服', 2: '传统服务' };
  66. const statusEnum = { 0: '未接听', 1: '已接通' };
  67. const serviceCategoryEnum = { 0: '人工坐席', 1: '机器人坐席', 2: '机器人转人工' };
  68. tableData.value = rows.map(item => ({
  69. ...item,
  70. url: item.url ? item.url + '?timstamp=' + new Date().getTime() : '',
  71. typeText: typeEnum[item.type],
  72. statusText: statusEnum[item.status],
  73. serviceCategoryText: serviceCategoryEnum[item.serviceCategory]
  74. }));
  75. loading.value = false;
  76. total.value = t;
  77. })
  78. };
  79. onMounted(() => {
  80. workbenchApi.getAgentList().then(({ data }) => {
  81. agentList.value = data;
  82. })
  83. getList();
  84. })
  85. </script>
  86. <template>
  87. <div class="call-viewprot">
  88. <div class="search-card">
  89. <el-row :gutter="24" class="mb-[24px]">
  90. <el-col :span="6">
  91. <SearchItemWrapper>
  92. <el-input class="search-input" placeholder="用户电话号码" v-model="queryParams.phone"></el-input>
  93. </SearchItemWrapper>
  94. </el-col>
  95. <el-col :span="6">
  96. <SearchItemWrapper label="客服">
  97. <el-select v-model="queryParams.userId" placeholder="请选择" size="large" :empty-values="[null, undefined]">
  98. <el-option label="全部" value="" />
  99. <el-option v-for="item in agentList" :key="item.id" :label="item.name" :value="item.id" />
  100. </el-select>
  101. </SearchItemWrapper>
  102. </el-col>
  103. <el-col :span="6">
  104. <SearchItemWrapper label="通话状态">
  105. <el-select v-model="queryParams.status" placeholder="Select" size="large" :empty-values="[null, undefined]">
  106. <el-option label="全部" value="" />
  107. <el-option label="未接听" :value="0" />
  108. <el-option label="已接通" :value="1" />
  109. </el-select>
  110. </SearchItemWrapper>
  111. </el-col>
  112. <el-col :span="6">
  113. <SearchItemWrapper label="通话类型">
  114. <el-select v-model="queryParams.category" placeholder="全部" size="large" :empty-values="[null, undefined]">
  115. <el-option label="全部" value="" />
  116. <el-option label="呼入" :value="0" />
  117. <el-option label="呼出" :value="1" />
  118. </el-select>
  119. </SearchItemWrapper>
  120. </el-col>
  121. </el-row>
  122. <el-row :gutter="24">
  123. <el-col :span="6">
  124. <SearchItemWrapper label="通话发起时间">
  125. <el-date-picker v-model="dataPickerValue" type="daterange" range-separator="-" start-placeholder="起始日期"
  126. end-placeholder="结束日期" style="width: 100%;" :editable="false" value-format="YYYY-MM-DD"/>
  127. </SearchItemWrapper>
  128. </el-col>
  129. <el-col :span="18">
  130. <div class="flex items-center justify-start space-x-[30px]">
  131. <div class="custom-btn custom-btn_primary" @click="getList">搜索</div>
  132. <div class="custom-btn custom-btn_default" @click="handleBatchDownload">
  133. 批量下载语音
  134. </div>
  135. <div class="custom-btn custom-btn_text space-x-[2px]" @click="handleCleanOptions">
  136. <img src="@/assets/images/workbench/icon-clean.svg" alt="">
  137. <span>清除条件</span>
  138. </div>
  139. </div>
  140. </el-col>
  141. </el-row>
  142. </div>
  143. <div class="table-card">
  144. <div style="height: 100%;" ref="tableContainer">
  145. <el-table :data="tableData" style="width: 100%" :max-height="tableMaxHeight" v-loading="loading">
  146. <el-table-column prop="phone" label="用户电话" align="center" width="130" fixed />
  147. <el-table-column prop="typeText" label="呼叫类型" align="center" width="100" />
  148. <el-table-column prop="statusText" label="通话状态" align="center" width="100">
  149. <template #default="scope">
  150. <div class="flex items-center justify-center space-x-[6px]">
  151. <span class="w-[6px] h-[6px] rounded-full" :class="[scope.row.status === 1 ? 'bg-[#65C734]': 'bg-[#c75134]']" ></span>
  152. <span>{{ scope.row.statusText }}</span>
  153. </div>
  154. </template>
  155. </el-table-column>
  156. <el-table-column prop="timeBegin" label="通话发起时间" align="center" width="180" />
  157. <el-table-column prop="timeEnd" label="通话结束时间" align="center" width="180" />
  158. <el-table-column prop="times" label="通话时长" align="center" />
  159. <el-table-column prop="address" label="通话录音" align="center" width="350">
  160. <template #default="scope">
  161. <div class="flex justify-center" v-show="scope.row.url">
  162. <AudioPlayer :audioUrl="scope.row.url" @loadDone="onAudioLoadDone" :id="scope.row.id"></AudioPlayer>
  163. </div>
  164. </template>
  165. </el-table-column>
  166. <el-table-column prop="serviceCategoryText" label="服务类型" align="center" width="120"/>
  167. <el-table-column prop="userName" label="客服" align="center" />
  168. <el-table-column prop="handle" label="操作" align="center" fixed="right" width="150">
  169. <template #default="scope">
  170. <div class="flex justify-center space-x-[20px]">
  171. <span class="text-[#165DFF] cursor-pointer" @click="jumpDetails(scope.row)">详情</span>
  172. <span class="text-[#165DFF] cursor-pointer" @click="handleDownload(scope.row)">语音下载</span>
  173. </div>
  174. </template>
  175. </el-table-column>
  176. </el-table>
  177. <pagination v-show="total >= 0" :total="total" v-model:page="queryParams.pageNum"
  178. v-model:limit="queryParams.pageSize" @pagination="getList" />
  179. </div>
  180. </div>
  181. </div>
  182. </template>
  183. <style lang="scss" scoped>
  184. .call-viewprot {
  185. display: flex;
  186. flex-flow: column;
  187. width: 100%;
  188. height: 100%;
  189. padding: 28px 24px 18px 24px;
  190. border-radius: 8px;
  191. background: #fff;
  192. .search-card {
  193. padding-bottom: 20px;
  194. margin-bottom: 20px;
  195. border-bottom: 1px dashed #E5E6EB;
  196. overflow: hidden;
  197. flex-shrink: 0;
  198. }
  199. .table-card {
  200. height: 100%;
  201. }
  202. }
  203. </style>