vue2-client 1.6.21 → 1.6.22

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