vue2-client 1.6.47 → 1.6.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/CHANGELOG.md +625 -622
  2. package/index.js +30 -30
  3. package/package.json +81 -81
  4. package/src/base-client/components/common/AddressSearchCombobox/AddressSearchCombobox.vue +316 -316
  5. package/src/base-client/components/common/CitySelect/CitySelect.vue +247 -247
  6. package/src/base-client/components/common/CreateQuery/CreateQueryItem.vue +733 -733
  7. package/src/base-client/components/common/CreateSimpleFormQuery/CreateSimpleFormQueryItem.vue +508 -508
  8. package/src/base-client/components/common/FormGroupEdit/FormGroupEdit.vue +140 -140
  9. package/src/base-client/components/common/FormGroupQuery/FormGroupQuery.vue +165 -165
  10. package/src/base-client/components/common/JSONToTree/jsontotree.vue +275 -275
  11. package/src/base-client/components/common/Upload/Upload.vue +168 -168
  12. package/src/base-client/components/common/XAddForm/XAddForm.vue +325 -325
  13. package/src/base-client/components/common/XAddNativeForm/XAddNativeForm.vue +279 -279
  14. package/src/base-client/components/common/XCard/XCard.vue +64 -64
  15. package/src/base-client/components/common/XForm/XForm.vue +180 -180
  16. package/src/base-client/components/common/XForm/XFormItem.vue +513 -519
  17. package/src/base-client/components/common/XForm/XTreeSelect.vue +184 -184
  18. package/src/base-client/components/common/XFormCol/XFormCol.vue +18 -29
  19. package/src/base-client/components/common/XFormTable/XFormTable.vue +336 -336
  20. package/src/base-client/components/common/XFormTable/index.md +97 -97
  21. package/src/base-client/components/common/XImportExcel/XImportExcel.vue +132 -132
  22. package/src/base-client/components/common/XTable/XTable.vue +6 -5
  23. package/src/base-client/components/common/XTreeOne/XTreeOne.vue +111 -111
  24. package/src/base-client/components/system/QueryParamsDetailsView/QueryParamsDetailsView.vue +281 -281
  25. package/src/base-client/components/ticket/TicketDetailsView/TicketDetailsView.vue +807 -807
  26. package/src/base-client/components/ticket/TicketDetailsView/index.md +29 -29
  27. package/src/base-client/components/ticket/TicketDetailsView/part/TicketDetailsFlow.vue +260 -260
  28. package/src/base-client/components/ticket/TicketSubmitSuccessView/TicketSubmitSuccessView.vue +532 -532
  29. package/src/base-client/components/ticket/TicketSubmitSuccessView/index.md +29 -29
  30. package/src/base-client/plugins/AppData.js +76 -76
  31. package/src/base-client/plugins/GetLoginInfoService.js +179 -179
  32. package/src/base-client/plugins/PagedList.js +177 -177
  33. package/src/base-client/plugins/compatible/LoginServiceOA.js +20 -20
  34. package/src/base-client/plugins/i18n-extend.js +32 -32
  35. package/src/components/Ellipsis/Ellipsis.vue +65 -65
  36. package/src/components/Ellipsis/index.md +38 -38
  37. package/src/components/NumberInfo/index.md +43 -43
  38. package/src/components/STable/README.md +341 -341
  39. package/src/components/STable/index.js +318 -318
  40. package/src/components/Trend/index.md +45 -45
  41. package/src/components/checkbox/ColorCheckbox.vue +157 -157
  42. package/src/components/checkbox/ImgCheckbox.vue +163 -163
  43. package/src/components/exception/ExceptionPage.vue +70 -70
  44. package/src/components/form/FormRow.vue +52 -52
  45. package/src/components/index.js +36 -36
  46. package/src/components/menu/SideMenu.vue +62 -62
  47. package/src/components/menu/menu.js +273 -273
  48. package/src/components/page/header/index.less +40 -40
  49. package/src/components/setting/Setting.vue +235 -235
  50. package/src/components/table/StandardTable.vue +141 -141
  51. package/src/components/table/advance/ActionColumns.vue +158 -158
  52. package/src/components/table/advance/SearchArea.vue +355 -355
  53. package/src/components/tool/AStepItem.vue +60 -60
  54. package/src/components/tool/AvatarList.vue +68 -68
  55. package/src/components/tool/Drawer.vue +142 -142
  56. package/src/components/tool/TagSelect.vue +83 -83
  57. package/src/components/transition/PageToggleTransition.vue +97 -97
  58. package/src/config/CreateQueryConfig.js +307 -307
  59. package/src/config/default/admin.config.js +18 -18
  60. package/src/config/default/setting.config.js +46 -46
  61. package/src/config/replacer/resolve.config.js +67 -67
  62. package/src/layouts/CommonLayout.vue +42 -42
  63. package/src/layouts/ComponentLayoutOne.vue +47 -47
  64. package/src/layouts/PageLayout.vue +151 -151
  65. package/src/layouts/SinglePageView.vue +116 -116
  66. package/src/layouts/footer/PageFooter.vue +49 -49
  67. package/src/layouts/header/AdminHeader.vue +134 -134
  68. package/src/layouts/header/HeaderAvatar.vue +64 -64
  69. package/src/layouts/header/HeaderNotice.vue +176 -176
  70. package/src/layouts/header/HeaderSearch.vue +67 -67
  71. package/src/layouts/header/InstitutionDetail.vue +181 -181
  72. package/src/layouts/header/index.less +92 -92
  73. package/src/layouts/tabs/TabsHead.vue +190 -190
  74. package/src/layouts/tabs/TabsView.vue +379 -379
  75. package/src/mock/goods/index.js +108 -108
  76. package/src/pages/login/Login.vue +369 -366
  77. package/src/pages/report/ReportTable.js +124 -124
  78. package/src/pages/report/ReportTableHome.vue +28 -28
  79. package/src/pages/resourceManage/orgListManage.vue +98 -98
  80. package/src/pages/system/file/index.vue +317 -317
  81. package/src/pages/system/settings/index.vue +126 -126
  82. package/src/pages/system/settings/modifyPassword.vue +109 -109
  83. package/src/router/async/config.async.js +28 -28
  84. package/src/router/async/router.map.js +68 -68
  85. package/src/router/index.js +27 -27
  86. package/src/services/api/DictionaryDetailsViewApi.js +6 -6
  87. package/src/services/api/LogDetailsViewApi.js +10 -10
  88. package/src/services/api/QueryParamsDetailsViewApi.js +6 -6
  89. package/src/services/api/TicketDetailsViewApi.js +34 -34
  90. package/src/services/api/cas.js +79 -79
  91. package/src/services/api/common.js +9 -0
  92. package/src/services/api/commonTempTable.js +10 -10
  93. package/src/services/api/index.js +17 -17
  94. package/src/services/api/logininfor/index.js +6 -6
  95. package/src/services/api/manage.js +8 -8
  96. package/src/services/apiService.js +14 -14
  97. package/src/services/user.js +67 -67
  98. package/src/store/modules/index.js +4 -4
  99. package/src/theme/default/nprogress.less +76 -76
  100. package/src/theme/default/style.less +58 -58
  101. package/src/utils/EncryptUtil.js +53 -53
  102. package/src/utils/colors.js +107 -107
  103. package/src/utils/excel/Blob.js +180 -180
  104. package/src/utils/excel/Export2Excel.js +141 -141
  105. package/src/utils/formatter.js +68 -68
  106. package/src/utils/i18n.js +80 -80
  107. package/src/utils/map-utils.js +37 -37
  108. package/src/utils/theme-color-replacer-extend.js +91 -91
  109. package/src/utils/themeUtil.js +100 -100
  110. package/src/utils/util.js +230 -230
  111. 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>