vue2-client 1.2.48 → 1.2.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,844 +1,853 @@
1
- <template>
2
- <a-drawer
3
- title="工单详情"
4
- placement="right"
5
- :width="isMobile ? screenWidth : screenWidth * 0.85"
6
- :visible="visible"
7
- @close="onClose"
8
- >
9
- <!-- 移交工单对话框 -->
10
- <a-modal
11
- title="移交工单"
12
- :visible="transVisible"
13
- @ok="handleTransferOk"
14
- @cancel="handleTransferCancel"
15
- :zIndex="1001"
16
- >
17
- <a-input placeholder="请输入要转移工单的员工名" v-model="transferTo" @blur="checkEmp" @change="inputChange"/>
18
- <div class="nameOptions" v-show="nameOptionsVisible">
19
- <p class="nameOptionsItem" v-for="(item,index) in nameOptionsForChoose" :key="index" @click="chooseName(item.name)">{{ item.name }}</p>
20
- </div>
21
- <span v-if="!transferCheck" style="color: red">您输入的员工不存在,请检查后再试</span>
22
- <span v-else style="color: green">您输入的员工已确认,可以进行工单转移</span>
23
- <a-form-model-item label="留言">
24
- <a-input v-model="note" type="textarea" />
25
- </a-form-model-item>
26
- <div class="clearfix">
27
- <a-upload
28
- name="avatar"
29
- list-type="picture-card"
30
- :before-upload="beforeUpload"
31
- action="/webmeteruploadapi/upload"
32
- :file-list="fileList"
33
- @preview="handlePreview"
34
- @change="handleChange"
35
- :remove="remove"
36
- >
37
- <div v-if="fileList.length < 5">
38
- <a-icon type="plus" />
39
- <div class="ant-upload-text">点击上传</div>
40
- </div>
41
- </a-upload>
42
- <a-modal
43
- :visible="previewVisible"
44
- :footer="null"
45
- @cancel="handleCancel"
46
- >
47
- <img style="width: 100%" :src="previewImage" alt="图片上传"/>
48
- </a-modal>
49
- </div>
50
- </a-modal>
51
- <!-- 关闭工单确认框 -->
52
- <a-modal
53
- title="关闭工单"
54
- :visible="closeVisible"
55
- @ok="handleCloseOk"
56
- @cancel="handleCloseCancel"
57
- :zIndex="1002"
58
- >
59
- <p>是否确认手动关闭工单?</p>
60
- <p style="color: red">(该操作不可撤销,请谨慎操作!)</p>
61
- </a-modal>
62
- <!-- 信息主体 -->
63
- <a-spin :spinning="loadTicketDetails">
64
- <a-page-header :title="'订单:' + this.details.serial_number">
65
- <div class="row">
66
- <div class="content">
67
- <a-descriptions size="small" :column="isMobile ? 1 : 2">
68
- <a-descriptions-item label="优先级">
69
- <a-alert
70
- :type="showPriorityColorControl()"
71
- :message="showPriority()"
72
- :show-icon="true"/>
73
- </a-descriptions-item>
74
- <a-descriptions-item label="发起人">{{ details.uploader }}</a-descriptions-item>
75
- <a-descriptions-item label="问题类型">{{ details.category }}</a-descriptions-item>
76
- <a-descriptions-item label="发起人联系方式">{{ details.uploader_phone }}</a-descriptions-item>
77
- </a-descriptions>
78
- </div>
79
- </div>
80
- <!-- 按钮 -->
81
- <template v-slot:extra>
82
- <a-button-group style="margin-right: 4px;">
83
- <a-popover title="开始处理" placement="bottomLeft">
84
- <template slot="content">
85
- <p>开始处理当前工单</p>
86
- </template>
87
- <a-button type="primary" @click="confirmTicketBtn()" :disabled="handlerBtnDisable()">开始处理</a-button>
88
- </a-popover>
89
- <a-popover title="移交他人" placement="bottomLeft">
90
- <template slot="content">
91
- <p>将工单转交他人处理</p>
92
- </template>
93
- <a-button type="primary" @click="transferBtn()" :disabled="transferBtnDisable()">移交他人</a-button>
94
- </a-popover>
95
- <a-popover title="关闭工单" placement="bottomLeft">
96
- <template slot="content">
97
- <p>强制关闭当前工单</p>
98
- <p style="color: red">此操作不可撤销,请谨慎使用</p>
99
- </template>
100
- <a-button type="danger" @click="closeBtn()" :disabled="closeBtnDisable()">关闭工单</a-button>
101
- </a-popover>
102
- </a-button-group>
103
- <a-button-group style="margin-right: 4px;">
104
- <a-button type="dashed" @click="initView" :loading="showSpin">刷新({{ btnCountdownText }})</a-button>
105
- </a-button-group>
106
- </template>
107
- <!-- 进度条 -->
108
- <a-card :bordered="false" style="margin-top: 40px">
109
- <a-steps :direction="'horizontal'" :current="step">
110
- <a-step>
111
- <template v-slot:title><span>待处理</span></template>
112
- <template v-slot:description>
113
- <div>工单已提交,等待处理<div>{{ format(details.created_time,'yyyy-MM-dd hh:mm:ss') }}</div></div>
114
- </template>
115
- </a-step>
116
- <a-step>
117
- <template v-slot:title><span>处理中</span></template>
118
- <template v-slot:description>
119
- <div v-if="step >= 1"><strong>{{ details.name }}</strong> 处理中...<div>{{ format(details.confirm_time,'yyyy-MM-dd hh:mm:ss') }}</div></div>
120
- </template>
121
- </a-step>
122
- <a-step>
123
- <template v-slot:title><span>{{ getStatus() }}</span></template>
124
- <template v-slot:description>
125
- <div v-if="step >= 2">{{ format(details.finished_time,'yyyy-MM-dd hh:mm:ss') }}</div>
126
- </template>
127
- </a-step>
128
- </a-steps>
129
- </a-card>
130
- <!-- 切换栏 -->
131
- <a-tabs style="margin-top: 50px;">
132
- <!-- 问题描述 -->
133
- <a-tab-pane :key="1" tab="问题详细描述">
134
- <a-card style="width: 100%" :loading="descriptionLoading">
135
- <div v-if="addOnDescription.length > 0" v-for="(item,index) in addOnDescription" :key="index">
136
- <span style="font-size: 2em;margin-right: 20px;margin-top: 20px">{{ details.uploader }}</span>
137
- <span>{{ format(item.time,'yyyy年MM月dd日 hh:mm:ss') }}</span>
138
- <p style="text-indent: 2em;font-size: 20px;margin: 20px">{{ item.description }}</p>
139
- <img
140
- v-for="(pic,m) in item.images"
141
- :src="'data:image/png;base64,' + pic.url"
142
- @click="changePhotoClass(pic.id)"
143
- :key="'is' + m"
144
- :class="changePhotoClassForSmall(pic.id)"
145
- >
146
- <div style="height: 1px;background: -webkit-linear-gradient(left, #fff -4%,#6b6c72 50%,#fff 100%);"></div>
147
- </div>
148
- <span style="font-size: 2em;margin-right: 20px">{{ details.uploader }}</span>
149
- <span>{{ format(details.created_time,'yyyy年MM月dd日 hh:mm:ss') }}</span>
150
- <p style="text-indent: 2em;font-size: 20px;margin: 10px">{{ details.description }}</p>
151
- <img
152
- v-for="(originalPic,n) in originalImages"
153
- :src="'data:image/png;base64,' + originalPic.url"
154
- @click="changePhotoClass(originalPic.id)"
155
- :key="'os' + n"
156
- :class="changePhotoClassForSmall(originalPic.id)"
157
- style="margin-bottom: 10px"
158
- >
159
- </a-card>
160
- </a-tab-pane>
161
- <!-- 工单流转历史 -->
162
- <a-tab-pane :key="2" tab="工单流转历史">
163
- <ticket-details-flow
164
- @imageClick="flowImageClick"
165
- v-if="details.id"
166
- :ticketId="ticketId"
167
- :disableCloseBtn="disableCloseBtn"
168
- :id="details.id"/>
169
- </a-tab-pane>
170
- </a-tabs>
171
- </a-page-header>
172
- </a-spin>
173
- <audio controls="controls" hidden src="@vue2-client/assets/sound/newNote.mp3" ref="audio"></audio>
174
- <!-- 放大后图片 -->
175
- <div class="imgBackground" v-if="selectedImageShow" @click="changePhotoClass(selectedImage.id)">
176
- <img
177
- :src="'data:image/png;base64,' + selectedImage.url"
178
- class="img_xl">
179
- </div>
180
- </a-drawer>
181
- </template>
182
-
183
- <script>
184
- import JsonViewer from 'vue-json-viewer'
185
- import { formatDate } from '@vue2-client/utils/util'
186
- import { TicketDetailsViewApi, EmployeeDetailsViewApi, post } from '@vue2-client/services/api'
187
- import XTable from '@vue2-client/base-client/components/common/XTable/XTable'
188
- import { mapState } from 'vuex'
189
- import TicketDetailsFlow from './part/TicketDetailsFlow'
190
- import moment from 'moment'
191
-
192
- function getBase64 (file) {
193
- return new Promise((resolve, reject) => {
194
- const reader = new FileReader()
195
- reader.readAsDataURL(file)
196
- reader.onload = () => resolve(reader.result)
197
- reader.onerror = error => reject(error)
198
- })
199
- }
200
-
201
- export default {
202
- name: 'TicketDetailsView',
203
- components: {
204
- TicketDetailsFlow,
205
- JsonViewer,
206
- XTable
207
- },
208
- data () {
209
- return {
210
- moment,
211
- // 页面宽度
212
- screenWidth: document.documentElement.clientWidth,
213
- // 控制成功页面显示
214
- successVisible: false,
215
- // 控制预览显示
216
- previewVisible: false,
217
- // 图片真实地址
218
- previewImage: '',
219
- // 图片列表
220
- fileList: [],
221
- // 工单详情
222
- details: {
223
- // 当前负责人ID
224
- id: undefined,
225
- name: '',
226
- uploader: '',
227
- status: undefined,
228
- priority: undefined,
229
- description: '',
230
- created_time: '',
231
- confirm_time: '',
232
- finished_time: '',
233
- category: '',
234
- serial_number: '',
235
- uploader_phone: ''
236
- },
237
- images: [],
238
- selectedImage: null,
239
- selectedImageShow: false,
240
- // 当前步骤
241
- step: 0,
242
- // 校验转移员工是否存在
243
- transferCheck: false,
244
- // 移交工单窗口可见性
245
- transVisible: false,
246
- // 转移工单对象
247
- transferTo: '',
248
- // 新增留言
249
- note: '',
250
- // 控制关闭订单按钮可用性
251
- disableCloseBtn: false,
252
- // 控制关闭工单确认框显示
253
- closeVisible: false,
254
- // 控制加载过程
255
- loadTicketDetails: false,
256
- // 附加描述
257
- addOnDescription: [],
258
- // 上传工单时自带照片
259
- originalImages: [],
260
- // 定时刷新定时器
261
- timer: undefined,
262
- // 所有员工姓名
263
- nameOptions: [],
264
- // 员工姓名备选
265
- nameOptionsForChoose: [],
266
- // 员工姓名备选项显示控制
267
- nameOptionsVisible: false,
268
- // 工单流转抽屉可见性
269
- workFlowVisible: false,
270
- // 控制小加载指示物显示
271
- showSpin: true,
272
- // 原始描述长度
273
- addOnDescriptionOriginal: undefined,
274
- // 描述加载控制
275
- descriptionLoading: false,
276
- // 用于刷新按钮倒计时显示
277
- btnCountdownText: 5,
278
- // 当前登陆用户在数据库表中id,临时使用,之后会替换
279
- empIdInDataBase: undefined
280
- }
281
- },
282
- mounted () {
283
- this.initView()
284
- },
285
- computed: {
286
- ...mapState('account', { currUser: 'user' }),
287
- ...mapState('setting', ['isMobile'])
288
- },
289
- props: {
290
- ticketId: {
291
- type: String,
292
- required: true
293
- },
294
- visible: {
295
- type: Boolean,
296
- default: false
297
- }
298
- },
299
- methods: {
300
- // 转移员工姓名change事件,用于展示备选
301
- inputChange () {
302
- if (this.transferTo.length === 0) {
303
- this.nameOptionsVisible = false
304
- } else {
305
- this.nameOptionsVisible = true
306
- var inputName = this.transferTo
307
- this.nameOptionsForChoose = this.nameOptions.filter(function (element) {
308
- return element.name.indexOf(inputName) !== -1
309
- })
310
- }
311
- },
312
- // 在候选框中点击了名称
313
- chooseName (name) {
314
- this.nameOptionsVisible = false
315
- this.transferTo = name
316
- this.checkEmp()
317
- },
318
- // 获取所有员工名,供输入框备选
319
- getAllEmpNames () {
320
- return post(EmployeeDetailsViewApi.getAllEmployeeName, null)
321
- .then(res => {
322
- this.nameOptions = res
323
- }, err => {
324
- console.log(err)
325
- })
326
- },
327
- // 图像修改检测
328
- handleChange ({ fileList }) {
329
- this.fileList = fileList.filter((item) => {
330
- return item.status === 'done' || item.status === 'uploading'
331
- })
332
- },
333
- // 在上传页面,点击图片上的垃圾桶,撤销上传
334
- remove (file) {
335
- return post(TicketDetailsViewApi.revocationImage, {
336
- file: file
337
- })
338
- .then(res => {
339
- if (res === 'success') {
340
- this.$message.success('删除成功')
341
- }
342
- this.fileList = this.fileList.filter((item) => {
343
- return item.response.name !== file.response.name
344
- })
345
- }, err => {
346
- console.log(err)
347
- })
348
- },
349
- // 上传头像前校验
350
- beforeUpload (file) {
351
- const isJpgOrPng =
352
- file.type === 'image/jpeg' ||
353
- file.type === 'image/jpg' ||
354
- file.type === 'image/png'
355
- const Lt2M = file.size / 1024 / 1024 < 2
356
- if (!Lt2M) {
357
- this.$message.error('图片不得大于2MB!')
358
- }
359
- if (!isJpgOrPng) {
360
- this.$message.error('只能上传jpg/png格式的图片')
361
- }
362
- return isJpgOrPng && Lt2M
363
- },
364
- handleCancel () {
365
- this.previewVisible = false
366
- },
367
- // 处理预览图像
368
- async handlePreview (file) {
369
- if (!file.url && !file.preview) {
370
- file.preview = await getBase64(file.originFileObj)
371
- }
372
- this.previewImage = file.url || file.preview
373
- this.previewVisible = true
374
- },
375
- // 开启定时器
376
- setTimer () {
377
- if (this.timer === undefined) {
378
- this.timer = setInterval(() => {
379
- if (this.btnCountdownText > 0) {
380
- this.btnCountdownText--
381
- if (this.btnCountdownText === 0) {
382
- this.initView()
383
- this.btnCountdownText = 5
384
- }
385
- }
386
- }, 1000)
387
- }
388
- },
389
- // 初始化组件
390
- initView () {
391
- this.showSpin = true
392
- this.descriptionLoading = true
393
- // 获取所有员工名
394
- this.getAllEmpNames()
395
- // 获取工单的基本详情
396
- this.getTicketDetail(this.ticketId)
397
- // 获取工单的附加信息
398
- this.handleOtherData()
399
- },
400
- // 图像点击切换放大缩小
401
- changePhotoClass (id) {
402
- for (let i = 0; i < this.images.length; i++) {
403
- if (id === this.images[i].id) {
404
- this.images[i].isLarge = !this.images[i].isLarge
405
- this.selectedImage = this.images[i]
406
- }
407
- }
408
- this.selectedImageShow = !this.selectedImageShow
409
- },
410
- // 控制未放大图像变暗
411
- changePhotoClassForSmall (id) {
412
- for (let i = 0; i < this.images.length; i++) {
413
- if (id == this.images[i].id) {
414
- if (this.images[i].isLarge === true) {
415
- return 'img_sm_dark'
416
- } else {
417
- return 'img_sm'
418
- }
419
- }
420
- }
421
- },
422
- // 获取当前相关联的照片
423
- getPhoto () {
424
- return post(TicketDetailsViewApi.getTicketImages, {
425
- ticketId: this.ticketId
426
- })
427
- .then(res => {
428
- this.images = res
429
- for (let i = 0; i < this.images.length; i++) {
430
- this.images[i].isLarge = false
431
- }
432
- this.mergeAddImage()
433
- }, err => {
434
- console.log(err)
435
- })
436
- },
437
- // 将同一次上传的图片,与描述放在一起
438
- mergeAddImage () {
439
- this.originalImages = []
440
- for (let i = 0; i < this.addOnDescription.length; i++) {
441
- this.addOnDescription[i].images = []
442
- }
443
- for (let i = 0; i < this.images.length; i++) {
444
- if (this.images[i].did === 0) {
445
- this.originalImages.push(this.images[i])
446
- continue
447
- }
448
- for (let j = 0; j < this.addOnDescription.length; j++) {
449
- if (this.images[i].did === this.addOnDescription[j].id) {
450
- this.addOnDescription[j].images.push(this.images[i])
451
- }
452
- }
453
- }
454
- this.initDone()
455
- },
456
- initDone () {
457
- // 初始化信息
458
- this.showSpin = false
459
- this.descriptionLoading = false
460
- if (this.step !== 3) {
461
- this.setTimer()
462
- }
463
- this.addOnDescriptionOriginal = 1
464
- },
465
- // 关闭工单确认后业务逻辑
466
- handleCloseOk () {
467
- return post(TicketDetailsViewApi.manualCloseTicket, {
468
- ticketId: this.ticketId,
469
- time: this.format(new Date(), 'yyyy-MM-dd hh:mm:ss')
470
- })
471
- .then(res => {
472
- if (res !== 0) {
473
- this.$message.success(
474
- '操作成功',
475
- 5
476
- )
477
- this.initView()
478
- this.closeVisible = false
479
- } else {
480
- this.$message.error(
481
- '操作失败',
482
- 5
483
- )
484
- }
485
- }, err => {
486
- console.error(err)
487
- })
488
- },
489
- // 关闭工单取消后业务逻辑
490
- handleCloseCancel () {
491
- this.closeVisible = false
492
- },
493
- // 确认工单按钮业务逻辑
494
- confirmTicketBtn () {
495
- this.getCurrentEmpId()
496
- return post(TicketDetailsViewApi.confirmTicket, {
497
- ticketId: this.ticketId,
498
- time: this.format(new Date(), 'yyyy-MM-dd hh:mm:ss'),
499
- empId: this.empIdInDataBase
500
- })
501
- .then(res => {
502
- if (res !== 0) {
503
- this.$message.success('操作成功', 5)
504
- } else {
505
- this.$message.error('工单已被他人处理或关闭,请刷新再试', 5)
506
- }
507
- this.initView()
508
- }, err => {
509
- console.error(err)
510
- this.$message.error('工单已被他人处理,请刷新再试', 5)
511
- this.initView()
512
- })
513
- },
514
- // 获取当前登陆员工ID
515
- getCurrentEmpId () {
516
- return post(EmployeeDetailsViewApi.getEmployeeId, {
517
- name: this.currUser.ename
518
- })
519
- .then(res => {
520
- this.empIdInDataBase = res
521
- }, err => {
522
- console.log(err)
523
- })
524
- },
525
- // 转移工单按钮业务逻辑
526
- transferBtn () {
527
- this.transVisible = true
528
- },
529
- checkEmp () {
530
- return post(EmployeeDetailsViewApi.findEmpName, {
531
- empName: this.transferTo
532
- })
533
- .then(res => {
534
- const num = res
535
- this.transferCheck = num !== 0
536
- }, err => {
537
- console.error(err)
538
- })
539
- },
540
- // 获取工单状态撤销or关闭
541
- getStatus () {
542
- if (this.details.status === 2) {
543
- return '已撤销'
544
- } else {
545
- return '已关闭'
546
- }
547
- },
548
- // 关闭工单按钮业务逻辑
549
- closeBtn () {
550
- this.closeVisible = true
551
- },
552
- // 转移工单确认后逻辑
553
- handleTransferOk () {
554
- if (!this.transferCheck) {
555
- this.$message.error(
556
- '请先检查您输入的员工名',
557
- 2
558
- )
559
- } else {
560
- return post(TicketDetailsViewApi.transferTicketToOthers, {
561
- ticketId: this.ticketId,
562
- endTime: this.format(new Date(), 'yyyy-MM-dd hh:mm:ss'),
563
- transferTo: this.transferTo,
564
- note: this.note,
565
- images: this.fileList
566
- })
567
- .then(res => {
568
- const result = res
569
- this.note = ''
570
- this.transVisible = false
571
- if (result === 1) {
572
- this.$message.success(
573
- '操作成功',
574
- 5
575
- )
576
- this.fileList = []
577
- this.transferTo = ''
578
- this.transferCheck = false
579
- this.initView()
580
- } else {
581
- this.$message.error('工单已被他人处理或关闭,请刷新再试', 5)
582
- }
583
- this.handleCancel()
584
- this.initView()
585
- }, err => {
586
- console.error(err)
587
- this.transVisible = false
588
- })
589
- }
590
- },
591
- // 转移工单取消后逻辑
592
- handleTransferCancel () {
593
- this.transVisible = false
594
- this.transferCheck = false
595
- this.transferTo = ''
596
- this.note = ''
597
- },
598
- // 关闭抽屉时回调
599
- onClose () {
600
- clearInterval(this.timer)
601
- this.timer = undefined
602
- this.addOnDescription = []
603
- this.disableCloseBtn = false
604
- this.$emit('update:visible', false)
605
- this.originalImages = []
606
- this.images = []
607
- this.addOnDescriptionOriginal = undefined
608
- },
609
- // 对其他信息进行初始化,包括附加描述,图片
610
- handleOtherData () {
611
- const beforeLength = this.addOnDescription.length
612
- return post(TicketDetailsViewApi.getAddonDescription, {
613
- ticketId: this.ticketId
614
- })
615
- .then(res => {
616
- this.addOnDescription = res
617
- this.getPhoto()
618
- if (this.addOnDescription.length > beforeLength && this.addOnDescriptionOriginal !== undefined) {
619
- this.addOnDescriptionOriginal = 1
620
- this.$refs.audio.currentTime = 0
621
- this.$refs.audio.play()
622
- this.$notification.open({
623
- message: '最新通知',
624
- description:
625
- '客户追加了问题描述,请及时查看!',
626
- onClick: () => {
627
- }
628
- })
629
- }
630
- }, err => {
631
- console.log(err)
632
- })
633
- },
634
- // 获取工单详情信息
635
- getTicketDetail (num) {
636
- this.showSpin = true
637
- return post(TicketDetailsViewApi.getTicketDetails, {
638
- ticketId: num
639
- }).then(res => {
640
- this.details = res[0]
641
- this.step = res[0].status
642
- if (this.details.status === 3) {
643
- this.disableCloseBtn = true
644
- }
645
- const categoryValue = this.details.category
646
- this.details.category = this.$appdata.getDictionaryList('ticketCategoryMap')[categoryValue].label
647
- }, err => {
648
- console.error(err)
649
- })
650
- },
651
- // 日期格式化
652
- format (date, format) {
653
- return formatDate(date, format)
654
- },
655
- // 转换JSON
656
- toJSON (value) {
657
- try {
658
- return JSON.parse(value)
659
- } catch (e) {
660
- return value
661
- }
662
- },
663
- // 控制开始处理按钮可用状态
664
- handlerBtnDisable () {
665
- return this.step !== 0
666
- },
667
- // 控制移交他人按钮可用状态
668
- transferBtnDisable () {
669
- return this.step === 0 || this.step === 3
670
- },
671
- // 控制关闭工单按钮可用状态
672
- closeBtnDisable () {
673
- return this.step === 0 || this.disableCloseBtn || this.details.name != this.currUser.ename
674
- },
675
- // 优先级文字显示
676
- showPriority () {
677
- if (this.details.priority === 2) {
678
- return '一般'
679
- } else if (this.details.priority === 1) {
680
- return '紧急'
681
- } else {
682
- return '非常紧急'
683
- }
684
- },
685
- // 优先度颜色控制
686
- showPriorityColorControl () {
687
- if (this.details.priority === 2) {
688
- return 'info'
689
- } else if (this.details.priority === 1) {
690
- return 'warning'
691
- } else {
692
- return 'error'
693
- }
694
- },
695
- // 单击工单流转历史图片
696
- flowImageClick (image) {
697
- this.selectedImage = image
698
- this.selectedImageShow = !this.selectedImageShow
699
- }
700
- },
701
- watch: {
702
- 'visible' (val) {
703
- if (val) {
704
- this.initView()
705
- }
706
- },
707
- 'step' (newVal) {
708
- if (newVal === 3) {
709
- clearInterval(this.timer)
710
- }
711
- }
712
- }
713
- }
714
- </script>
715
-
716
- <style lang="less" scoped>
717
- .high-priority{
718
- background-color: rgba(163, 30, 30, 0.66);
719
- border: red solid 1px;
720
- font-size: large;
721
- padding: 5px;
722
- color: white;
723
- border-radius: 10px;
724
- }
725
-
726
- .detail-layout {
727
- margin-left: 44px;
728
- }
729
- .text {
730
- color: rgba(0, 0, 0, .45);
731
- }
732
-
733
- .heading {
734
- color: rgba(0, 0, 0, .85);
735
- font-size: 20px;
736
- }
737
-
738
- .no-data {
739
- color: rgba(0, 0, 0, .25);
740
- text-align: center;
741
- line-height: 64px;
742
- font-size: 16px;
743
-
744
- i {
745
- font-size: 24px;
746
- margin-right: 16px;
747
- position: relative;
748
- top: 3px;
749
- }
750
- }
751
-
752
- .mobile {
753
- .detail-layout {
754
- margin-left: unset;
755
- }
756
- .text {
757
-
758
- }
759
- .status-list {
760
- text-align: left;
761
- }
762
- }
763
-
764
- .row {
765
- display: flex;
766
-
767
- .content {
768
- -webkit-box-flex: 1;
769
- flex: auto;
770
- -ms-flex: auto;
771
- }
772
-
773
- .extra {
774
- flex: 0 1 auto;
775
- -webkit-box-flex: 0;
776
- -ms-flex: 0 1 auto;
777
- min-width: 242px;
778
- margin-left: 88px;
779
- text-align: right;
780
- }
781
- }
782
-
783
- .img_sm {
784
- border: rgba(84, 84, 84, 0.2) solid 1.5px;
785
- border-radius: 5px;
786
- padding: 10px;
787
- width: 160px;
788
- height: 120px;
789
- margin-bottom: 20px;
790
- margin-left: 20px;
791
- }
792
- .img_xl {
793
- position: absolute;
794
- top:0;bottom:0;left:0;right:0;
795
- margin: auto;
796
- width: 60%;
797
- max-width: 1000px;
798
- cursor: zoom-out;
799
- z-index: 9999;
800
- animation: imgZoomIn 0.4s;
801
- -webkit-animation: imgZoomIn 0.4s;
802
- }
803
- @keyframes imgZoomIn
804
- {
805
- from {width: 160px;}
806
- to {width: 60%;}
807
- }
808
- .img_sm:hover {
809
- opacity: 0.6;
810
- cursor: zoom-in;
811
- }
812
- .img_sm_dark{
813
- filter: grayscale(100%);
814
- opacity: 0.6;
815
- border: rgba(84, 84, 84, 0.2) solid 1.5px;
816
- border-radius: 5px;
817
- padding: 10px;
818
- width: 160px;
819
- height: 120px;
820
- margin-bottom: 20px;
821
- margin-left: 20px;
822
- }
823
- .imgBackground {
824
- position: absolute;
825
- top:0;bottom:0;left:0;right:0;
826
- width: 100%;
827
- height: 100%;
828
- z-index: 9998;
829
- background-color: rgba(0,0,0,0.7);
830
- }
831
- .nameOptionsItem {
832
- padding-left: 5px;
833
- margin-bottom: 2px;
834
- }
835
- .nameOptionsItem:hover {
836
- background-color: rgb(209,233,255);
837
- }
838
- .nameOptionsItem:last-of-type {
839
- margin-bottom: 0px;
840
- }
841
- .nameOptions {
842
- border: rgba(0,0,0,0.4) solid 1px;
843
- }
844
- </style>
1
+ <template>
2
+ <a-drawer
3
+ title="工单详情"
4
+ placement="right"
5
+ :width="isMobile ? screenWidth : screenWidth * 0.85"
6
+ :visible="visible"
7
+ @close="onClose"
8
+ >
9
+ <!-- 移交工单对话框 -->
10
+ <a-modal
11
+ title="移交工单"
12
+ :visible="transVisible"
13
+ @ok="handleTransferOk"
14
+ @cancel="handleTransferCancel"
15
+ :zIndex="1001"
16
+ >
17
+ <a-input placeholder="请输入要转移工单的员工名" v-model="transferTo" @blur="checkEmp" @change="inputChange"/>
18
+ <div class="nameOptions" v-show="nameOptionsVisible">
19
+ <p class="nameOptionsItem" v-for="(item,index) in nameOptionsForChoose" :key="index" @click="chooseName(item.name)">{{ item.name }}</p>
20
+ </div>
21
+ <span v-if="!transferCheck" style="color: red">您输入的员工不存在,请检查后再试</span>
22
+ <span v-else style="color: green">您输入的员工已确认,可以进行工单转移</span>
23
+ <a-form-model-item label="留言">
24
+ <a-input v-model="note" type="textarea" />
25
+ </a-form-model-item>
26
+ <div class="clearfix">
27
+ <a-upload
28
+ name="avatar"
29
+ list-type="picture-card"
30
+ :before-upload="beforeUpload"
31
+ action="/webmeteruploadapi/upload"
32
+ :file-list="fileList"
33
+ @preview="handlePreview"
34
+ @change="handleChange"
35
+ :remove="remove"
36
+ >
37
+ <div v-if="fileList.length < 5">
38
+ <a-icon type="plus" />
39
+ <div class="ant-upload-text">点击上传</div>
40
+ </div>
41
+ </a-upload>
42
+ <a-modal
43
+ :visible="previewVisible"
44
+ :footer="null"
45
+ @cancel="handleCancel"
46
+ >
47
+ <img style="width: 100%" :src="previewImage" alt="图片上传"/>
48
+ </a-modal>
49
+ </div>
50
+ </a-modal>
51
+ <!-- 关闭工单确认框 -->
52
+ <a-modal
53
+ title="关闭工单"
54
+ :visible="closeVisible"
55
+ @ok="handleCloseOk"
56
+ @cancel="handleCloseCancel"
57
+ :zIndex="1002"
58
+ >
59
+ <p>是否确认手动关闭工单?</p>
60
+ <p style="color: red">(该操作不可撤销,请谨慎操作!)</p>
61
+ </a-modal>
62
+ <!-- 信息主体 -->
63
+ <a-spin :spinning="loadTicketDetails">
64
+ <a-page-header :title="'订单:' + this.details.serial_number">
65
+ <div class="row">
66
+ <div class="content">
67
+ <a-descriptions size="small" :column="isMobile ? 1 : 2">
68
+ <a-descriptions-item label="优先级">
69
+ <a-alert
70
+ :type="showPriorityColorControl()"
71
+ :message="showPriority()"
72
+ :show-icon="true"/>
73
+ </a-descriptions-item>
74
+ <a-descriptions-item label="发起人">{{ details.uploader }}</a-descriptions-item>
75
+ <a-descriptions-item label="问题类型">{{ details.category }}</a-descriptions-item>
76
+ <a-descriptions-item label="发起人联系方式">{{ details.uploader_phone }}</a-descriptions-item>
77
+ </a-descriptions>
78
+ </div>
79
+ </div>
80
+ <!-- 按钮 -->
81
+ <template v-slot:extra>
82
+ <a-button-group style="margin-right: 4px;">
83
+ <a-popover title="开始处理" placement="bottomLeft">
84
+ <template slot="content">
85
+ <p>开始处理当前工单</p>
86
+ </template>
87
+ <a-button type="primary" @click="confirmTicketBtn()" :disabled="handlerBtnDisable()">开始处理</a-button>
88
+ </a-popover>
89
+ <a-popover title="移交他人" placement="bottomLeft">
90
+ <template slot="content">
91
+ <p>将工单转交他人处理</p>
92
+ </template>
93
+ <a-button type="primary" @click="transferBtn()" :disabled="transferBtnDisable()">移交他人</a-button>
94
+ </a-popover>
95
+ <a-popover title="关闭工单" placement="bottomLeft">
96
+ <template slot="content">
97
+ <p>强制关闭当前工单</p>
98
+ <p style="color: red">此操作不可撤销,请谨慎使用</p>
99
+ </template>
100
+ <a-button type="danger" @click="closeBtn()" :disabled="closeBtnDisable()">关闭工单</a-button>
101
+ </a-popover>
102
+ </a-button-group>
103
+ <a-button-group style="margin-right: 4px;">
104
+ <a-button type="dashed" @click="initView" :loading="showSpin">刷新({{ btnCountdownText }})</a-button>
105
+ </a-button-group>
106
+ </template>
107
+ <!-- 进度条 -->
108
+ <a-card :bordered="false" style="margin-top: 40px">
109
+ <a-steps :direction="'horizontal'" :current="step">
110
+ <a-step>
111
+ <template v-slot:title><span>待处理</span></template>
112
+ <template v-slot:description>
113
+ <div>工单已提交,等待处理<div>{{ format(details.created_time,'yyyy-MM-dd hh:mm:ss') }}</div></div>
114
+ </template>
115
+ </a-step>
116
+ <a-step>
117
+ <template v-slot:title><span>处理中</span></template>
118
+ <template v-slot:description>
119
+ <div v-if="step >= 1"><strong>{{ details.name }}</strong> 处理中...<div>{{ format(details.confirm_time,'yyyy-MM-dd hh:mm:ss') }}</div></div>
120
+ </template>
121
+ </a-step>
122
+ <a-step>
123
+ <template v-slot:title><span>{{ getStatus() }}</span></template>
124
+ <template v-slot:description>
125
+ <div v-if="step >= 2">{{ format(details.finished_time,'yyyy-MM-dd hh:mm:ss') }}</div>
126
+ </template>
127
+ </a-step>
128
+ </a-steps>
129
+ </a-card>
130
+ <!-- 切换栏 -->
131
+ <a-tabs style="margin-top: 50px;">
132
+ <!-- 问题描述 -->
133
+ <a-tab-pane :key="1" tab="问题详细描述">
134
+ <a-card style="width: 100%" :loading="descriptionLoading">
135
+ <div v-if="addOnDescription.length > 0" v-for="(item,index) in addOnDescription" :key="index">
136
+ <span style="font-size: 2em;margin-right: 20px;margin-top: 20px">{{ details.uploader }}</span>
137
+ <span>{{ format(item.time,'yyyy年MM月dd日 hh:mm:ss') }}</span>
138
+ <p style="text-indent: 2em;font-size: 20px;margin: 20px">{{ item.description }}</p>
139
+ <img
140
+ v-for="(pic,m) in item.images"
141
+ :src="'data:image/png;base64,' + pic.url"
142
+ @click="changePhotoClass(pic.id)"
143
+ :key="'is' + m"
144
+ :class="changePhotoClassForSmall(pic.id)"
145
+ >
146
+ <div style="height: 1px;background: -webkit-linear-gradient(left, #fff -4%,#6b6c72 50%,#fff 100%);"></div>
147
+ </div>
148
+ <span style="font-size: 2em;margin-right: 20px">{{ details.uploader }}</span>
149
+ <span>{{ format(details.created_time,'yyyy年MM月dd日 hh:mm:ss') }}</span>
150
+ <p style="text-indent: 2em;font-size: 20px;margin: 10px">{{ details.description }}</p>
151
+ <img
152
+ v-for="(originalPic,n) in originalImages"
153
+ :src="'data:image/png;base64,' + originalPic.url"
154
+ @click="changePhotoClass(originalPic.id)"
155
+ :key="'os' + n"
156
+ :class="changePhotoClassForSmall(originalPic.id)"
157
+ style="margin-bottom: 10px"
158
+ >
159
+ </a-card>
160
+ </a-tab-pane>
161
+ <!-- 工单流转历史 -->
162
+ <a-tab-pane :key="2" tab="工单流转历史">
163
+ <ticket-details-flow
164
+ @imageClick="flowImageClick"
165
+ v-if="details.id"
166
+ :ticketId="ticketId"
167
+ :disableCloseBtn="disableCloseBtn"
168
+ :id="details.id"/>
169
+ </a-tab-pane>
170
+ </a-tabs>
171
+ </a-page-header>
172
+ </a-spin>
173
+ <audio controls="controls" hidden src="@vue2-client/assets/sound/newNote.mp3" ref="audio"></audio>
174
+ <!-- 放大后图片 -->
175
+ <div class="imgBackground" v-if="selectedImageShow" @click="changePhotoClass(selectedImage.id)">
176
+ <img
177
+ :src="'data:image/png;base64,' + selectedImage.url"
178
+ class="img_xl">
179
+ </div>
180
+ </a-drawer>
181
+ </template>
182
+
183
+ <script>
184
+ import JsonViewer from 'vue-json-viewer'
185
+ import { formatDate } from '@vue2-client/utils/util'
186
+ import { TicketDetailsViewApi, EmployeeDetailsViewApi, post } from '@vue2-client/services/api'
187
+ import XTable from '@vue2-client/base-client/components/common/XTable/XTable'
188
+ import { mapState } from 'vuex'
189
+ import TicketDetailsFlow from './part/TicketDetailsFlow'
190
+ import moment from 'moment'
191
+
192
+ function getBase64 (file) {
193
+ return new Promise((resolve, reject) => {
194
+ const reader = new FileReader()
195
+ reader.readAsDataURL(file)
196
+ reader.onload = () => resolve(reader.result)
197
+ reader.onerror = error => reject(error)
198
+ })
199
+ }
200
+
201
+ export default {
202
+ name: 'TicketDetailsView',
203
+ components: {
204
+ TicketDetailsFlow,
205
+ JsonViewer,
206
+ XTable
207
+ },
208
+ data () {
209
+ return {
210
+ moment,
211
+ // 页面宽度
212
+ screenWidth: document.documentElement.clientWidth,
213
+ // 控制成功页面显示
214
+ successVisible: false,
215
+ // 控制预览显示
216
+ previewVisible: false,
217
+ // 图片真实地址
218
+ previewImage: '',
219
+ // 图片列表
220
+ fileList: [],
221
+ // 工单详情
222
+ details: {
223
+ // 当前负责人ID
224
+ id: undefined,
225
+ name: '',
226
+ uploader: '',
227
+ status: undefined,
228
+ priority: undefined,
229
+ description: '',
230
+ created_time: '',
231
+ confirm_time: '',
232
+ finished_time: '',
233
+ category: '',
234
+ serial_number: '',
235
+ uploader_phone: ''
236
+ },
237
+ images: [],
238
+ selectedImage: null,
239
+ selectedImageShow: false,
240
+ // 当前步骤
241
+ step: 0,
242
+ // 校验转移员工是否存在
243
+ transferCheck: false,
244
+ // 移交工单窗口可见性
245
+ transVisible: false,
246
+ // 转移工单对象
247
+ transferTo: '',
248
+ // 新增留言
249
+ note: '',
250
+ // 控制关闭订单按钮可用性
251
+ disableCloseBtn: false,
252
+ // 控制关闭工单确认框显示
253
+ closeVisible: false,
254
+ // 控制加载过程
255
+ loadTicketDetails: false,
256
+ // 附加描述
257
+ addOnDescription: [],
258
+ // 上传工单时自带照片
259
+ originalImages: [],
260
+ // 定时刷新定时器
261
+ timer: undefined,
262
+ // 所有员工姓名
263
+ nameOptions: [],
264
+ // 员工姓名备选
265
+ nameOptionsForChoose: [],
266
+ // 员工姓名备选项显示控制
267
+ nameOptionsVisible: false,
268
+ // 工单流转抽屉可见性
269
+ workFlowVisible: false,
270
+ // 控制小加载指示物显示
271
+ showSpin: true,
272
+ // 原始描述长度
273
+ addOnDescriptionOriginal: undefined,
274
+ // 描述加载控制
275
+ descriptionLoading: false,
276
+ // 用于刷新按钮倒计时显示
277
+ btnCountdownText: 5,
278
+ // 当前登陆用户在数据库表中id,临时使用,之后会替换
279
+ empIdInDataBase: undefined
280
+ }
281
+ },
282
+ mounted () {
283
+ this.initView()
284
+ },
285
+ computed: {
286
+ ...mapState('account', { currUser: 'user' }),
287
+ ...mapState('setting', ['isMobile'])
288
+ },
289
+ props: {
290
+ ticketId: {
291
+ type: String,
292
+ required: true
293
+ },
294
+ visible: {
295
+ type: Boolean,
296
+ default: false
297
+ }
298
+ },
299
+ beforeDestroy () {
300
+ this.stopTimer()
301
+ },
302
+ methods: {
303
+ // 转移员工姓名change事件,用于展示备选
304
+ inputChange () {
305
+ if (this.transferTo.length === 0) {
306
+ this.nameOptionsVisible = false
307
+ } else {
308
+ this.nameOptionsVisible = true
309
+ var inputName = this.transferTo
310
+ this.nameOptionsForChoose = this.nameOptions.filter(function (element) {
311
+ return element.name.indexOf(inputName) !== -1
312
+ })
313
+ }
314
+ },
315
+ // 在候选框中点击了名称
316
+ chooseName (name) {
317
+ this.nameOptionsVisible = false
318
+ this.transferTo = name
319
+ this.checkEmp()
320
+ },
321
+ // 获取所有员工名,供输入框备选
322
+ getAllEmpNames () {
323
+ return post(EmployeeDetailsViewApi.getAllEmployeeName, null)
324
+ .then(res => {
325
+ this.nameOptions = res
326
+ }, err => {
327
+ console.log(err)
328
+ })
329
+ },
330
+ // 图像修改检测
331
+ handleChange ({ fileList }) {
332
+ this.fileList = fileList.filter((item) => {
333
+ return item.status === 'done' || item.status === 'uploading'
334
+ })
335
+ },
336
+ // 在上传页面,点击图片上的垃圾桶,撤销上传
337
+ remove (file) {
338
+ return post(TicketDetailsViewApi.revocationImage, {
339
+ file: file
340
+ })
341
+ .then(res => {
342
+ if (res === 'success') {
343
+ this.$message.success('删除成功')
344
+ }
345
+ this.fileList = this.fileList.filter((item) => {
346
+ return item.response.name !== file.response.name
347
+ })
348
+ }, err => {
349
+ console.log(err)
350
+ })
351
+ },
352
+ // 上传头像前校验
353
+ beforeUpload (file) {
354
+ const isJpgOrPng =
355
+ file.type === 'image/jpeg' ||
356
+ file.type === 'image/jpg' ||
357
+ file.type === 'image/png'
358
+ const Lt2M = file.size / 1024 / 1024 < 2
359
+ if (!Lt2M) {
360
+ this.$message.error('图片不得大于2MB!')
361
+ }
362
+ if (!isJpgOrPng) {
363
+ this.$message.error('只能上传jpg/png格式的图片')
364
+ }
365
+ return isJpgOrPng && Lt2M
366
+ },
367
+ handleCancel () {
368
+ this.previewVisible = false
369
+ },
370
+ // 处理预览图像
371
+ async handlePreview (file) {
372
+ if (!file.url && !file.preview) {
373
+ file.preview = await getBase64(file.originFileObj)
374
+ }
375
+ this.previewImage = file.url || file.preview
376
+ this.previewVisible = true
377
+ },
378
+ // 开启定时器
379
+ setTimer () {
380
+ if (this.timer === undefined) {
381
+ this.timer = setInterval(() => {
382
+ if (this.btnCountdownText > 0) {
383
+ this.btnCountdownText--
384
+ if (this.btnCountdownText === 0) {
385
+ this.initView()
386
+ this.btnCountdownText = 5
387
+ }
388
+ }
389
+ }, 1000)
390
+ }
391
+ },
392
+ // 初始化组件
393
+ initView () {
394
+ this.showSpin = true
395
+ this.descriptionLoading = true
396
+ // 获取所有员工名
397
+ this.getAllEmpNames()
398
+ // 获取工单的基本详情
399
+ this.getTicketDetail(this.ticketId)
400
+ // 获取工单的附加信息
401
+ this.handleOtherData()
402
+ },
403
+ // 图像点击切换放大缩小
404
+ changePhotoClass (id) {
405
+ for (let i = 0; i < this.images.length; i++) {
406
+ if (id === this.images[i].id) {
407
+ this.images[i].isLarge = !this.images[i].isLarge
408
+ this.selectedImage = this.images[i]
409
+ }
410
+ }
411
+ this.selectedImageShow = !this.selectedImageShow
412
+ },
413
+ // 控制未放大图像变暗
414
+ changePhotoClassForSmall (id) {
415
+ for (let i = 0; i < this.images.length; i++) {
416
+ if (id == this.images[i].id) {
417
+ if (this.images[i].isLarge === true) {
418
+ return 'img_sm_dark'
419
+ } else {
420
+ return 'img_sm'
421
+ }
422
+ }
423
+ }
424
+ },
425
+ // 获取当前相关联的照片
426
+ getPhoto () {
427
+ return post(TicketDetailsViewApi.getTicketImages, {
428
+ ticketId: this.ticketId
429
+ })
430
+ .then(res => {
431
+ this.images = res
432
+ for (let i = 0; i < this.images.length; i++) {
433
+ this.images[i].isLarge = false
434
+ }
435
+ this.mergeAddImage()
436
+ }, err => {
437
+ console.log(err)
438
+ })
439
+ },
440
+ // 将同一次上传的图片,与描述放在一起
441
+ mergeAddImage () {
442
+ this.originalImages = []
443
+ for (let i = 0; i < this.addOnDescription.length; i++) {
444
+ this.addOnDescription[i].images = []
445
+ }
446
+ for (let i = 0; i < this.images.length; i++) {
447
+ if (this.images[i].did === 0) {
448
+ this.originalImages.push(this.images[i])
449
+ continue
450
+ }
451
+ for (let j = 0; j < this.addOnDescription.length; j++) {
452
+ if (this.images[i].did === this.addOnDescription[j].id) {
453
+ this.addOnDescription[j].images.push(this.images[i])
454
+ }
455
+ }
456
+ }
457
+ this.initDone()
458
+ },
459
+ initDone () {
460
+ // 初始化信息
461
+ this.showSpin = false
462
+ this.descriptionLoading = false
463
+ if (this.step !== 3) {
464
+ this.setTimer()
465
+ }
466
+ this.addOnDescriptionOriginal = 1
467
+ },
468
+ // 关闭工单确认后业务逻辑
469
+ handleCloseOk () {
470
+ return post(TicketDetailsViewApi.manualCloseTicket, {
471
+ ticketId: this.ticketId,
472
+ time: this.format(new Date(), 'yyyy-MM-dd hh:mm:ss')
473
+ })
474
+ .then(res => {
475
+ if (res !== 0) {
476
+ this.$message.success(
477
+ '操作成功',
478
+ 5
479
+ )
480
+ this.initView()
481
+ this.closeVisible = false
482
+ } else {
483
+ this.$message.error(
484
+ '操作失败',
485
+ 5
486
+ )
487
+ }
488
+ }, err => {
489
+ console.error(err)
490
+ })
491
+ },
492
+ // 关闭工单取消后业务逻辑
493
+ handleCloseCancel () {
494
+ this.closeVisible = false
495
+ },
496
+ // 确认工单按钮业务逻辑
497
+ confirmTicketBtn () {
498
+ this.getCurrentEmpId()
499
+ return post(TicketDetailsViewApi.confirmTicket, {
500
+ ticketId: this.ticketId,
501
+ time: this.format(new Date(), 'yyyy-MM-dd hh:mm:ss'),
502
+ empId: this.empIdInDataBase
503
+ })
504
+ .then(res => {
505
+ if (res !== 0) {
506
+ this.$message.success('操作成功', 5)
507
+ } else {
508
+ this.$message.error('工单已被他人处理或关闭,请刷新再试', 5)
509
+ }
510
+ this.initView()
511
+ }, err => {
512
+ console.error(err)
513
+ this.$message.error('工单已被他人处理,请刷新再试', 5)
514
+ this.initView()
515
+ })
516
+ },
517
+ // 获取当前登陆员工ID
518
+ getCurrentEmpId () {
519
+ return post(EmployeeDetailsViewApi.getEmployeeId, {
520
+ name: this.currUser.ename
521
+ })
522
+ .then(res => {
523
+ this.empIdInDataBase = res
524
+ }, err => {
525
+ console.log(err)
526
+ })
527
+ },
528
+ // 转移工单按钮业务逻辑
529
+ transferBtn () {
530
+ this.transVisible = true
531
+ },
532
+ checkEmp () {
533
+ return post(EmployeeDetailsViewApi.findEmpName, {
534
+ empName: this.transferTo
535
+ })
536
+ .then(res => {
537
+ const num = res
538
+ this.transferCheck = num !== 0
539
+ }, err => {
540
+ console.error(err)
541
+ })
542
+ },
543
+ // 获取工单状态撤销or关闭
544
+ getStatus () {
545
+ if (this.details.status === 2) {
546
+ return '已撤销'
547
+ } else {
548
+ return '已关闭'
549
+ }
550
+ },
551
+ // 关闭工单按钮业务逻辑
552
+ closeBtn () {
553
+ this.closeVisible = true
554
+ },
555
+ // 转移工单确认后逻辑
556
+ handleTransferOk () {
557
+ if (!this.transferCheck) {
558
+ this.$message.error(
559
+ '请先检查您输入的员工名',
560
+ 2
561
+ )
562
+ } else {
563
+ return post(TicketDetailsViewApi.transferTicketToOthers, {
564
+ ticketId: this.ticketId,
565
+ endTime: this.format(new Date(), 'yyyy-MM-dd hh:mm:ss'),
566
+ transferTo: this.transferTo,
567
+ note: this.note,
568
+ images: this.fileList
569
+ })
570
+ .then(res => {
571
+ const result = res
572
+ this.note = ''
573
+ this.transVisible = false
574
+ if (result === 1) {
575
+ this.$message.success(
576
+ '操作成功',
577
+ 5
578
+ )
579
+ this.fileList = []
580
+ this.transferTo = ''
581
+ this.transferCheck = false
582
+ this.initView()
583
+ } else {
584
+ this.$message.error('工单已被他人处理或关闭,请刷新再试', 5)
585
+ }
586
+ this.handleCancel()
587
+ this.initView()
588
+ }, err => {
589
+ console.error(err)
590
+ this.transVisible = false
591
+ })
592
+ }
593
+ },
594
+ // 转移工单取消后逻辑
595
+ handleTransferCancel () {
596
+ this.transVisible = false
597
+ this.transferCheck = false
598
+ this.transferTo = ''
599
+ this.note = ''
600
+ },
601
+ // 关闭抽屉时回调
602
+ onClose () {
603
+ this.stopTimer()
604
+ this.addOnDescription = []
605
+ this.disableCloseBtn = false
606
+ this.$emit('update:visible', false)
607
+ this.originalImages = []
608
+ this.images = []
609
+ this.addOnDescriptionOriginal = undefined
610
+ },
611
+ // 对其他信息进行初始化,包括附加描述,图片
612
+ handleOtherData () {
613
+ const beforeLength = this.addOnDescription.length
614
+ return post(TicketDetailsViewApi.getAddonDescription, {
615
+ ticketId: this.ticketId
616
+ })
617
+ .then(res => {
618
+ this.addOnDescription = res
619
+ this.getPhoto()
620
+ if (this.addOnDescription.length > beforeLength && this.addOnDescriptionOriginal !== undefined) {
621
+ this.addOnDescriptionOriginal = 1
622
+ this.$refs.audio.currentTime = 0
623
+ this.$refs.audio.play()
624
+ this.$notification.open({
625
+ message: '最新通知',
626
+ description:
627
+ '客户追加了问题描述,请及时查看!',
628
+ onClick: () => {
629
+ }
630
+ })
631
+ }
632
+ }, err => {
633
+ console.log(err)
634
+ })
635
+ },
636
+ // 获取工单详情信息
637
+ getTicketDetail (num) {
638
+ this.showSpin = true
639
+ return post(TicketDetailsViewApi.getTicketDetails, {
640
+ ticketId: num
641
+ }).then(res => {
642
+ this.details = res[0]
643
+ this.step = res[0].status
644
+ if (this.details.status === 3) {
645
+ this.disableCloseBtn = true
646
+ }
647
+ const categoryValue = this.details.category
648
+ this.details.category = this.$appdata.getDictionaryList('ticketCategoryMap')[categoryValue].label
649
+ }, err => {
650
+ console.error(err)
651
+ })
652
+ },
653
+ // 日期格式化
654
+ format (date, format) {
655
+ return formatDate(date, format)
656
+ },
657
+ // 转换JSON
658
+ toJSON (value) {
659
+ try {
660
+ return JSON.parse(value)
661
+ } catch (e) {
662
+ return value
663
+ }
664
+ },
665
+ // 控制开始处理按钮可用状态
666
+ handlerBtnDisable () {
667
+ return this.step !== 0
668
+ },
669
+ // 控制移交他人按钮可用状态
670
+ transferBtnDisable () {
671
+ return this.step === 0 || this.step === 3
672
+ },
673
+ // 控制关闭工单按钮可用状态
674
+ closeBtnDisable () {
675
+ return this.step === 0 || this.disableCloseBtn || this.details.name != this.currUser.ename
676
+ },
677
+ // 优先级文字显示
678
+ showPriority () {
679
+ if (this.details.priority === 2) {
680
+ return '一般'
681
+ } else if (this.details.priority === 1) {
682
+ return '紧急'
683
+ } else {
684
+ return '非常紧急'
685
+ }
686
+ },
687
+ // 优先度颜色控制
688
+ showPriorityColorControl () {
689
+ if (this.details.priority === 2) {
690
+ return 'info'
691
+ } else if (this.details.priority === 1) {
692
+ return 'warning'
693
+ } else {
694
+ return 'error'
695
+ }
696
+ },
697
+ // 单击工单流转历史图片
698
+ flowImageClick (image) {
699
+ this.selectedImage = image
700
+ this.selectedImageShow = !this.selectedImageShow
701
+ },
702
+ // 停止定时器
703
+ stopTimer () {
704
+ if (this.timer) {
705
+ clearInterval(this.timer)
706
+ this.timer = undefined
707
+ }
708
+ },
709
+ },
710
+ watch: {
711
+ 'visible' (val) {
712
+ if (val) {
713
+ this.initView()
714
+ }
715
+ },
716
+ 'step' (newVal) {
717
+ if (newVal === 3) {
718
+ this.stopTimer()
719
+ }
720
+ }
721
+ }
722
+ }
723
+ </script>
724
+
725
+ <style lang="less" scoped>
726
+ .high-priority{
727
+ background-color: rgba(163, 30, 30, 0.66);
728
+ border: red solid 1px;
729
+ font-size: large;
730
+ padding: 5px;
731
+ color: white;
732
+ border-radius: 10px;
733
+ }
734
+
735
+ .detail-layout {
736
+ margin-left: 44px;
737
+ }
738
+ .text {
739
+ color: rgba(0, 0, 0, .45);
740
+ }
741
+
742
+ .heading {
743
+ color: rgba(0, 0, 0, .85);
744
+ font-size: 20px;
745
+ }
746
+
747
+ .no-data {
748
+ color: rgba(0, 0, 0, .25);
749
+ text-align: center;
750
+ line-height: 64px;
751
+ font-size: 16px;
752
+
753
+ i {
754
+ font-size: 24px;
755
+ margin-right: 16px;
756
+ position: relative;
757
+ top: 3px;
758
+ }
759
+ }
760
+
761
+ .mobile {
762
+ .detail-layout {
763
+ margin-left: unset;
764
+ }
765
+ .text {
766
+
767
+ }
768
+ .status-list {
769
+ text-align: left;
770
+ }
771
+ }
772
+
773
+ .row {
774
+ display: flex;
775
+
776
+ .content {
777
+ -webkit-box-flex: 1;
778
+ flex: auto;
779
+ -ms-flex: auto;
780
+ }
781
+
782
+ .extra {
783
+ flex: 0 1 auto;
784
+ -webkit-box-flex: 0;
785
+ -ms-flex: 0 1 auto;
786
+ min-width: 242px;
787
+ margin-left: 88px;
788
+ text-align: right;
789
+ }
790
+ }
791
+
792
+ .img_sm {
793
+ border: rgba(84, 84, 84, 0.2) solid 1.5px;
794
+ border-radius: 5px;
795
+ padding: 10px;
796
+ width: 160px;
797
+ height: 120px;
798
+ margin-bottom: 20px;
799
+ margin-left: 20px;
800
+ }
801
+ .img_xl {
802
+ position: absolute;
803
+ top:0;bottom:0;left:0;right:0;
804
+ margin: auto;
805
+ width: 60%;
806
+ max-width: 1000px;
807
+ cursor: zoom-out;
808
+ z-index: 9999;
809
+ animation: imgZoomIn 0.4s;
810
+ -webkit-animation: imgZoomIn 0.4s;
811
+ }
812
+ @keyframes imgZoomIn
813
+ {
814
+ from {width: 160px;}
815
+ to {width: 60%;}
816
+ }
817
+ .img_sm:hover {
818
+ opacity: 0.6;
819
+ cursor: zoom-in;
820
+ }
821
+ .img_sm_dark{
822
+ filter: grayscale(100%);
823
+ opacity: 0.6;
824
+ border: rgba(84, 84, 84, 0.2) solid 1.5px;
825
+ border-radius: 5px;
826
+ padding: 10px;
827
+ width: 160px;
828
+ height: 120px;
829
+ margin-bottom: 20px;
830
+ margin-left: 20px;
831
+ }
832
+ .imgBackground {
833
+ position: absolute;
834
+ top:0;bottom:0;left:0;right:0;
835
+ width: 100%;
836
+ height: 100%;
837
+ z-index: 9998;
838
+ background-color: rgba(0,0,0,0.7);
839
+ }
840
+ .nameOptionsItem {
841
+ padding-left: 5px;
842
+ margin-bottom: 2px;
843
+ }
844
+ .nameOptionsItem:hover {
845
+ background-color: rgb(209,233,255);
846
+ }
847
+ .nameOptionsItem:last-of-type {
848
+ margin-bottom: 0px;
849
+ }
850
+ .nameOptions {
851
+ border: rgba(0,0,0,0.4) solid 1px;
852
+ }
853
+ </style>