Visit-detail.vue 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781
  1. <template>
  2. <el-dialog
  3. v-model="state.dialogVisible"
  4. draggable
  5. :title="dialogTitle"
  6. ref="dialogRef"
  7. width="50%"
  8. append-to-body
  9. @mouseup="mouseup"
  10. :style="'transform: ' + state.transform + ';'"
  11. @close="close"
  12. destroy-on-close
  13. >
  14. <el-collapse v-model="state.collapseArr" class="collapse-box" v-loading="state.loading">
  15. <!-- 工单信息 -->
  16. <el-collapse-item name="1">
  17. <template #title>
  18. <p class="pl20">
  19. <b class="font14">工单信息</b>
  20. </p>
  21. </template>
  22. <div class="collapse-container">
  23. <el-form label-width="100px" class="show-info-form">
  24. <el-row :gutter="10">
  25. <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
  26. <el-form-item label="工单编码" class="mb5"> {{ state.orderDetail.no }} </el-form-item>
  27. </el-col>
  28. <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
  29. <el-form-item label="受理时间" class="mb5">
  30. <span>{{ dayjs(state.orderDetail.startTime).format('YYYY-MM-DD HH:mm:ss') }}</span></el-form-item
  31. >
  32. </el-col>
  33. <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
  34. <el-form-item label="受理人" class="mb5">
  35. <span>{{ state.orderDetail?.acceptorName }}</span>
  36. </el-form-item>
  37. </el-col>
  38. <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
  39. <el-form-item label="来源渠道" class="mb5"> {{ state.orderDetail.sourceChannel }} </el-form-item>
  40. </el-col>
  41. <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
  42. <el-form-item label="受理类型" class="mb5"> {{ state.orderDetail.acceptType }} </el-form-item>
  43. </el-col>
  44. <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
  45. <el-form-item label="热点分类" class="mb5"> {{ state.orderDetail.hotspotName }} </el-form-item>
  46. </el-col>
  47. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
  48. <el-form-item label="工单标题" class="mb5"> {{ state.orderDetail.title }} </el-form-item>
  49. </el-col>
  50. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
  51. <el-form-item label="工单内容" class="mb5"> {{ state.orderDetail.content }} </el-form-item>
  52. </el-col>
  53. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
  54. <el-form-item label="办理结果" class="formatted-text mb5"> {{ state.orderDetail.fileOpinion }} </el-form-item>
  55. </el-col>
  56. </el-row>
  57. </el-form>
  58. </div>
  59. </el-collapse-item>
  60. <el-collapse-item name="2">
  61. <template #title>
  62. <p class="pl20">
  63. <b class="font14">来电人信息</b>
  64. </p>
  65. </template>
  66. <div class="collapse-container">
  67. <el-form label-width="100px" class="show-info-form">
  68. <el-row :gutter="10">
  69. <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
  70. <el-form-item label="来电人姓名"> {{ state.orderDetail.fromName }} </el-form-item>
  71. </el-col>
  72. <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" v-if="state.orderDetail.fromPhone">
  73. <el-form-item label="来电号码" class="mb5">
  74. {{ state.orderDetail.fromPhone }}
  75. <el-button
  76. plain
  77. title="人工回访录音"
  78. size="small"
  79. type="primary"
  80. class="ml8"
  81. @click="recordFile"
  82. v-if="state.recordingAbsolutePath"
  83. >人工回访录音</el-button
  84. >
  85. </el-form-item>
  86. </el-col>
  87. <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
  88. <el-form-item label="来电人性别" class="mb5"> {{ state.orderDetail.fromGenderText }} </el-form-item>
  89. </el-col>
  90. <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
  91. <el-form-item label="联系电话">
  92. {{ state.orderDetail.contact }}
  93. <el-button plain title="呼叫" size="small" type="primary" class="ml8" @click="onCall(state.orderDetail.contact)" v-if="!disabled"
  94. >外呼</el-button
  95. >
  96. </el-form-item>
  97. </el-col>
  98. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
  99. <el-form-item label="联系地址"> {{ state.orderDetail.address }} </el-form-item>
  100. </el-col>
  101. </el-row>
  102. </el-form>
  103. </div>
  104. </el-collapse-item>
  105. <el-collapse-item name="3">
  106. <template #title>
  107. <p class="pl20">
  108. <b class="font14">回访信息</b>
  109. </p>
  110. </template>
  111. <div class="collapse-container">
  112. <el-form label-width="120px" ref="ruleFormRef" :model="state.ruleForm" label-position="left">
  113. <!-- 详情 -->
  114. <template v-if="disabled">
  115. <el-row :gutter="10" class="show-info-form">
  116. <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
  117. <el-form-item label="当前回访人">
  118. {{ state.orderVisitModel.employeeName }}
  119. <el-button plain title="智能回访录音" size="small" type="primary" class="ml8" @click="onSmartRecord" v-if="state.smartRecord"
  120. >智能回访录音
  121. </el-button>
  122. <el-button
  123. plain
  124. title="人工回访录音"
  125. size="small"
  126. type="primary"
  127. class="ml8"
  128. @click="recordFile"
  129. v-if="state.recordingAbsolutePath"
  130. >人工回访录音</el-button
  131. >
  132. </el-form-item>
  133. </el-col>
  134. <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" v-if="visitCount">
  135. <el-form-item label="当前工单已回访次数" label-width="140px"> {{ visitCount }}次 </el-form-item>
  136. </el-col>
  137. <!-- <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
  138. <el-form-item label="回访标签">
  139. <span v-if="state.ruleForm.isPutThrough !== null">{{ state.ruleForm.isPutThrough ? '已接通' : '未接通' }}</span>
  140. </el-form-item>
  141. </el-col>-->
  142. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" v-if="['ZiGong'].includes(themeConfig.appScope)">
  143. <el-form-item label="语音评价">
  144. </el-form-item>
  145. </el-col>
  146. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
  147. <el-row v-for="item in state.ruleForm.visitDetails" :key="item.id" :gutter="10">
  148. <!-- 务员评价 -->
  149. <template v-if="item.visitTarget === 10 && isTelSource">
  150. <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
  151. <el-form-item label="话务员评价">
  152. {{ item.seatEvaluateText }}
  153. </el-form-item>
  154. </el-col>
  155. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
  156. <el-form-item label="话务员回访内容">
  157. {{ item.visitContent }}
  158. </el-form-item>
  159. </el-col>
  160. </template>
  161. <!-- 部门评价 -->
  162. <template v-if="item.visitTarget === 20">
  163. <el-divider content-position="left">
  164. <el-text tag="b" size="large" type="primary"> {{ item.visitOrgName }} </el-text>
  165. </el-divider>
  166. <template v-if="['YiBin'].includes(themeConfig.appScope)">
  167. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
  168. <el-form-item label="部门是否联系">
  169. {{ item.isContact === null ? '' : item.isContact === true ? '是' : '否' }}
  170. </el-form-item>
  171. </el-col>
  172. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
  173. <el-form-item label="处理结果">
  174. {{ item.volved === null ? '' : item.volved === true ? '已得到解决' : '未得到解决' }}
  175. </el-form-item>
  176. </el-col>
  177. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" v-if="item.volveConent">
  178. <el-form-item label="备注" class="formatted-text mb5">
  179. {{ item.volveConent }}
  180. </el-form-item>
  181. </el-col>
  182. </template>
  183. <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
  184. <el-form-item label="部门办件结果">
  185. {{ item.orgProcessingResults?.dicDataName }}
  186. </el-form-item>
  187. </el-col>
  188. <!-- 不满意才会选择不满意原因 -->
  189. <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12" v-if="item.orgNoSatisfiedReason && item.orgNoSatisfiedReason.length">
  190. <el-form-item label="不满意原因">
  191. {{ item.orgNoSatisfiedReason.map((item) => item.dicDataName).join(',') }}
  192. </el-form-item>
  193. </el-col>
  194. <!-- <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
  195. <el-form-item label="部门办件态度">
  196. {{ item.orgHandledAttitude?.dicDataName }}
  197. </el-form-item>
  198. </el-col>-->
  199. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
  200. <el-form-item label="部门回访内容">
  201. {{ item.visitContent }}
  202. </el-form-item>
  203. </el-col>
  204. </template>
  205. </el-row>
  206. </el-col>
  207. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" v-if="state.orderVisitModel.orgJudge || state.orderVisitModel.seatJudge">
  208. <el-form-item label="扭转满意度">
  209. <el-tag v-if="state.orderVisitModel.orgJudge" class="mr10">扭转部门满意度</el-tag>
  210. <el-tag v-if="state.orderVisitModel.seatJudge">扭转坐席满意度</el-tag>
  211. </el-form-item>
  212. </el-col>
  213. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" v-if="state.orderVisitModel.judgeStateText">
  214. <el-form-item label="评判结果">
  215. <span>{{ state.orderVisitModel.judgeStateText }}</span>
  216. </el-form-item>
  217. </el-col>
  218. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" v-if="state.orderVisitModel.judgeContent">
  219. <el-form-item label="评判内容" class="formatted-text mb5">
  220. <span>{{ state.orderVisitModel.judgeContent }}</span>
  221. </el-form-item>
  222. </el-col>
  223. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" v-if="['ZiGong'].includes(themeConfig.appScope)">
  224. <el-form-item label="历史回访记录">
  225. <ProTable
  226. ref="proTableRef"
  227. :columns="columns"
  228. :data="state.tableData"
  229. @updateTable="queryList"
  230. :loading="state.loading"
  231. :pagination="false"
  232. border
  233. :toolButton="false"
  234. >
  235. <template #empty>
  236. 暂无数据
  237. </template>
  238. </ProTable>
  239. </el-form-item>
  240. </el-col>
  241. </el-row>
  242. </template>
  243. <!-- 编辑 -->
  244. <template v-else>
  245. <el-row :gutter="10">
  246. <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
  247. <el-form-item label="当前回访人">
  248. {{ userInfos.name }}
  249. <el-button plain title="智能回访录音" size="small" type="primary" class="ml8" @click="onSmartRecord" v-if="state.smartRecord"
  250. >智能回访录音
  251. </el-button>
  252. <el-button
  253. plain
  254. title="人工回访录音"
  255. size="small"
  256. type="primary"
  257. class="ml8"
  258. @click="recordFile"
  259. v-if="state.recordingAbsolutePath"
  260. >人工回访录音</el-button
  261. >
  262. </el-form-item>
  263. </el-col>
  264. <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
  265. <el-form-item label="当前工单已回访次数" label-width="140px"> {{ visitCount }}次 </el-form-item>
  266. </el-col>
  267. <!-- <el-col :span="24">
  268. <el-form-item label="回访标签" prop="isPutThrough" :rules="[{ required: false, message: '请选择回访标签', trigger: 'change' }]">
  269. <el-checkbox v-model="state.ruleForm.isPutThrough">未接通</el-checkbox>
  270. </el-form-item>
  271. </el-col>-->
  272. <el-col :span="24">
  273. <el-row v-for="(item, index) in state.ruleForm.visitDetails" :key="item.id" :gutter="10">
  274. <!-- 务员评价 -->
  275. <template v-if="item.visitTarget === 10 && isTelSource">
  276. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" v-if="['ZiGong'].includes(themeConfig.appScope)">
  277. <el-form-item
  278. label="语音评价"
  279. :prop="`visitDetails.${index}.viceEvaluate`"
  280. :rules="[{ required: true, message: '请选择语音评价', trigger: 'change' }]"
  281. >
  282. <el-radio-group v-model="item.viceEvaluate">
  283. <el-radio v-for="items in seatEvaluate" :key="items.key" :label="items.value" :value="items.key">{{items.value}}</el-radio>
  284. </el-radio-group>
  285. </el-form-item>
  286. </el-col>
  287. <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
  288. <el-form-item
  289. label="话务员评价"
  290. :prop="`visitDetails.${index}.seatEvaluate`"
  291. :rules="[{ required: true, message: '请选择话务员评价', trigger: 'change' }]"
  292. >
  293. <el-select v-model="item.seatEvaluate" placeholder="请选择话务员评价" class="w100">
  294. <el-option v-for="items in seatEvaluate" :key="items.key" :label="items.value" :value="items.key" />
  295. </el-select>
  296. </el-form-item>
  297. </el-col>
  298. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
  299. <el-form-item
  300. label="话务员回访内容"
  301. :prop="`visitDetails.${index}.visitContent`"
  302. :rules="[{ required: true, message: '请填写话务员回访内容', trigger: 'blur' }]"
  303. >
  304. <common-advice
  305. @chooseAdvice="chooseAdvice($event, index)"
  306. v-model="item.visitContent"
  307. placeholder="请填写话务员回访内容"
  308. :loading="state.loading"
  309. :commonEnum="commonEnum.ReturnVisit"
  310. :minRows="5"
  311. :maxRows="10"
  312. drawerWidth="40%"
  313. />
  314. </el-form-item>
  315. </el-col>
  316. </template>
  317. <!-- 部门评价 -->
  318. <template v-if="item.visitTarget === 20">
  319. <el-divider content-position="left">
  320. <el-text tag="b" size="large"> {{ item.visitOrgName }} </el-text>
  321. </el-divider>
  322. <template v-if="['YiBin'].includes(themeConfig.appScope)">
  323. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
  324. <el-form-item
  325. label="部门是否联系"
  326. :prop="`visitDetails.${index}.isContact`"
  327. :rules="[{ required: true, message: '请选择部门是否联系', trigger: 'change' }]"
  328. >
  329. <el-radio-group v-model="item.isContact">
  330. <el-radio :value="true">是</el-radio>
  331. <el-radio :value="false">否</el-radio>
  332. </el-radio-group>
  333. </el-form-item>
  334. </el-col>
  335. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
  336. <el-form-item
  337. label="处理结果"
  338. :prop="`visitDetails.${index}.volved`"
  339. :rules="[{ required: true, message: '请选择处理结果', trigger: 'change' }]"
  340. >
  341. <el-radio-group v-model="item.volved">
  342. <el-radio :value="true">已得到解决</el-radio>
  343. <el-radio :value="false">未得到解决</el-radio>
  344. </el-radio-group>
  345. </el-form-item>
  346. </el-col>
  347. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" v-if="item.volved === false">
  348. <el-form-item
  349. label=""
  350. :prop="`visitDetails.${index}.volveConent`"
  351. :rules="[{ required: false, message: '请填写备注', trigger: 'blur' }]"
  352. >
  353. <common-advice
  354. @chooseAdvice="chooseAdvice($event, index)"
  355. v-model="item.volveConent"
  356. placeholder="请填写备注"
  357. :loading="state.loading"
  358. :commonEnum="commonEnum.ReturnVisit"
  359. :minRows="5"
  360. :maxRows="10"
  361. drawerWidth="40%"
  362. />
  363. </el-form-item>
  364. </el-col>
  365. </template>
  366. <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
  367. <el-form-item
  368. label="部门办件结果"
  369. :prop="`visitDetails.${index}.orgProcessingResults`"
  370. :rules="[{ required: true, message: '请选择部门办件结果', trigger: 'change' }]"
  371. >
  372. <el-select
  373. v-model="item.orgProcessingResults"
  374. placeholder="请选择部门办件结果"
  375. class="w100"
  376. value-key="dicDataValue"
  377. @change="
  378. (val) => {
  379. item.orgProcessingResults.value = val?.dicDataName;
  380. item.orgProcessingResults.key = val?.dicDataValue;
  381. }
  382. "
  383. >
  384. <el-option v-for="items in visitSatisfaction" :key="items.dicDataValue" :label="items.dicDataName" :value="items" />
  385. </el-select>
  386. </el-form-item>
  387. </el-col>
  388. <!-- 不满意才会选择不满意原因 -->
  389. <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12" v-if="['1', '2'].includes(item.orgProcessingResults?.key)">
  390. <el-form-item
  391. label="不满意原因"
  392. :prop="`visitDetails.${index}.orgNoSatisfiedReason`"
  393. :rules="[{ required: true, message: '请选择不满意原因', trigger: 'change' }]"
  394. >
  395. <el-select
  396. v-model="item.orgNoSatisfiedReason"
  397. placeholder="请选择不满意原因"
  398. class="w100"
  399. value-key="dicDataValue"
  400. multiple
  401. collapse-tags
  402. collapse-tags-tooltip
  403. @change="selectReason($event, index)"
  404. >
  405. <el-option v-for="items in dissatisfiedReason" :key="items.dicDataValue" :label="items.dicDataName" :value="items" />
  406. </el-select>
  407. </el-form-item>
  408. </el-col>
  409. <!-- <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
  410. <el-form-item
  411. label="部门办件态度"
  412. :prop="`visitDetails.${index}.orgHandledAttitude`"
  413. :rules="[{ required: true, message: '请选择部门办件态度', trigger: 'change' }]"
  414. >
  415. <el-select
  416. v-model="item.orgHandledAttitude"
  417. placeholder="请选择部门办件态度"
  418. class="w100"
  419. value-key="dicDataValue"
  420. @change="
  421. (val) => {
  422. item.orgHandledAttitude.value = val.dicDataName;
  423. item.orgHandledAttitude.key = val.dicDataValue;
  424. }
  425. "
  426. >
  427. <el-option v-for="items in visitManner" :key="items.dicDataValue" :label="items.dicDataName" :value="items" />
  428. </el-select>
  429. </el-form-item>
  430. </el-col>-->
  431. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
  432. <el-form-item
  433. label="部门回访内容"
  434. :prop="`visitDetails.${index}.visitContent`"
  435. :rules="[{ required: true, message: '请填写部门回访内容', trigger: 'blur' }]"
  436. >
  437. <common-advice
  438. @chooseAdvice="chooseAdvice($event, index)"
  439. v-model="item.visitContent"
  440. d
  441. placeholder="请填写部门回访内容"
  442. :loading="state.loading"
  443. :commonEnum="commonEnum.ReturnVisit"
  444. :minRows="5"
  445. :maxRows="10"
  446. drawerWidth="40%"
  447. />
  448. </el-form-item>
  449. </el-col>
  450. </template>
  451. </el-row>
  452. </el-col>
  453. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
  454. <el-form-item label="扭转满意度" prop="orgJudge" :rules="[{ required: false, message: '请选择扭转满意度', trigger: 'change' }]">
  455. <el-checkbox v-model="state.ruleForm.orgJudge">扭转部门满意度</el-checkbox>
  456. <el-checkbox v-model="state.ruleForm.seatJudge">扭转坐席满意度</el-checkbox>
  457. </el-form-item>
  458. </el-col>
  459. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" v-if="['ZiGong'].includes(themeConfig.appScope)">
  460. <el-form-item label="历史回访记录">
  461. <ProTable
  462. ref="proTableRef"
  463. :columns="columns"
  464. :data="state.tableData"
  465. @updateTable="queryList"
  466. :loading="state.loading"
  467. :pagination="false"
  468. border
  469. :toolButton="false"
  470. >
  471. <template #empty>
  472. 暂无数据
  473. </template>
  474. </ProTable>
  475. </el-form-item>
  476. </el-col>
  477. </el-row>
  478. </template>
  479. </el-form>
  480. </div>
  481. </el-collapse-item>
  482. </el-collapse>
  483. <template #footer v-if="disabled">
  484. <span class="dialog-footer">
  485. <el-button @click="closeDialog" class="default-button">关 闭</el-button>
  486. </span>
  487. </template>
  488. <template #footer v-else>
  489. <span class="dialog-footer">
  490. <el-button @click="closeDialog" class="default-button">取 消</el-button>
  491. <el-button type="primary" @click="onRedo" :loading="state.loading" v-if="['ZiGong'].includes(themeConfig.appScope)">重办</el-button>
  492. <el-button type="primary" @click="onSubmit(ruleFormRef)" :loading="state.loading">保存</el-button>
  493. </span>
  494. </template>
  495. </el-dialog>
  496. <!-- 播放录音 -->
  497. <play-record ref="playRecordRef" />
  498. <!-- 回访重办 -->
  499. <visit-redo ref="visitRedoRef" />
  500. </template>
  501. <script setup lang="tsx" name="orderFollowUpDetail">
  502. import { computed, defineAsyncComponent, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue';
  503. import { ElMessage, FormInstance } from 'element-plus';
  504. import { commonEnum } from '@/utils/constants';
  505. import { storeToRefs } from 'pinia';
  506. import { useUserInfo } from '@/stores/userInfo';
  507. import dayjs from 'dayjs';
  508. import { visitDetailBaseData, visitOrder } from '@/api/business/visit';
  509. import mittBus from '@/utils/mitt';
  510. import { callCenterOutbound } from '@/utils/callCenter';
  511. import { useThemeConfig } from '@/stores/themeConfig';
  512. // 引入组件
  513. const CommonAdvice = defineAsyncComponent(() => import('@/components/CommonAdvice/index.vue')); // 常用意见
  514. const PlayRecord = defineAsyncComponent(() => import('@/components/PlayRecord/index.vue')); // 播放录音
  515. const VisitRedo = defineAsyncComponent(() => import('@/views/business/visit/component/Visit-redo.vue')); // 回访重办
  516. // 定义子组件向父组件传值/事件
  517. const emit = defineEmits(['updateList']);
  518. // 定义变量内容
  519. const state = reactive<any>({
  520. collapseArr: ['1', '2', '3'], // 折叠面板
  521. dialogVisible: false, // 是否显示弹窗
  522. loading: false, // 是否显示加载
  523. transform: 'translate(0px, 0px)', // 附件弹窗位置
  524. smartRecord: null,
  525. ruleForm: {
  526. isPutThrough: false, //未接通
  527. visitDetails: {},
  528. orgJudge: false, // 扭转部门满意度
  529. seatJudge: false, // 扭转坐席满意度
  530. },
  531. orderDetail: {}, // 工单详情
  532. orderVisitModel: {}, // 回访详情
  533. recordingAbsolutePath: '', // 录音文件
  534. tableData:[], //回访记录列表
  535. });
  536. const ruleFormRef = ref<RefType>();
  537. const storesUserInfo = useUserInfo();
  538. const { userInfos } = storeToRefs(storesUserInfo); // 用户信息
  539. const visitCount = ref<number>(0); // 回访次数
  540. const seatEvaluate = ref<EmptyArrayType>(); // 话务员评价
  541. const viceEvaluate = ref<EmptyArrayType>(); // 语音评价
  542. const dissatisfiedReason = ref<EmptyArrayType>(); // 不满意原因
  543. const visitManner = ref<EmptyArrayType>(); // 部门办件态度
  544. const visitSatisfaction = ref<EmptyArrayType>(); // 部门办件结果
  545. const visitId = ref<string>(''); // 回访id
  546. const dialogTitle = ref<string>('回访'); // 弹窗标题
  547. const aiVisitVoiceBaseUrl = ref(''); // 智能回访录音前缀
  548. const storesThemeConfig = useThemeConfig();
  549. const { themeConfig } = storeToRefs(storesThemeConfig);
  550. const getBaseData = async (id: string) => {
  551. state.loading = true;
  552. try {
  553. const { result } = await visitDetailBaseData(id);
  554. visitCount.value = result?.visitCount ?? 0;
  555. seatEvaluate.value = result?.seatEvaluate ?? [];
  556. viceEvaluate.value = result?.viceEvaluate ?? [];
  557. dissatisfiedReason.value = result?.dissatisfiedReason ?? [];
  558. visitManner.value = result?.visitManner ?? [];
  559. visitSatisfaction.value = result?.visitSatisfaction ?? [];
  560. visitId.value = result?.orderVisitModel?.id ?? '';
  561. state.orderDetail = result?.orderVisitModel?.order ?? {};
  562. state.orderVisitModel = result?.orderVisitModel ?? {};
  563. state.recordingAbsolutePath = result?.recordingAbsolutePath ?? '';
  564. state.smartRecord = result?.orderVisitModel.recordUrl ?? '';
  565. aiVisitVoiceBaseUrl.value = result.aiVisitVoiceBaseUrl;
  566. if (result?.orderVisitModel?.isPutThrough !== null) {
  567. state.ruleForm.isPutThrough = !result?.orderVisitModel?.isPutThrough ?? false;
  568. } else {
  569. state.ruleForm.isPutThrough = false;
  570. }
  571. state.ruleForm.visitDetails = result?.orderVisitModel?.orderVisitDetails ?? {};
  572. for (let i of state.ruleForm.visitDetails) {
  573. if (i.visitTarget === 20) {
  574. if (i.orgProcessingResults) {
  575. i.orgProcessingResults = {
  576. ...i.orgProcessingResults,
  577. dicDataName: i.orgProcessingResults.value,
  578. dicDataValue: i.orgProcessingResults.key,
  579. };
  580. }
  581. if (i.orgNoSatisfiedReason) {
  582. i.orgNoSatisfiedReason = i.orgNoSatisfiedReason.map((item: any) => {
  583. return {
  584. ...item,
  585. dicDataName: item.value,
  586. dicDataValue: item.key,
  587. };
  588. });
  589. } else {
  590. i.orgNoSatisfiedReason = [];
  591. }
  592. if (i.orgHandledAttitude) {
  593. i.orgHandledAttitude = {
  594. ...i.orgHandledAttitude,
  595. dicDataName: i.orgHandledAttitude.value,
  596. dicDataValue: i.orgHandledAttitude.key,
  597. };
  598. }
  599. }
  600. }
  601. state.loading = false;
  602. } catch (error) {
  603. console.log(error);
  604. state.loading = false;
  605. }
  606. };
  607. const notGetThrough = ref(false);
  608. watch(
  609. () => state.ruleForm.isPutThrough,
  610. (val) => {
  611. notGetThrough.value = !val;
  612. },
  613. { immediate: true, deep: true }
  614. );
  615. // 判断当前工单是否是电话来源
  616. const isTelSource = computed(() => {
  617. return state.orderDetail?.sourceChannelCode === 'RGDH';
  618. });
  619. // 打开弹窗
  620. const openDialog = (row: any, type: string = '回访') => {
  621. if (!row.id || !row) {
  622. ElMessage.warning('传入回访ID不正确');
  623. return;
  624. }
  625. state.dialogVisible = true;
  626. getBaseData(row.id);
  627. dialogTitle.value = type;
  628. };
  629. const disabled = computed(() => {
  630. return ['回访明细', '回访详情'].includes(dialogTitle.value);
  631. });
  632. // 设置抽屉
  633. const dialogRef = ref<RefType>(); // 弹窗ref
  634. const mouseup = () => {
  635. state.transform = dialogRef.value.dialogContentRef.$el.style.transform;
  636. };
  637. // 关闭弹窗
  638. const closeDialog = () => {
  639. state.dialogVisible = false;
  640. };
  641. // 查看人工回访录音文件
  642. const playRecordRef = ref<RefType>();
  643. const recordFile = () => {
  644. playRecordRef.value.openDialog(state.orderDetail.callId)
  645. };
  646. // 查看智能回访录音
  647. const onSmartRecord = () => {
  648. playRecordRef.value.playRecord(aiVisitVoiceBaseUrl.value + state.smartRecord);
  649. };
  650. // 呼叫
  651. const onCall = (phoneNumber: string) => {
  652. callCenterOutbound(phoneNumber);
  653. };
  654. // 选择不满意原因
  655. const selectReason = (val: any, index: number | string) => {
  656. state.ruleForm.visitDetails[index].orgNoSatisfiedReason = val.map((item: any) => {
  657. return {
  658. ...item,
  659. value: item.dicDataName,
  660. key: item.dicDataValue,
  661. };
  662. });
  663. };
  664. const close = () => {
  665. ruleFormRef.value?.clearValidate();
  666. ruleFormRef.value?.resetFields();
  667. };
  668. // 提交
  669. const onSubmit = (formEl: FormInstance | undefined) => {
  670. if (!formEl) return;
  671. formEl.validate((valid: boolean) => {
  672. if (!valid) return;
  673. state.loading = true;
  674. let request = {
  675. ...state.ruleForm,
  676. isPutThrough: !state.ruleForm.isPutThrough,
  677. ...state.visitDetails,
  678. id: visitId.value,
  679. };
  680. if (callId.value) {
  681. request.callId = callId.value;
  682. }
  683. visitOrder(request)
  684. .then(() => {
  685. ElMessage.success('操作成功');
  686. state.loading = false;
  687. closeDialog();
  688. emit('updateList');
  689. })
  690. .catch(() => {
  691. state.loading = false;
  692. });
  693. });
  694. };
  695. // 选中常用意见
  696. const chooseAdvice = (item: any, index: number | string) => {
  697. if (state.ruleForm.visitDetails[index].visitContent === null) {
  698. state.ruleForm.visitDetails[index].visitContent = '';
  699. }
  700. state.ruleForm.visitDetails[index].visitContent += item.content;
  701. }
  702. // 重办
  703. const visitRedoRef = ref<RefType>();
  704. const onRedo = ()=>{
  705. visitRedoRef.value.openDialog(state.orderDetail)
  706. }
  707. const queryList = ()=>{
  708. }
  709. // 表格配置项
  710. const columns = ref<any[]>([
  711. { prop: 'order.no', label: '语音评价' },
  712. { prop: 'order.isProvinceText', label: '话务员评价'},
  713. { prop: 'order.title', label: '部门名称'},
  714. { prop: 'order.sourceChannel', label: '部门办件结果' },
  715. { prop: 'visitStateText', label: '部门办件态度' },
  716. { prop: 'visitTypeText', label: '部门评价内容'},
  717. {
  718. prop: 'visitTime',
  719. label: '回访时间',
  720. minWidth: 160,
  721. render: (scope) => {
  722. return <span>{formatDate(scope.row.visitTime, 'YYYY-mm-dd HH:MM:SS')}</span>;
  723. },
  724. },
  725. ]);
  726. const callId = ref<string>('');
  727. onMounted(() => {
  728. mittBus.on('outboundConnect', (data) => {
  729. console.log(data, '外呼已经接通辣');
  730. if (data.callNumber === state.orderDetail.contact) callId.value = data.callId;
  731. });
  732. });
  733. onBeforeUnmount(() => {
  734. mittBus.off('outboundConnect');
  735. });
  736. defineExpose({
  737. openDialog,
  738. closeDialog,
  739. });
  740. </script>
  741. <style lang="scss" scoped>
  742. .collapse-box {
  743. :deep(.el-collapse-item__header) {
  744. background-color: var(--hotline-bg-main-color);
  745. height: 40px;
  746. border-radius: var(--el-border-radius-base);
  747. }
  748. :deep(.el-collapse-item__content) {
  749. padding-bottom: 10px !important;
  750. }
  751. .collapse-container {
  752. padding: 10px;
  753. .plug-container {
  754. border: var(--el-border);
  755. border-radius: var(--el-border-radius-base);
  756. margin-bottom: 15px;
  757. &:last-child {
  758. margin-bottom: 0;
  759. }
  760. .plug-container-title {
  761. padding: 10px 15px;
  762. font-weight: bold;
  763. border-bottom: var(--el-border);
  764. font-size: var(--el-font-size-medium);
  765. }
  766. }
  767. }
  768. }
  769. </style>
  770. <style lang="scss">
  771. .demo-tabs-form {
  772. .title {
  773. font-size: var(--el-font-size-medium);
  774. padding: 10px 15px 20px 15px;
  775. }
  776. }
  777. </style>