index.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. <template>
  2. <div class="app-container">
  3. <el-row :gutter="24" class="baseInfo">
  4. <!-- 总记录 -->
  5. <el-col :xs="12" :sm="12" :lg="6" class="ivu-mb">
  6. <el-card shadow="hover" class="stat-card">
  7. <div slot="header" class="card-header">
  8. <span>外呼记录总数</span>
  9. </div>
  10. <div class="content" v-loading="loadingCount">
  11. <span class="card-number total">
  12. <count-to
  13. v-if="!loadingCount"
  14. :start-val="0"
  15. :end-val="count.recordCount || 0"
  16. :duration="2000"
  17. />
  18. </span>
  19. </div>
  20. </el-card>
  21. </el-col>
  22. <!-- 成功数 -->
  23. <el-col :xs="12" :sm="12" :lg="6" class="ivu-mb">
  24. <el-card shadow="hover" class="stat-card">
  25. <div slot="header" class="card-header">
  26. <span>外呼成功总数</span>
  27. </div>
  28. <div class="content" v-loading="loadingCount">
  29. <span class="card-number success">
  30. <count-to
  31. v-if="!loadingCount"
  32. :start-val="0"
  33. :end-val="count.successRecordCount || 0"
  34. :duration="2000"
  35. />
  36. </span>
  37. </div>
  38. </el-card>
  39. </el-col>
  40. <!-- 今日发送 -->
  41. <el-col :xs="12" :sm="12" :lg="6" class="ivu-mb">
  42. <el-card shadow="hover" class="stat-card">
  43. <div slot="header" class="card-header">
  44. <span>今日发送</span>
  45. </div>
  46. <div class="content" v-loading="loadingCount">
  47. <span class="card-number today">
  48. <count-to
  49. v-if="!loadingCount"
  50. :start-val="0"
  51. :end-val="count.todayCount || 0"
  52. :duration="2000"
  53. />
  54. </span>
  55. </div>
  56. </el-card>
  57. </el-col>
  58. <!-- 今日成功 -->
  59. <el-col :xs="12" :sm="12" :lg="6" class="ivu-mb">
  60. <el-card shadow="hover" class="stat-card">
  61. <div slot="header" class="card-header">
  62. <span>今日成功</span>
  63. </div>
  64. <div class="content" v-loading="loadingCount">
  65. <span class="card-number today-success">
  66. <count-to
  67. v-if="!loadingCount"
  68. :start-val="0"
  69. :end-val="count.todaySuccessCount || 0"
  70. :duration="2000"
  71. />
  72. </span>
  73. </div>
  74. </el-card>
  75. </el-col>
  76. </el-row>
  77. <el-form v-show="showSearch" ref="queryForm" :model="queryParams" :inline="true" label-width="96px">
  78. <el-form-item prop="roboticId">
  79. <el-select
  80. v-model="queryParams.roboticId"
  81. filterable
  82. clearable
  83. placeholder="请选择任务"
  84. @change="handleQuery"
  85. class="task-select"
  86. >
  87. <i slot="prefix" class="el-icon-s-operation"/>
  88. <el-option label="所有任务" :value="null"/>
  89. <el-option
  90. v-for="item in roboticList"
  91. :key="item.id"
  92. :label="item.name"
  93. :value="item.id"
  94. />
  95. </el-select>
  96. </el-form-item>
  97. <!-- <el-form-item>-->
  98. <!-- <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">查找</el-button>-->
  99. <!-- <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>-->
  100. <!-- </el-form-item>-->
  101. </el-form>
  102. <el-row :gutter="10" class="mb8">
  103. <el-col :span="1.5">
  104. <el-button
  105. v-hasPermi="['company:addwxlog:list']"
  106. type="warning"
  107. icon="el-icon-download"
  108. size="mini"
  109. @click="handleExport"
  110. >导出
  111. </el-button>
  112. </el-col>
  113. <right-toolbar :show-search.sync="showSearch" @queryTable="handleQuery"/>
  114. </el-row>
  115. <el-table v-loading="loading" :data="companySendMsgList">
  116. <!-- <el-table-column label="记录 ID" align="center" prop="logId" width="90" />-->
  117. <el-table-column label="任务 ID" align="center" prop="roboticId" width="150"/>
  118. <el-table-column label="任务名称" align="center" prop="roboticName" width="250"/>
  119. <el-table-column label="发送数量" align="center" prop="totalRecordCount" width="150"/>
  120. <el-table-column label="发送成功数量" align="center" prop="successCount" width="150"/>
  121. <el-table-column label="发送失败数量" align="center" prop="failCount" width="150"/>
  122. <el-table-column label="发送中数量" align="center" prop="runningCount" width="150"/>
  123. <!-- <el-table-column label="销售名称" align="center" prop="companyUserName" width="180" />-->
  124. <!-- <el-table-column label="花费金额" align="center" prop="cost" />-->
  125. <!-- <el-table-column label="手机号" align="center" prop="phone" />-->
  126. <!-- <el-table-column label="短信模版名称" align="center" prop="smsTempName" />-->
  127. <!-- <el-table-column label="内容长度" align="center" prop="contentLen" />-->
  128. <!-- <el-table-column label="公司" align="center" prop="companyName" />-->
  129. <!-- <el-table-column label="添加时间" align="center" prop="addTime" />-->
  130. <!-- <el-table-column label="状态" align="center" prop="status" width="100">-->
  131. <!-- <template slot-scope="scope">-->
  132. <!-- <el-tag :type="getStatusType(scope.row.status)">-->
  133. <!-- {{ getStatusLabel(scope.row.status) }}-->
  134. <!-- </el-tag>-->
  135. <!-- </template>-->
  136. <!-- </el-table-column>-->
  137. <!-- <el-table-column label="执行时间" align="center" prop="runTime" width="180">-->
  138. <!-- <template slot-scope="scope">-->
  139. <!-- <span>{{ parseTime(scope.row.runTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>-->
  140. <!-- </template>-->
  141. <!-- </el-table-column>-->
  142. <el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed ="right">
  143. <template slot-scope="scope">
  144. <el-button
  145. size="mini"
  146. type="text"
  147. icon="el-icon-view"
  148. @click="handleViewDetail(scope.row)"
  149. >
  150. 查看详情
  151. </el-button>
  152. </template>
  153. </el-table-column>
  154. </el-table>
  155. <el-dialog
  156. :title="detailTitle"
  157. :visible.sync="detailDialogVisible"
  158. width="90%"
  159. append-to-body
  160. @close="handleDetailClose"
  161. >
  162. <!-- 筛选条件 -->
  163. <el-form
  164. :model="detailQueryParams"
  165. ref="detailQueryForm"
  166. :inline="true"
  167. size="small"
  168. label-width="70px"
  169. class="detail-query-form"
  170. >
  171. <!-- <el-form-item label="调用时间">-->
  172. <!-- <el-date-picker-->
  173. <!-- v-model="detailTimeRange"-->
  174. <!-- type="daterange"-->
  175. <!-- range-separator="至"-->
  176. <!-- start-placeholder="开始日期"-->
  177. <!-- end-placeholder="结束日期"-->
  178. <!-- value-format="yyyy-MM-dd"-->
  179. <!-- clearable-->
  180. <!-- />-->
  181. <!-- </el-form-item>-->
  182. <el-form-item label="手机号">
  183. <el-input
  184. v-model="detailQueryParams.phone"
  185. placeholder="请输入手机号"
  186. clearable
  187. @keyup.enter.native="handleDetailQuery"
  188. />
  189. </el-form-item>
  190. <el-form-item>
  191. <el-button type="primary" icon="el-icon-search" @click="handleDetailQuery">
  192. 查询
  193. </el-button>
  194. <el-button icon="el-icon-refresh" @click="resetDetailQuery">
  195. 重置
  196. </el-button>
  197. </el-form-item>
  198. </el-form>
  199. <el-table
  200. v-loading="detailLoading"
  201. :data="detailList"
  202. border
  203. style="width: 100%"
  204. >
  205. <el-table-column label="日志ID" align="center" prop="logId" width="90" />
  206. <el-table-column label="任务名称" align="center" prop="roboticName" min-width="160" show-overflow-tooltip />
  207. <el-table-column label="记录调用时间" align="center" prop="runTime" width="160" />
  208. <el-table-column label="执行状态" align="center" prop="status" width="100">
  209. <template slot-scope="scope">
  210. <el-tag v-if="scope.row.status === 1" type="warning">执行中</el-tag>
  211. <el-tag v-else-if="scope.row.status === 2" type="success">执行成功</el-tag>
  212. <el-tag v-else-if="scope.row.status === 3" type="danger">执行失败</el-tag>
  213. <span v-else>--</span>
  214. </template>
  215. </el-table-column>
  216. <el-table-column label="客户号码" align="center" prop="callerNum" width="130" />
  217. <el-table-column label="话术号码" align="center" prop="calleeNum" width="130" />
  218. <el-table-column label="客户类型" align="center" prop="intention" width="120" />
  219. <el-table-column label="通话时长(秒)" align="center" prop="callTime" width="120" />
  220. <el-table-column label="花费金额" align="center" prop="cost" width="100" />
  221. <el-table-column label="公司名称" align="center" prop="companyName" min-width="140" show-overflow-tooltip />
  222. <el-table-column label="销售名称" align="center" prop="companyUserName" min-width="120" show-overflow-tooltip />
  223. <el-table-column label="录音地址" align="center" min-width="120">
  224. <template slot-scope="scope">
  225. <el-link
  226. v-if="scope.row.recordPath"
  227. :href="scope.row.recordPath"
  228. type="primary"
  229. target="_blank"
  230. >
  231. 查看录音
  232. </el-link>
  233. <span v-else>--</span>
  234. </template>
  235. </el-table-column>
  236. </el-table>
  237. <!-- 分页 -->
  238. <pagination
  239. v-show="detailTotal > 0"
  240. :total="detailTotal"
  241. :page.sync="detailQueryParams.pageNum"
  242. :limit.sync="detailQueryParams.pageSize"
  243. @pagination="getDetailList"
  244. />
  245. <div slot="footer" class="dialog-footer">
  246. <el-button @click="detailDialogVisible = false">关 闭</el-button>
  247. </div>
  248. </el-dialog>
  249. <pagination
  250. v-show="total > 0"
  251. :total="total"
  252. :page.sync="queryParams.pageNum"
  253. :limit.sync="queryParams.pageSize"
  254. @pagination="getList"
  255. />
  256. </div>
  257. </template>
  258. <script>
  259. import {listCallphone, listCallPhoneByRoboticId, delSendmsg, getCallPhoneLogCount, exportCallphone, groupList} from '@/api/company/callphone'
  260. import {listAll as roboticListAll} from '@/api/company/companyVoiceRobotic'
  261. import {listAll as accountListAll} from '@/api/company/companyAccount'
  262. import CountTo from 'vue-count-to'
  263. export default {
  264. name: 'WxClientStatistics',
  265. components: {CountTo},
  266. data() {
  267. return {
  268. loading: false,
  269. showSearch: true,
  270. total: 0,
  271. companySendMsgList: [],
  272. roboticList: [],
  273. accountList: [],
  274. count: {},
  275. totalRecordCount: 0,
  276. successRecordCount: 0,
  277. detailDialogVisible: false,
  278. detailLoading: false,
  279. detailList: [],
  280. detailTitle: "查看详情",
  281. detailTimeRange: [],
  282. detailTotal: 0,
  283. queryParams: {
  284. pageNum: 1,
  285. pageSize: 10,
  286. roboticId: null,
  287. wxAccountId: null,
  288. customerIdId: null,
  289. status: null,
  290. time: []
  291. },
  292. detailQueryParams: {
  293. pageNum: 1,
  294. pageSize: 10,
  295. roboticId: null,
  296. phone: null,
  297. beginRunTime: null,
  298. endRunTime: null
  299. },
  300. loadingCount: false
  301. }
  302. },
  303. created() {
  304. this.initOptions()
  305. this.handleQuery()
  306. this.getCount()
  307. },
  308. methods: {
  309. initOptions() {
  310. roboticListAll().then((res) => {
  311. this.roboticList = res.data || []
  312. })
  313. accountListAll().then((res) => {
  314. this.accountList = res.data || []
  315. })
  316. },
  317. getQueryData() {
  318. const params = JSON.parse(JSON.stringify(this.queryParams))
  319. if (params.time && params.time.length === 2) {
  320. params.beginTime = params.time[0]
  321. params.endTime = params.time[1]
  322. }
  323. delete params.time
  324. return params
  325. },
  326. getList() {
  327. this.loading = true
  328. groupList(this.getQueryData())
  329. .then((response) => {
  330. this.companySendMsgList = response.rows || []
  331. this.total = response.total || 0
  332. this.successRecordCount = response.successRecordCount || 0
  333. this.totalRecordCount = response.totalRecordCount || 0
  334. })
  335. .finally(() => {
  336. this.loading = false
  337. })
  338. },
  339. // statis() {
  340. // addWxStatistics(this.getQueryData()).then((res) => {
  341. // this.count = res || {}
  342. // })
  343. // },
  344. getStatusLabel(value) {
  345. if (value === 1) return '执行中'
  346. if (value === 2) return '成功'
  347. if (value === 3) return '失败'
  348. return '-'
  349. },
  350. getStatusType(value) {
  351. if (value === 1) return 'warning'
  352. if (value === 2) return 'success'
  353. if (value === 3) return 'danger'
  354. return 'info'
  355. },
  356. handleQuery() {
  357. this.queryParams.pageNum = 1
  358. // this.statis()
  359. this.getList()
  360. },
  361. resetQuery() {
  362. this.resetForm('queryForm')
  363. this.handleQuery()
  364. },
  365. handleExport() {
  366. const queryParams = this.getQueryData()
  367. this.$confirm('是否确认导出查询出的短链课程看课记录数据项?', "警告", {
  368. confirmButtonText: "确定",
  369. cancelButtonText: "取消",
  370. type: "warning"
  371. }).then(() => {
  372. this.exportLoading = true;
  373. return exportCallphone(queryParams)
  374. }).then((response) => {
  375. this.download(response.msg)
  376. this.exportLoading = false;
  377. }).catch(() => {
  378. })
  379. },
  380. /** 删除按钮操作 */
  381. handleDelete(row) {
  382. const ids = row.logId || this.ids;
  383. this.$confirm('是否确认删除编号为 "' + ids + '" 的加微信日志数据项?', "警告", {
  384. confirmButtonText: "确定",
  385. cancelButtonText: "取消",
  386. type: "warning"
  387. }).then(function () {
  388. return delSendmsg(ids);
  389. }).then(() => {
  390. this.getList();
  391. this.msgSuccess("删除成功");
  392. }).catch(function () {
  393. });
  394. },
  395. /** 点击查看详情 */
  396. handleViewDetail(row) {
  397. this.detailTitle = `查看详情 - ${row.roboticName || ""}`;
  398. this.detailDialogVisible = true;
  399. this.detailQueryParams.pageNum = 1;
  400. this.detailQueryParams.pageSize = 10;
  401. this.detailQueryParams.callerId = row.callerId;
  402. this.detailQueryParams.roboticId = row.roboticId;
  403. this.detailQueryParams.phone = null;
  404. this.detailQueryParams.beginRunTime = null;
  405. this.detailQueryParams.endRunTime = null;
  406. this.detailTimeRange = [];
  407. this.getDetailList();
  408. },
  409. /** 查询详情分页列表 */
  410. getDetailList() {
  411. this.detailLoading = true;
  412. listCallPhoneByRoboticId(this.detailQueryParams).then(response => {
  413. this.detailList = response.rows || [];
  414. this.detailTotal = response.total || 0;
  415. this.detailLoading = false;
  416. }).catch(() => {
  417. this.detailLoading = false;
  418. });
  419. },
  420. handleDetailQuery() {
  421. this.detailQueryParams.pageNum = 1;
  422. if (this.detailTimeRange && this.detailTimeRange.length === 2) {
  423. this.detailQueryParams.beginRunTime = this.detailTimeRange[0];
  424. this.detailQueryParams.endRunTime = this.detailTimeRange[1];
  425. } else {
  426. this.detailQueryParams.beginRunTime = null;
  427. this.detailQueryParams.endRunTime = null;
  428. }
  429. this.getDetailList();
  430. },
  431. resetDetailQuery() {
  432. this.detailTimeRange = [];
  433. this.detailQueryParams.pageNum = 1;
  434. this.detailQueryParams.pageSize = 10;
  435. this.detailQueryParams.phone = null;
  436. this.detailQueryParams.beginRunTime = null;
  437. this.detailQueryParams.endRunTime = null;
  438. this.getDetailList();
  439. },
  440. /** 获取统计数据 */
  441. getCount() {
  442. this.loadingCount = true;
  443. getCallPhoneLogCount().then(res => {
  444. this.count = res.data || {};
  445. this.loadingCount = false;
  446. }).catch(() => {
  447. this.loadingCount = false;
  448. });
  449. },
  450. handleDetailClose() {
  451. this.detailList = [];
  452. this.detailTotal = 0;
  453. this.detailTimeRange = [];
  454. this.detailQueryParams = {
  455. pageNum: 1,
  456. pageSize: 10,
  457. callerId: null,
  458. roboticId: null,
  459. phone: null,
  460. beginRunTime: null,
  461. endRunTime: null
  462. };
  463. },
  464. }
  465. }
  466. </script>
  467. <style rel="stylesheet/scss" lang="scss" scoped>
  468. .baseInfo {
  469. margin-bottom: 30px;
  470. }
  471. .content {
  472. .card-panel-num {
  473. display: inline-block;
  474. font-size: 30px;
  475. }
  476. &-time {
  477. font-size: 14px;
  478. }
  479. }
  480. .task-select .el-input__inner {
  481. background-color: #ecf5ff;
  482. border: 1px solid #409EFF;
  483. border-radius: 20px;
  484. height: 36px;
  485. padding-left: 15px;
  486. box-shadow: 0 2px 6px rgba(64, 158, 255, 0.1);
  487. transition: all 0.2s;
  488. }
  489. .task-select .el-input__inner:hover {
  490. border-color: #66b1ff;
  491. }
  492. .task-select .el-input.is-focus .el-input__inner {
  493. border-color: #409EFF;
  494. box-shadow: 0 0 8px rgba(64, 158, 255, 0.3);
  495. background-color: #ffffff;
  496. }
  497. /* 卡片整体 */
  498. .stat-card {
  499. border-radius: 10px;
  500. transition: all 0.3s;
  501. }
  502. .stat-card:hover {
  503. transform: translateY(-3px);
  504. }
  505. /* 标题 */
  506. .card-header {
  507. font-size: 14px;
  508. color: #666;
  509. }
  510. /* 内容区域 */
  511. .content {
  512. height: 60px;
  513. display: flex;
  514. align-items: center;
  515. }
  516. /* 数字基础样式 */
  517. .card-number {
  518. font-size: 28px;
  519. font-weight: bold;
  520. }
  521. /* 🎨 颜色区分 */
  522. .card-number.total {
  523. color: #409EFF; /* 蓝色 */
  524. }
  525. .card-number.success {
  526. color: #67C23A; /* 绿色 */
  527. }
  528. .card-number.today {
  529. color: #E6A23C; /* 橙色 */
  530. }
  531. .card-number.today-success {
  532. color: #F56C6C; /* 红色 */
  533. }
  534. </style>