detail.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. <template>
  2. <div class="detail">
  3. <div class="side-layout">
  4. <div class="info">
  5. <div class="title">
  6. <p>工单</p>
  7. <h3>
  8. {{ detail.name }}
  9. <span v-if="detail.orderStatus == '30'">
  10. <span>
  11. <el-button
  12. v-if="
  13. detail.orderTypeName == '产品试用申请(硬件)' || detail.orderTypeName == '产品试用申请(软件)'
  14. "
  15. v-permissions="['work:workOrder:try:changeTime']"
  16. @click="handleChangeTime()">
  17. 改期
  18. </el-button>
  19. <el-button
  20. v-if="detail.orderTypeName == '技术文件支持'"
  21. v-permissions="['work:workOrder:support:changeTime']"
  22. @click="handleChangeTime()">
  23. 改期
  24. </el-button>
  25. <el-button
  26. v-if="detail.orderTypeName == '售前讲解支持'"
  27. v-permissions="['work:workOrder:sale:changeTime']"
  28. @click="handleChangeTime()">
  29. 改期
  30. </el-button>
  31. </span>
  32. <!--支持人员反馈(总结)-->
  33. <span>
  34. <el-button
  35. v-if="
  36. !detail.feedbackSupportBy &&
  37. (detail.orderTypeName == '技术文件支持' || detail.orderTypeName == '售前讲解支持')
  38. "
  39. v-permissions="['work:workOrder:feedback:support']"
  40. @click="openFeedBack('support')">
  41. 支持人员总结
  42. </el-button>
  43. </span>
  44. <!--销售反馈-->
  45. <span>
  46. <el-button
  47. v-if="
  48. detail.feedbackSupportBy &&
  49. !detail.feedbackSaleBy &&
  50. (detail.orderTypeName == '技术文件支持' || detail.orderTypeName == '售前讲解支持')
  51. "
  52. v-permissions="['work:workOrder:feedback:sale']"
  53. @click="openFeedBack('sale')">
  54. 销售反馈
  55. </el-button>
  56. </span>
  57. <span
  58. v-if="detail.orderTypeName == '产品试用申请(硬件)' || detail.orderTypeName == '产品试用申请(软件)'">
  59. <!--试用启动反馈-->
  60. <el-button
  61. v-if="!detail.feedbackTrail"
  62. v-permissions="['work:workOrder:feedback:start']"
  63. @click="openFeedBack('start')">
  64. 试用启动反馈
  65. </el-button>
  66. <!--试用过程反馈-->
  67. <el-button
  68. v-if="detail.feedbackTrail && !detail.feedbackTrail.find((item) => item.feedbackTrialType == 30)"
  69. v-permissions="['work:workOrder:feedback:process']"
  70. @click="openFeedBack('process')">
  71. 试用过程反馈
  72. </el-button>
  73. <!--试用总结反馈-->
  74. <el-button
  75. v-if="
  76. detail.feedbackTrail &&
  77. detail.feedbackTrail.length > 1 &&
  78. !detail.feedbackTrail.find((item) => item.feedbackTrialType == 30)
  79. "
  80. v-permissions="['work:workOrder:feedback:summarize']"
  81. @click="openFeedBack('summarize')">
  82. 试用总结反馈
  83. </el-button>
  84. </span>
  85. </span>
  86. </h3>
  87. </div>
  88. <header>
  89. <el-descriptions :colon="false" :column="6" direction="vertical" style="padding-top: 15px">
  90. <el-descriptions-item content-class-name="my-content" label="名称" label-class-name="my-label">
  91. {{ detail.name }}
  92. </el-descriptions-item>
  93. <el-descriptions-item content-class-name="my-content" label="工单类型" label-class-name="my-label">
  94. {{ detail.orderTypeName }}
  95. </el-descriptions-item>
  96. <el-descriptions-item content-class-name="my-content" label="工单状态" label-class-name="my-label">
  97. {{ selectDictLabel(orderStatusOptions, detail.orderStatus) }}
  98. </el-descriptions-item>
  99. <el-descriptions-item content-class-name="my-content" label="分派人员" label-class-name="my-label">
  100. {{ detail.assignUserName }}
  101. </el-descriptions-item>
  102. <el-descriptions-item content-class-name="my-content" label="创建人" label-class-name="my-label">
  103. {{ detail.createdName }}
  104. </el-descriptions-item>
  105. <el-descriptions-item content-class-name="my-content" label="创建时间" label-class-name="my-label">
  106. {{ parseTime(detail.createdTime, '{y}-{m}-{d}') }}
  107. </el-descriptions-item>
  108. </el-descriptions>
  109. </header>
  110. <el-tabs v-model="activeName" @tab-click="handleClick">
  111. <el-tab-pane label="详细信息" name="detail">
  112. <el-descriptions
  113. border
  114. :column="2"
  115. :content-style="{ width: '35%', 'word-break': 'break-all' }"
  116. :label-style="{ width: '15%' }"
  117. size="small">
  118. <el-descriptions-item
  119. v-for="(item, index) in dingFormData"
  120. v-show="item.componentName !== 'TableField'"
  121. :key="index"
  122. :label="item.name">
  123. <span v-if="item.name == '试用开始时间'">
  124. {{ parseTime(detail.trialTimeStart, '{y}-{m}-{d} {h}:{i}') }}
  125. </span>
  126. <span v-else-if="item.name == '试用结束时间'">
  127. {{ parseTime(detail.trialTimeEnd, '{y}-{m}-{d} {h}:{i}') }}
  128. </span>
  129. <span v-else-if="item.name == '期望完成时间'">
  130. {{ parseTime(detail.expectTime, '{y}-{m}-{d} {h}:{i}') }}
  131. </span>
  132. <span v-else-if="item.name == '支持时间'">
  133. {{ parseTime(detail.supportTime, '{y}-{m}-{d} {h}:{i}') }}
  134. </span>
  135. <span v-else-if="item.componentName === 'DDDateRangeField'">{{ item.value.join(' 至 ') }}</span>
  136. <span v-else-if="item.componentName === 'DDAttachment'">
  137. {{ item && item.length > 0 ? item[0].fileName : '' }}
  138. </span>
  139. <span v-else>{{ item.value }}</span>
  140. </el-descriptions-item>
  141. </el-descriptions>
  142. <div v-if="dingFormTableData.length">
  143. <h3 style="margin-top: 10px; margin-bottom: 10px">{{ dingFormTableName }}</h3>
  144. <el-table border :data="dingFormTableData">
  145. <el-table-column
  146. v-for="(col, index) in dingFormTableHeader"
  147. :key="index"
  148. align="center"
  149. :label="col.name"
  150. :prop="col.id"
  151. show-overflow-tooltip />
  152. </el-table>
  153. </div>
  154. <div v-if="detail.finishRemark">
  155. <h3 style="margin-top: 10px; margin-bottom: 10px">完成信息</h3>
  156. <el-row>
  157. <el-col :span="24" style="margin-bottom: 10px">
  158. <div>{{ detail.finishByName }}于 {{ detail.finishTime }} 完成</div>
  159. </el-col>
  160. <el-col :span="24">
  161. <div>
  162. {{ detail.finishRemark }}
  163. </div>
  164. </el-col>
  165. </el-row>
  166. </div>
  167. </el-tab-pane>
  168. <!-- <el-tab-pane label="反馈记录" name="feedback">
  169. <feedback-record :detail="detail" />
  170. </el-tab-pane> -->
  171. <el-tab-pane
  172. v-if="detail.orderTypeName == '技术文件支持' || detail.orderTypeName == '售前讲解支持'"
  173. label="支持人员总结"
  174. name="supportFB">
  175. <feedback-record :detail="detail" type="support" />
  176. </el-tab-pane>
  177. <el-tab-pane
  178. v-if="detail.orderTypeName == '技术文件支持' || detail.orderTypeName == '售前讲解支持'"
  179. label="销售反馈"
  180. name="saleFB">
  181. <feedback-record :detail="detail" type="sale" />
  182. </el-tab-pane>
  183. <el-tab-pane
  184. v-if="detail.orderTypeName == '产品试用申请(硬件)' || detail.orderTypeName == '产品试用申请(软件)'"
  185. label="试用反馈"
  186. name="try">
  187. <feedback-record :detail="detail" type="try" />
  188. </el-tab-pane>
  189. </el-tabs>
  190. </div>
  191. <div class="info-side">
  192. <ul class="records">
  193. <li v-for="(value, key) in records" :key="key">
  194. <div class="date">
  195. <h2>{{ key.split('-')[2] }}</h2>
  196. <h3>{{ key.split('-').splice(0, 2).join('.') }}</h3>
  197. </div>
  198. <ul class="content">
  199. <li v-for="(item, index) in records[key]" :key="index">
  200. <!-- <el-avatar class="user-avatar"
  201. :src="avatar" /> -->
  202. <vab-icon class="user-avatar" icon="account-circle-fill" />
  203. <div class="text">
  204. <p class="action">{{ item.opnPeople }} {{ item.opnType }}</p>
  205. <div class="feedback-content">{{ JSON.parse(item.opnContent).content }}</div>
  206. <p>{{ item.opnDate }}</p>
  207. </div>
  208. </li>
  209. </ul>
  210. </li>
  211. </ul>
  212. <el-input
  213. v-model="feedBackContent"
  214. maxlength="30"
  215. placeholder="请输入反馈内容"
  216. show-word-limit
  217. style="margin-bottom: 7px"
  218. type="textarea" />
  219. <el-row>
  220. <el-col style="text-align: right">
  221. <el-button type="primary" @click="handleSubFeedBack">提交反馈</el-button>
  222. </el-col>
  223. </el-row>
  224. </div>
  225. </div>
  226. <support-feedback ref="supportFB" @update-detail="getDetail" />
  227. <sale-feedback ref="saleFB" @update-detail="getDetail" />
  228. <try-feedback ref="tryFB" @update-detail="getDetail" />
  229. <change-time ref="changeTime" @update-detail="getDetail" />
  230. </div>
  231. </template>
  232. <script>
  233. import to from 'await-to-js'
  234. import api from '@/api/work/index'
  235. import SaleFeedback from '@/views/work/order/components/SaleFeedback'
  236. import SupportFeedback from '@/views/work/order/components/SupportFeedback'
  237. import TryFeedback from '@/views/work/order/components/TryFeedback'
  238. import FeedbackRecord from './components/FeedbackRecord'
  239. import ChangeTime from './components/ChangeTime'
  240. export default {
  241. name: 'WorkOrderDetail',
  242. components: { FeedbackRecord, SaleFeedback, SupportFeedback, TryFeedback, ChangeTime },
  243. data() {
  244. return {
  245. feedBackContent: '',
  246. id: undefined,
  247. activeName: 'detail',
  248. detail: {},
  249. dingFormData: {},
  250. dingFormTableName: [],
  251. dingFormTableHeader: [],
  252. dingFormTableData: [],
  253. orderStatusOptions: [],
  254. records: [],
  255. }
  256. },
  257. mounted() {
  258. this.id = parseInt(this.$route.query.id)
  259. this.getOptions()
  260. this.getDynamics()
  261. this.getDetail()
  262. },
  263. methods: {
  264. handleChangeTime() {
  265. this.$refs.changeTime.open(this.detail)
  266. },
  267. // 打开不同的反馈组件
  268. openFeedBack(type = null) {
  269. if (type == 'support') {
  270. this.$refs.supportFB.open(this.detail)
  271. } else if (type == 'sale') {
  272. this.$refs.saleFB.open(this.detail)
  273. } else {
  274. this.$refs.tryFB.open(this.detail, type)
  275. }
  276. },
  277. async getDynamics() {
  278. const [err, res] = await to(api.dynamicsList({ orderId: this.id }))
  279. if (err) return
  280. if (res.data.list) {
  281. let obj = res.data.list
  282. const keys = Object.keys(obj).reverse()
  283. let records = {}
  284. for (const item of keys) {
  285. records[item] = obj[item]
  286. }
  287. this.records = records
  288. }
  289. },
  290. async handleSubFeedBack() {
  291. let params = {
  292. content: this.feedBackContent,
  293. orderId: this.id,
  294. }
  295. const [err, res] = await to(api.addDynamics({ ...params }))
  296. if (err) return
  297. console.log(res)
  298. if (res.code == 200) {
  299. this.feedBackContent = ''
  300. this.$message({
  301. type: 'success',
  302. message: '提交成功!',
  303. })
  304. this.getDynamics()
  305. }
  306. },
  307. getOptions() {
  308. Promise.all([this.getDicts('work_order_status')])
  309. .then(([workOrderStatus]) => {
  310. this.orderStatusOptions = workOrderStatus.data.values || []
  311. })
  312. .catch((err) => console.log(err))
  313. },
  314. async getDetail() {
  315. const [err, res] = await to(api.getDetail({ id: this.id }))
  316. if (err) return
  317. if (res.code == 200) {
  318. this.detail = res.data
  319. this.dingFormData = JSON.parse(this.detail.formData)
  320. this.checkDingFormType()
  321. }
  322. },
  323. checkDingFormType() {
  324. for (let item of this.dingFormData) {
  325. if (item.componentName === 'TableField') {
  326. this.dingFormTableName = item.name
  327. if (item.value.length === 0) {
  328. continue
  329. }
  330. this.dingFormTableHeader = item.value[0]
  331. this.dingFormTableData = []
  332. let tableData = []
  333. for (let row of item.value) {
  334. let obj = {}
  335. for (let col of row) {
  336. obj[col.id] = col.value
  337. }
  338. tableData.push(obj)
  339. }
  340. this.dingFormTableData = tableData
  341. }
  342. }
  343. this.dingFormData = this.dingFormData.filter((item) => item.componentName !== 'TableField')
  344. },
  345. async handleClick(tab) {
  346. if (tab.name === 'feedback') {
  347. // await this.$refs.feedback.fetchData()
  348. } else if (tab.name === 'worksheet') {
  349. return
  350. } else {
  351. return
  352. }
  353. },
  354. },
  355. }
  356. </script>
  357. <style lang="scss" scoped>
  358. $base: '.detail';
  359. #{$base} {
  360. height: calc(100vh - 60px - 12px * 2 - 40px);
  361. display: flex;
  362. padding: 20px 40px;
  363. > .el-row {
  364. flex: 1;
  365. > .el-col {
  366. height: 100%;
  367. }
  368. }
  369. .title {
  370. p,
  371. h3 {
  372. margin: 0;
  373. }
  374. p {
  375. font-size: 14px;
  376. font-weight: 400;
  377. line-height: 22px;
  378. }
  379. h3 {
  380. font-size: 24px;
  381. font-weight: 500;
  382. line-height: 36px;
  383. color: #333;
  384. display: flex;
  385. justify-content: space-between;
  386. }
  387. }
  388. header {
  389. height: 74px;
  390. background: rgba(196, 196, 196, 0.5);
  391. border-radius: 4px;
  392. display: flex;
  393. align-items: center;
  394. padding: 0 20px;
  395. margin-top: 16px;
  396. ::v-deep .el-descriptions__body {
  397. background: transparent;
  398. }
  399. ::v-deep .my-label {
  400. font-size: 14px;
  401. font-weight: 600;
  402. color: #1d66dc;
  403. }
  404. ::v-deep .my-content {
  405. font-size: 14px;
  406. font-weight: 600;
  407. color: #333;
  408. white-space: nowrap;
  409. overflow: hidden;
  410. text-overflow: ellipsis;
  411. }
  412. }
  413. .el-tabs {
  414. height: calc(100% - 148px);
  415. display: flex;
  416. flex-direction: column;
  417. ::v-deep .el-tabs__content {
  418. flex: 1;
  419. .el-tab-pane {
  420. height: 100%;
  421. }
  422. }
  423. }
  424. .buttons {
  425. padding-top: 28px;
  426. text-align: right;
  427. }
  428. .records {
  429. margin: 0;
  430. padding: 10px 20px;
  431. list-style: none;
  432. height: calc(100% - 90px);
  433. overflow-y: auto;
  434. > li {
  435. display: flex;
  436. & + li {
  437. margin-top: 10px;
  438. }
  439. }
  440. .date {
  441. width: 100px;
  442. display: flex;
  443. flex-direction: column;
  444. align-items: center;
  445. h2,
  446. h3 {
  447. margin: 0;
  448. }
  449. h2 {
  450. font-size: 26px;
  451. line-height: 32px;
  452. }
  453. }
  454. .content {
  455. flex: 1;
  456. list-style: none;
  457. li {
  458. display: flex;
  459. & + li {
  460. margin-top: 10px;
  461. }
  462. }
  463. .user-avatar {
  464. font-size: 40px;
  465. }
  466. .text {
  467. flex: 1;
  468. padding-left: 20px;
  469. p {
  470. font-weight: 500;
  471. margin: 0;
  472. line-height: 20px;
  473. span {
  474. color: #1d66dc;
  475. }
  476. }
  477. p:nth-child(2) {
  478. margin-bottom: 10px;
  479. }
  480. .action {
  481. font-weight: bold;
  482. color: #333;
  483. }
  484. }
  485. }
  486. }
  487. .feedback-content {
  488. margin: 10px 0;
  489. color: #000;
  490. }
  491. }
  492. </style>