vue2-client 1.3.2 → 1.3.5

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