vue2-client 1.7.0 → 1.7.1

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 (126) hide show
  1. package/.env +15 -15
  2. package/CHANGELOG.md +31 -1
  3. package/Components.md +60 -0
  4. package/index.js +39 -30
  5. package/package.json +4 -2
  6. package/src/base-client/components/common/AddressSearchCombobox/AddressSearchCombobox.vue +316 -316
  7. package/src/base-client/components/common/CitySelect/CitySelect.vue +247 -247
  8. package/src/base-client/components/common/CreateQuery/CreateQuery.vue +669 -667
  9. package/src/base-client/components/common/CreateQuery/CreateQueryItem.vue +735 -733
  10. package/src/base-client/components/common/CreateSimpleFormQuery/CreateSimpleFormQuery.vue +466 -468
  11. package/src/base-client/components/common/CreateSimpleFormQuery/CreateSimpleFormQueryItem.vue +510 -508
  12. package/src/base-client/components/common/FormGroupEdit/FormGroupEdit.vue +144 -146
  13. package/src/base-client/components/common/FormGroupQuery/FormGroupQuery.vue +165 -165
  14. package/src/base-client/components/common/JSONToTree/jsontotree.vue +275 -275
  15. package/src/base-client/components/common/Upload/Upload.vue +168 -168
  16. package/src/base-client/components/common/XAddForm/XAddForm.vue +72 -325
  17. package/src/base-client/components/common/XAddNativeForm/XAddNativeForm.vue +365 -279
  18. package/src/base-client/components/common/XAddNativeForm/index.md +96 -56
  19. package/src/base-client/components/common/XCard/XCard.vue +64 -64
  20. package/src/base-client/components/common/XDataDrawer/XDataDrawer.vue +180 -0
  21. package/src/base-client/components/common/XDataDrawer/index.js +3 -0
  22. package/src/base-client/components/common/XDataDrawer/index.md +41 -0
  23. package/src/base-client/components/common/XForm/XForm.vue +178 -180
  24. package/src/base-client/components/common/XForm/XFormItem.vue +521 -513
  25. package/src/base-client/components/common/XForm/XTreeSelect.vue +184 -184
  26. package/src/base-client/components/common/XFormCol/XFormCol.vue +38 -38
  27. package/src/base-client/components/common/XFormTable/XFormTable.vue +356 -344
  28. package/src/base-client/components/common/XFormTable/index.md +97 -97
  29. package/src/base-client/components/common/XImportExcel/XImportExcel.vue +132 -132
  30. package/src/base-client/components/common/XTable/XTable.vue +519 -506
  31. package/src/base-client/components/common/XTreeOne/XTreeOne.vue +111 -111
  32. package/src/base-client/components/system/DictionaryDetailsView/DictionaryDetailsView.vue +231 -231
  33. package/src/base-client/components/system/QueryParamsDetailsView/QueryParamsDetailsView.vue +281 -281
  34. package/src/base-client/components/ticket/TicketDetailsView/TicketDetailsView.vue +807 -807
  35. package/src/base-client/components/ticket/TicketDetailsView/index.md +29 -29
  36. package/src/base-client/components/ticket/TicketDetailsView/part/TicketDetailsFlow.vue +260 -260
  37. package/src/base-client/components/ticket/TicketSubmitSuccessView/TicketSubmitSuccessView.vue +532 -532
  38. package/src/base-client/components/ticket/TicketSubmitSuccessView/index.md +29 -29
  39. package/src/base-client/plugins/AppData.js +76 -76
  40. package/src/base-client/plugins/GetLoginInfoService.js +179 -179
  41. package/src/base-client/plugins/PagedList.js +177 -177
  42. package/src/base-client/plugins/compatible/LoginServiceOA.js +20 -20
  43. package/src/base-client/plugins/i18n-extend.js +32 -32
  44. package/src/components/Ellipsis/Ellipsis.vue +65 -65
  45. package/src/components/Ellipsis/index.md +38 -38
  46. package/src/components/NumberInfo/index.md +43 -43
  47. package/src/components/STable/README.md +341 -341
  48. package/src/components/STable/index.js +318 -318
  49. package/src/components/Trend/index.md +45 -45
  50. package/src/components/checkbox/ColorCheckbox.vue +157 -157
  51. package/src/components/checkbox/ImgCheckbox.vue +163 -163
  52. package/src/components/exception/ExceptionPage.vue +70 -70
  53. package/src/components/form/FormRow.vue +52 -52
  54. package/src/components/index.js +36 -36
  55. package/src/components/menu/SideMenu.vue +62 -62
  56. package/src/components/menu/menu.js +273 -273
  57. package/src/components/page/header/index.less +40 -40
  58. package/src/components/setting/Setting.vue +235 -235
  59. package/src/components/table/StandardTable.vue +141 -141
  60. package/src/components/table/advance/ActionColumns.vue +158 -158
  61. package/src/components/table/advance/SearchArea.vue +355 -355
  62. package/src/components/tool/AStepItem.vue +60 -60
  63. package/src/components/tool/AvatarList.vue +68 -68
  64. package/src/components/tool/Drawer.vue +142 -142
  65. package/src/components/tool/TagSelect.vue +83 -83
  66. package/src/components/transition/PageToggleTransition.vue +97 -97
  67. package/src/config/CreateQueryConfig.js +307 -307
  68. package/src/config/default/admin.config.js +18 -18
  69. package/src/config/default/setting.config.js +3 -1
  70. package/src/config/replacer/resolve.config.js +67 -67
  71. package/src/layouts/CommonLayout.vue +42 -42
  72. package/src/layouts/ComponentLayoutOne.vue +47 -47
  73. package/src/layouts/PageLayout.vue +151 -151
  74. package/src/layouts/SinglePageView.vue +116 -116
  75. package/src/layouts/footer/PageFooter.vue +49 -49
  76. package/src/layouts/header/AdminHeader.vue +134 -134
  77. package/src/layouts/header/HeaderAvatar.vue +64 -64
  78. package/src/layouts/header/HeaderNotice.vue +176 -176
  79. package/src/layouts/header/HeaderSearch.vue +67 -67
  80. package/src/layouts/header/InstitutionDetail.vue +181 -181
  81. package/src/layouts/header/index.less +92 -92
  82. package/src/layouts/tabs/TabsHead.vue +190 -190
  83. package/src/layouts/tabs/TabsView.vue +379 -379
  84. package/src/mock/goods/index.js +108 -108
  85. package/src/pages/CreateQueryPage.vue +84 -84
  86. package/src/pages/login/Login.vue +369 -369
  87. package/src/pages/report/ReportTable.js +124 -124
  88. package/src/pages/report/ReportTableHome.vue +28 -28
  89. package/src/pages/resourceManage/orgListManage.vue +98 -98
  90. package/src/pages/system/dictionary/index.vue +43 -43
  91. package/src/pages/system/file/index.vue +317 -317
  92. package/src/pages/system/monitor/loginInfor/index.vue +36 -36
  93. package/src/pages/system/monitor/operLog/index.vue +36 -36
  94. package/src/pages/system/settings/index.vue +126 -126
  95. package/src/pages/system/settings/modifyPassword.vue +109 -109
  96. package/src/router/async/config.async.js +28 -28
  97. package/src/router/async/router.map.js +66 -66
  98. package/src/router/guards.js +52 -12
  99. package/src/router/index.js +27 -27
  100. package/src/services/api/DictionaryDetailsViewApi.js +6 -6
  101. package/src/services/api/LogDetailsViewApi.js +10 -10
  102. package/src/services/api/QueryParamsDetailsViewApi.js +6 -6
  103. package/src/services/api/TicketDetailsViewApi.js +34 -34
  104. package/src/services/api/cas.js +79 -79
  105. package/src/services/api/common.js +137 -137
  106. package/src/services/api/commonTempTable.js +10 -10
  107. package/src/services/api/index.js +17 -17
  108. package/src/services/api/logininfor/index.js +6 -6
  109. package/src/services/api/manage.js +8 -8
  110. package/src/services/apiService.js +14 -14
  111. package/src/services/user.js +67 -67
  112. package/src/store/modules/index.js +4 -4
  113. package/src/theme/default/nprogress.less +76 -76
  114. package/src/theme/default/style.less +58 -58
  115. package/src/utils/EncryptUtil.js +53 -53
  116. package/src/utils/colors.js +107 -107
  117. package/src/utils/excel/Blob.js +180 -180
  118. package/src/utils/excel/Export2Excel.js +141 -141
  119. package/src/utils/formatter.js +68 -68
  120. package/src/utils/i18n.js +80 -80
  121. package/src/utils/login.js +138 -0
  122. package/src/utils/map-utils.js +37 -37
  123. package/src/utils/theme-color-replacer-extend.js +91 -91
  124. package/src/utils/themeUtil.js +100 -100
  125. package/src/utils/util.js +230 -230
  126. 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/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/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>