n20-common-lib 3.3.0 → 3.3.2

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.
@@ -0,0 +1,634 @@
1
+ <template>
2
+ <div class="v3-approval-card">
3
+ <div class="v3-approval-card__timeline">
4
+ <section
5
+ v-for="(item, index) in displayApprovalData"
6
+ :key="getItemKey(item, index)"
7
+ class="v3-approval-card__item"
8
+ :class="statusClass(item)"
9
+ >
10
+ <span v-if="index < displayApprovalData.length - 1" class="v3-approval-card__track"></span>
11
+ <span class="v3-approval-card__dot"></span>
12
+
13
+ <header class="v3-approval-card__node-header">
14
+ <div class="v3-approval-card__title-row">
15
+ <span class="v3-approval-card__title">{{ resultTitle(item) }}</span>
16
+ <span v-if="isWaiting(item) && getPendingUsers(item).length" class="v3-approval-card__count">
17
+ · {{ getPendingUsers(item).length }} {{ $lc('人') }}
18
+ </span>
19
+ </div>
20
+ <div v-if="showNodeMeta(item)" class="v3-approval-card__meta">
21
+ <span v-if="item.endTime">{{ item.endTime }}</span>
22
+ <span v-if="item.endTime && item.approvalCost" class="v3-approval-card__meta-divider"></span>
23
+ <span v-if="item.approvalCost" class="v3-approval-card__cost">
24
+ <i class="el-icon-time"></i>
25
+ {{ $lc('本节点用时') }} {{ item.approvalCost }}
26
+ </span>
27
+ </div>
28
+ </header>
29
+
30
+ <div v-if="isWaiting(item)" class="v3-approval-card__panel v3-approval-card__pending-panel">
31
+ <div class="v3-approval-card__panel-title">{{ $lc('待审批人') }}</div>
32
+ <div class="v3-approval-card__pending-users">
33
+ <div
34
+ v-for="(user, userIndex) in getPendingUsers(item)"
35
+ :key="`${getUserName(user)}-${userIndex}`"
36
+ class="v3-approval-card__pending-user"
37
+ >
38
+ <span class="v3-approval-card__avatar">
39
+ <img v-if="getUserAvatar(user)" :src="getUserAvatar(user)" :alt="getUserName(user)" />
40
+ <i v-else class="n20-icon-user"></i>
41
+ </span>
42
+ <span>{{ getUserName(user) }}</span>
43
+ <span v-if="userIndex < getPendingUsers(item).length - 1" class="v3-approval-card__separator">、</span>
44
+ </div>
45
+ </div>
46
+ </div>
47
+
48
+ <template v-else-if="!isEnd(item)">
49
+ <div v-if="item.addTaskName" class="v3-approval-card__add-task">
50
+ <span>{{ $lc('加签审批人:') }}</span>
51
+ <span class="v3-approval-card__avatar v3-approval-card__avatar--small">
52
+ <i class="n20-icon-user"></i>
53
+ </span>
54
+ <span>{{ item.addTaskName }}</span>
55
+ <span v-if="item.addTaskTag" class="v3-approval-card__add-task-tag">
56
+ {{ item.addTaskTag | remarkF(remarkOpt) }}
57
+ </span>
58
+ </div>
59
+
60
+ <div class="v3-approval-card__panel v3-approval-card__person-panel">
61
+ <div
62
+ v-if="getOrganizationText(item) || item.showConnect === '1'"
63
+ class="v3-approval-card__organization-row"
64
+ >
65
+ <span v-title="getOrganizationText(item)" class="v3-approval-card__organization">
66
+ {{ getOrganizationText(item) }}
67
+ </span>
68
+ <button
69
+ v-if="item.showConnect === '1'"
70
+ type="button"
71
+ class="v3-approval-card__link-button"
72
+ @click="seeDetail(item)"
73
+ >
74
+ {{ $lc('查看详情') }}
75
+ </button>
76
+ </div>
77
+ <div v-if="item.assignee" class="v3-approval-card__assignee-pill">
78
+ <span class="v3-approval-card__avatar">
79
+ <img v-if="getUserAvatar(item)" :src="getUserAvatar(item)" :alt="item.assignee" />
80
+ <i v-else class="n20-icon-user"></i>
81
+ </span>
82
+ <span class="v3-approval-card__assignee-name">{{ item.assignee }}</span>
83
+ <span v-if="getTerminalLabel(item)" class="v3-approval-card__terminal"
84
+ >· {{ getTerminalLabel(item) }}</span
85
+ >
86
+ </div>
87
+ <div v-if="item.ccUserName" class="v3-approval-card__secondary-line">
88
+ {{ $lc('抄送人:') }}{{ item.ccUserName }}
89
+ </div>
90
+ <div
91
+ v-if="showSuggestionContent(item) && item.suggestion && !item.subProcInitId"
92
+ class="v3-approval-card__opinion"
93
+ >
94
+ <span v-if="item.result !== status.waiting">{{ $lc('审批意见:') }}</span>
95
+ <span>{{ item.suggestion }}</span>
96
+ </div>
97
+ </div>
98
+
99
+ <div
100
+ v-if="showSuggestionContent(item) && item.subProcInitId"
101
+ class="v3-approval-card__panel v3-approval-card__sub-process"
102
+ >
103
+ <button
104
+ type="button"
105
+ class="v3-approval-card__sub-process-trigger"
106
+ @click="seeApproveChild(item.subProcInitId)"
107
+ >
108
+ <span>{{ item.suggestion || $lc('查看子流程') }}</span>
109
+ <i :class="showChildTimeline ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"></i>
110
+ </button>
111
+ <div v-if="showChildTimeline" class="v3-approval-card__child-list">
112
+ <div
113
+ v-for="(row, childIndex) in displayApprovalChildrenData"
114
+ :key="getItemKey(row, childIndex)"
115
+ class="v3-approval-card__child-item"
116
+ >
117
+ <div class="v3-approval-card__child-title-row">
118
+ <span :class="['v3-approval-card__child-title', statusClass(row)]">{{ resultTitle(row) }}</span>
119
+ <span v-if="row.endTime" class="v3-approval-card__child-time">{{ row.endTime }}</span>
120
+ </div>
121
+ <div v-if="getOrganizationText(row)" class="v3-approval-card__organization">
122
+ {{ getOrganizationText(row) }}
123
+ </div>
124
+ <div v-if="row.assignee" class="v3-approval-card__assignee-pill">
125
+ <span class="v3-approval-card__avatar v3-approval-card__avatar--small">
126
+ <i class="n20-icon-user"></i>
127
+ </span>
128
+ <span>{{ row.assignee }}</span>
129
+ </div>
130
+ <div v-if="row.ccUserName" class="v3-approval-card__secondary-line">
131
+ {{ $lc('抄送人:') }}{{ row.ccUserName }}
132
+ </div>
133
+ <div v-if="row.addTaskName" class="v3-approval-card__secondary-line">
134
+ {{ $lc('加签审批人:') }}{{ row.addTaskName }}
135
+ </div>
136
+ <div v-if="showSuggestionContent(row) && row.suggestion" class="v3-approval-card__opinion">
137
+ <span v-if="row.result !== status.waiting">{{ $lc('审批意见:') }}</span>
138
+ <span>{{ row.suggestion }}</span>
139
+ </div>
140
+ <div
141
+ v-if="showSuggestionContent(row) && row.flowHistoryCfgs && row.flowHistoryCfgs.length"
142
+ class="v3-approval-card__child-review"
143
+ >
144
+ <div class="v3-approval-card__review-header">
145
+ <span>{{ $lc('审查意见') }}</span>
146
+ <button
147
+ type="button"
148
+ class="v3-approval-card__link-button v3-approval-card__collapse-button"
149
+ @click="showFlowHistoryChild = !showFlowHistoryChild"
150
+ >
151
+ <i :class="showFlowHistoryChild ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"></i>
152
+ {{ showFlowHistoryChild ? $lc('收起') : $lc('展开') }}
153
+ </button>
154
+ </div>
155
+ <div v-show="showFlowHistoryChild" class="v3-approval-card__review-content">
156
+ <div
157
+ v-for="(history, historyIndex) in row.flowHistoryCfgs"
158
+ :key="historyIndex"
159
+ class="v3-approval-card__review-row"
160
+ :class="{ 'is-stacked': align !== 'left-right' }"
161
+ >
162
+ <span>{{ history.cfgName }}:{{ history.cfgText || history.cfgVal }}</span>
163
+ <span v-if="history.cfgRemark">{{ history.cfgRemark }}</span>
164
+ </div>
165
+ </div>
166
+ </div>
167
+ <div v-if="row.flowHisFileList && row.flowHisFileList.length" class="v3-approval-card__child-files">
168
+ <button
169
+ v-for="(file, fileIndex) in row.flowHisFileList"
170
+ :key="`${file.beid || file.fileName}-${fileIndex}`"
171
+ type="button"
172
+ class="v3-approval-card__child-file"
173
+ @click="preview(file)"
174
+ >
175
+ {{ file.fileName }}
176
+ </button>
177
+ </div>
178
+ </div>
179
+ </div>
180
+ </div>
181
+
182
+ <div
183
+ v-if="showSuggestionContent(item) && item.flowHistoryCfgs && item.flowHistoryCfgs.length"
184
+ class="v3-approval-card__panel v3-approval-card__review-panel"
185
+ >
186
+ <div class="v3-approval-card__review-header">
187
+ <span>{{ $lc('审查意见') }}</span>
188
+ <button
189
+ type="button"
190
+ class="v3-approval-card__link-button v3-approval-card__collapse-button"
191
+ @click="showFlowHistory = !showFlowHistory"
192
+ >
193
+ <i :class="showFlowHistory ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"></i>
194
+ {{ showFlowHistory ? $lc('收起') : $lc('展开') }}
195
+ </button>
196
+ </div>
197
+ <div v-show="showFlowHistory" class="v3-approval-card__review-content">
198
+ <div
199
+ v-for="(history, historyIndex) in item.flowHistoryCfgs"
200
+ :key="historyIndex"
201
+ class="v3-approval-card__review-row"
202
+ :class="{ 'is-stacked': align !== 'left-right' }"
203
+ >
204
+ <span>{{ history.cfgName }}:{{ history.cfgText || history.cfgVal }}</span>
205
+ <span v-if="history.cfgRemark">{{ history.cfgRemark }}</span>
206
+ </div>
207
+ </div>
208
+ </div>
209
+
210
+ <div
211
+ v-if="item.flowHisFileList && item.flowHisFileList.length"
212
+ class="v3-approval-card__panel v3-approval-card__file-panel"
213
+ >
214
+ <div class="v3-approval-card__panel-title">{{ $lc('附件') }}</div>
215
+ <div class="v3-approval-card__file-list">
216
+ <div
217
+ v-for="(file, fileIndex) in item.flowHisFileList"
218
+ :key="`${file.beid || file.fileName}-${fileIndex}`"
219
+ class="v3-approval-card__file-item"
220
+ >
221
+ <div class="v3-approval-card__file-main">
222
+ <img class="v3-approval-card__file-icon" :src="getFileIcon(file.fileName)" alt="" />
223
+ <span v-title="file.fileName" class="v3-approval-card__file-name">{{ file.fileName }}</span>
224
+ </div>
225
+ <div class="v3-approval-card__file-actions">
226
+ <span class="v3-approval-card__file-divider"></span>
227
+ <button type="button" :title="$lc('预览')" @click="preview(file)">
228
+ <i class="el-icon-view"></i>
229
+ </button>
230
+ <button type="button" :title="$lc('下载')" @click="downFile(file)">
231
+ <i class="el-icon-download"></i>
232
+ </button>
233
+ </div>
234
+ </div>
235
+ </div>
236
+ </div>
237
+ </template>
238
+ </section>
239
+ </div>
240
+
241
+ <el-drawer
242
+ class="v3-approval-card__preview-drawer"
243
+ custom-class="v3-approval-card__drawer"
244
+ :visible.sync="visibleP"
245
+ direction="rtl"
246
+ size="60%"
247
+ :append-to-body="true"
248
+ :wrapper-closable="true"
249
+ @close="closeSee"
250
+ >
251
+ <div slot="title" class="v3-approval-card__drawer-header">
252
+ <span class="v3-approval-card__drawer-title">{{ $lc('附件预览') }}</span>
253
+ <el-button v-if="seeRow && seeRow.beid" plain size="mini" @click.stop="downFile(seeRow)">
254
+ {{ $lc('下载') }}
255
+ </el-button>
256
+ </div>
257
+ <div v-if="visibleP" class="v3-approval-card__drawer-body">
258
+ <component
259
+ :is="previewSameOrg ? 'object' : 'div'"
260
+ :key="previewUrl"
261
+ :data="previewUrl"
262
+ class="v3-approval-card__preview-content"
263
+ >
264
+ <div class="v3-approval-card__preview-fallback">
265
+ <i class="el-icon-s-release"></i>
266
+ <span>
267
+ {{ $lc('不支持在线预览,请') }}
268
+ <el-link type="primary" @click="downFile(seeRow)">{{ $lc('下载') }}</el-link>
269
+ {{ $lc('到本地查看') }}
270
+ </span>
271
+ </div>
272
+ </component>
273
+ </div>
274
+ </el-drawer>
275
+ </div>
276
+ </template>
277
+
278
+ <script>
279
+ import getJsonc from '../../../assets/getJsonc.js'
280
+ import axios from '../../../utils/axios'
281
+ import { $lc } from '../../../utils/i18n/index'
282
+ import { linkPush } from '../../../utils/urlToGo.js'
283
+ import pdfIcon from '../UploadList/assets/37.svg'
284
+ import wordIcon from '../UploadList/assets/46.svg'
285
+ import excelIcon from '../UploadList/assets/60.svg'
286
+ import defaultFileIcon from '../UploadList/assets/not.svg'
287
+ import pngIcon from '../UploadList/assets/Png.svg'
288
+ import txtIcon from '../UploadList/assets/Txt.svg'
289
+ import zipIcon from '../UploadList/assets/Zip.svg'
290
+
291
+ function getApprovalExtend() {
292
+ if (typeof window === 'undefined') return {}
293
+ if (!window._approvalExtend) window._approvalExtend = {}
294
+ return window._approvalExtend
295
+ }
296
+
297
+ export default {
298
+ name: 'ApprovalCardV3',
299
+ filters: {
300
+ remarkF(value, list) {
301
+ const option = (list || []).find((item) => item.code === value)
302
+ return option ? option.name : value
303
+ }
304
+ },
305
+ props: {
306
+ apiPrefix: {
307
+ type: String,
308
+ default: undefined
309
+ },
310
+ close: {
311
+ type: Boolean,
312
+ default: false
313
+ },
314
+ status: {
315
+ type: Object,
316
+ default: () => ({
317
+ waiting: 0,
318
+ submit: 1,
319
+ approval: 2,
320
+ reject: 3,
321
+ invalid: 4,
322
+ end: 5,
323
+ revocation: 6
324
+ })
325
+ },
326
+ approvalData: {
327
+ type: Array,
328
+ default: () => []
329
+ },
330
+ procInstId: {
331
+ type: String,
332
+ default: undefined
333
+ },
334
+ hideLoading: {
335
+ type: Boolean,
336
+ default: false
337
+ },
338
+ align: {
339
+ type: String,
340
+ default: 'left-right'
341
+ },
342
+ seeTypes: {
343
+ type: RegExp,
344
+ default: () => /\.(jpg|png|gif|svg|pdf|swf|xlsx|xls|docx|doc|txt)$/i
345
+ }
346
+ },
347
+ data() {
348
+ this.approvalRequestId = 0
349
+ this.remarkRequest = null
350
+ return {
351
+ remoteApprovalData: [],
352
+ remarkOpt: [],
353
+ showChildTimeline: false,
354
+ approvalChildrenData: [],
355
+ showCtdMsg: getApprovalExtend()['card.countermand.suggestion'] === true,
356
+ visibleP: false,
357
+ previewUrl: '',
358
+ previewSameOrg: false,
359
+ seeRow: {},
360
+ showFlowHistory: true,
361
+ showFlowHistoryChild: true
362
+ }
363
+ },
364
+ computed: {
365
+ resolvedApprovalData() {
366
+ return [...(this.approvalData || []), ...this.remoteApprovalData]
367
+ },
368
+ displayApprovalData() {
369
+ // 保持旧版 el-timeline reverse 的展示顺序,但不再修改调用方传入的数组。
370
+ return [...this.resolvedApprovalData].reverse()
371
+ },
372
+ displayApprovalChildrenData() {
373
+ return [...(this.approvalChildrenData || [])].reverse()
374
+ }
375
+ },
376
+ watch: {
377
+ procInstId: {
378
+ immediate: true,
379
+ handler(value) {
380
+ this.loadApprovalData(value)
381
+ }
382
+ }
383
+ },
384
+ beforeDestroy() {
385
+ this.approvalRequestId += 1
386
+ this.closeSee()
387
+ },
388
+ methods: {
389
+ async loadApprovalData(procInstId) {
390
+ const requestId = ++this.approvalRequestId
391
+ if (!procInstId) {
392
+ this.remoteApprovalData = []
393
+ return
394
+ }
395
+ // 切换流程实例时先清空旧记录,避免新请求完成前短暂展示上一条流程的数据。
396
+ this.remoteApprovalData = []
397
+ try {
398
+ const data = await this.getProcInstData(procInstId)
399
+ // 只接收最后一次请求,避免快速切换流程实例时出现旧数据回写。
400
+ if (requestId === this.approvalRequestId) {
401
+ this.remoteApprovalData = data
402
+ }
403
+ } catch (error) {
404
+ if (requestId === this.approvalRequestId) {
405
+ this.remoteApprovalData = []
406
+ }
407
+ return error
408
+ }
409
+ },
410
+ async seeDetail(item) {
411
+ const details = (item && item._details) || {}
412
+ if (!details.appNo) {
413
+ this.$message.error($lc('未配置审批详情链接'))
414
+ return
415
+ }
416
+ const { appUrl = {} } = await getJsonc('activiti/server-config.jsonc', null, true)
417
+ if (!appUrl[details.appNo]) {
418
+ this.$message.error($lc('未配置审批详情链接'))
419
+ return
420
+ }
421
+ linkPush(`${appUrl[details.appNo]}/${details.typeCode}`, {
422
+ taskId: details.taskId,
423
+ typeCode: details.typeCode,
424
+ processInstanceId: details.procInstId,
425
+ appNo: details.appNo,
426
+ businessId: details.businessId
427
+ })
428
+ },
429
+ async seeApproveChild(id) {
430
+ this.showChildTimeline = !this.showChildTimeline
431
+ if (!this.showChildTimeline) {
432
+ this.approvalChildrenData = []
433
+ return
434
+ }
435
+ try {
436
+ this.approvalChildrenData = await this.getProcInstData(id)
437
+ } catch (error) {
438
+ this.approvalChildrenData = []
439
+ return error
440
+ }
441
+ },
442
+ async getProcInstData(procInstId) {
443
+ const { data } = await axios.get(
444
+ `/bems/activiti/sample/Q003?r=${Math.random()}`,
445
+ { procInstId },
446
+ { loading: !this.hideLoading }
447
+ )
448
+ return (Array.isArray(data) ? data : [])
449
+ .map((item) => this.normalizeApprovalItem(item))
450
+ .filter((item) => item.isShow !== '0')
451
+ },
452
+ normalizeApprovalItem(item) {
453
+ const resultNames = String(item.optResult || '').split('-')
454
+ const rawResultName = resultNames[resultNames.length - 1]
455
+ const normalizedResultName = rawResultName === $lc('推进') ? $lc('批准') : rawResultName
456
+ const approvalItem = {
457
+ approvalCost: item.approvalCost,
458
+ job: item.job || '',
459
+ isShow: item.isShow,
460
+ endTime: item.endTime || '',
461
+ assignee: item.assignee,
462
+ assigneeList: item.assigneeList,
463
+ approvers: item.approvers,
464
+ suggestion: item.suggestion,
465
+ roleName: item.roleName,
466
+ flowHisFileList: item.flowHisFileList || null,
467
+ ccUserName: item.ccUserName,
468
+ addTaskName: item.addTaskName,
469
+ memberName: item.memberName,
470
+ subProcInitId: item.subProcInitId,
471
+ appOrPc: item.appOrPc,
472
+ outTitle: item.outTitle,
473
+ showConnect: item.showConnect,
474
+ deptName: item.deptName,
475
+ addTaskTag: item.addTaskTag,
476
+ flowHistoryCfgs: item.flowHistoryCfgs,
477
+ avatar: item.avatar,
478
+ avatarUrl: item.avatarUrl,
479
+ photoUrl: item.photoUrl,
480
+ headImg: item.headImg,
481
+ resultName: item.optName || normalizedResultName,
482
+ resultNameA: normalizedResultName,
483
+ _details: item
484
+ }
485
+
486
+ if (approvalItem.addTaskTag && this.remarkOpt.length === 0) {
487
+ this.getMdmDataDoc()
488
+ }
489
+ if (item.taskName === $lc('开始')) {
490
+ approvalItem.result = this.status.submit
491
+ } else if (approvalItem.resultNameA === $lc('加签推进')) {
492
+ approvalItem.result = this.status.approval
493
+ } else if (approvalItem.resultNameA === $lc('作废')) {
494
+ approvalItem.result = this.status.invalid
495
+ } else if (item.taskName === $lc('结束')) {
496
+ approvalItem.result = this.status.end
497
+ } else if (Object.prototype.hasOwnProperty.call(item, 'approvalState')) {
498
+ approvalItem.result = item.approvalState
499
+ } else if (!item.endTime) {
500
+ approvalItem.result = this.status.waiting
501
+ } else if (item.rejectFlag === 1) {
502
+ approvalItem.result = this.status.reject
503
+ } else {
504
+ approvalItem.result = this.status.approval
505
+ }
506
+ return approvalItem
507
+ },
508
+ async getMdmDataDoc() {
509
+ if (this.remarkRequest) return this.remarkRequest
510
+ this.remarkRequest = axios.get(`/bems/1.0/mdmDataDoc?t=${Math.random()}`, { typeCode: 'ADD_TASK_TAG' })
511
+ try {
512
+ const { code, data } = await this.remarkRequest
513
+ if (code === 200) {
514
+ this.remarkOpt = data || []
515
+ }
516
+ } catch (error) {
517
+ // 字典仅用于展示加签标签,请求失败不应阻断审批记录渲染。
518
+ return error
519
+ } finally {
520
+ this.remarkRequest = null
521
+ }
522
+ },
523
+ preview(row = {}) {
524
+ const fileName = row.fileName || ''
525
+ this.visibleP = true
526
+ this.seeRow = row
527
+ this.previewUrl = row.beid ? `/api/onlinePreview?beid=${row.beid}` : ''
528
+ // 全局正则的 lastIndex 会影响连续预览结果,每次判断前后均复位。
529
+ this.seeTypes.lastIndex = 0
530
+ this.previewSameOrg = this.seeTypes.test(fileName)
531
+ this.seeTypes.lastIndex = 0
532
+ },
533
+ closeSee() {
534
+ if (this.previewUrl && this.previewUrl.indexOf('blob:') === 0 && typeof URL !== 'undefined') {
535
+ URL.revokeObjectURL(this.previewUrl)
536
+ }
537
+ this.previewUrl = ''
538
+ this.previewSameOrg = false
539
+ this.seeRow = {}
540
+ this.visibleP = false
541
+ },
542
+ async downFile(row = {}) {
543
+ if (!row.beid) {
544
+ this.$message.error($lc('缺少文件标识'))
545
+ return
546
+ }
547
+ const url = this.apiPrefix
548
+ ? `${this.apiPrefix}/neams/eamsbaserecord/download/${row.beid}`
549
+ : `/neams/eamsbaserecord/download/${row.beid}`
550
+ const blob = await axios.get(url, null, {
551
+ responseType: 'blob',
552
+ loading: false
553
+ })
554
+ this.$downloadBlob(blob, blob.name || row.fileName)
555
+ },
556
+ getItemKey(item = {}, index) {
557
+ return `${item.subProcInitId || item.endTime || item.resultName || 'approval'}-${item.result}-${index}`
558
+ },
559
+ isWaiting(item = {}) {
560
+ return item.result === this.status.waiting
561
+ },
562
+ isEnd(item = {}) {
563
+ return item.result === this.status.end
564
+ },
565
+ showSuggestionContent(item = {}) {
566
+ return item.resultName !== this.$lc('撤回') || this.showCtdMsg
567
+ },
568
+ showNodeMeta(item = {}) {
569
+ return !this.isWaiting(item) && !this.isEnd(item) && Boolean(item.endTime || item.approvalCost)
570
+ },
571
+ resultTitle(item = {}) {
572
+ if (item.resultName) return this.$lc(item.resultName)
573
+ if (this.isWaiting(item)) return this.$lc('待审批')
574
+ if (this.isEnd(item)) return this.$lc('结束')
575
+ if (item.result === this.status.submit) return this.$lc('提交')
576
+ if (item.result === this.status.approval) return this.$lc('批准')
577
+ if (item.result === this.status.reject) return this.$lc('驳回')
578
+ if (item.result === this.status.invalid) return this.$lc('作废')
579
+ if (item.result === this.status.revocation) return this.$lc('撤回')
580
+ return ''
581
+ },
582
+ statusClass(item = {}) {
583
+ if (this.isWaiting(item)) return 'is-waiting'
584
+ if (item.result === this.status.approval) return 'is-approval'
585
+ if (item.result === this.status.reject || item.result === this.status.invalid) return 'is-reject'
586
+ if (item.result === this.status.revocation) return 'is-revocation'
587
+ return 'is-primary'
588
+ },
589
+ getPendingUsers(item = {}) {
590
+ const details = item._details || {}
591
+ // 兼容外部直传的审批人列表,以及接口映射后保留在 _details 中的原始列表。
592
+ const users = item.assigneeList || item.approvers || details.assigneeList || details.approvers
593
+ if (Array.isArray(users)) return users.filter(Boolean)
594
+ return String(item.assignee || '')
595
+ .split(/[、,,;;]+/)
596
+ .map((name) => name.trim())
597
+ .filter(Boolean)
598
+ },
599
+ getUserName(user) {
600
+ if (!user) return ''
601
+ if (typeof user === 'string') return user
602
+ return user.name || user.assignee || user.userName || user.memberName || user.label || ''
603
+ },
604
+ getUserAvatar(user) {
605
+ if (!user || typeof user === 'string') return ''
606
+ const source = user._details || user
607
+ return source.avatar || source.avatarUrl || source.photoUrl || source.headImg || ''
608
+ },
609
+ getOrganizationText(item = {}) {
610
+ return [item.memberName, item.deptName, item.job, item.roleName].filter(Boolean).join('|')
611
+ },
612
+ getTerminalLabel(item = {}) {
613
+ if (item.outTitle) return item.outTitle
614
+ if (item.appOrPc === '1' || item.appOrPc === 1) return 'PC'
615
+ if (item.appOrPc === '2' || item.appOrPc === 2) return this.$lc('外部系统')
616
+ if (item.appOrPc === null || item.appOrPc === '' || item.appOrPc === undefined) return ''
617
+ return 'APP'
618
+ },
619
+ getFileIcon(fileName = '') {
620
+ const suffix = String(fileName || '')
621
+ .split('.')
622
+ .pop()
623
+ .toLowerCase()
624
+ if (suffix === 'pdf' || suffix === 'ppt' || suffix === 'pptx') return pdfIcon
625
+ if (suffix === 'doc' || suffix === 'docx') return wordIcon
626
+ if (suffix === 'xls' || suffix === 'xlsx') return excelIcon
627
+ if (['png', 'jpg', 'jpeg', 'gif', 'svg'].includes(suffix)) return pngIcon
628
+ if (suffix === 'txt') return txtIcon
629
+ if (['zip', 'rar', '7z'].includes(suffix)) return zipIcon
630
+ return defaultFileIcon
631
+ }
632
+ }
633
+ }
634
+ </script>
package/src/i18n.json CHANGED
@@ -12654,5 +12654,12 @@
12654
12654
  "vi": "Vui lòng cấu hình danh mục tệp đính kèm",
12655
12655
  "ja": "添付ファイルのカテゴリを設定してください",
12656
12656
  "zh-hk": "請配置附件分類"
12657
+ },
12658
+ "上传结果为空,请稍后重试": {
12659
+ "en": "Upload result is empty, please try again later",
12660
+ "th": "ผลการอัปโหลดว่างเปล่า โปรดลองอีกครั้งในภายหลัง",
12661
+ "vi": "Kết quả tải lên trống, vui lòng thử lại sau",
12662
+ "ja": "アップロード結果が空です。後でもう一度お試しください",
12663
+ "zh-hk": "上傳結果為空,請稍後重試"
12657
12664
  }
12658
12665
  }
package/src/index.js CHANGED
@@ -127,6 +127,7 @@ import Tree from './components/Tree/index.vue'
127
127
  import Upload from './components/Upload/index.vue'
128
128
  import UploadMsg from './components/Upload/uploadMsg.vue'
129
129
  import AnchorV3 from './components/v3/Anchor/index.vue'
130
+ import ApprovalCardV3 from './components/v3/ApprovalCard/index.vue'
130
131
  import Collapse from './components/v3/Collapse/index.vue'
131
132
  import CollapseLink from './components/v3/CollapseLink/index.vue'
132
133
  import Footer from './components/v3/Footer/index.vue'
@@ -293,6 +294,7 @@ const components = [
293
294
  operatingStatus,
294
295
  /* v3组件*/
295
296
  tableProV3,
297
+ ApprovalCardV3,
296
298
  SecondaryTabV3,
297
299
  Header,
298
300
  Footer,
@@ -447,6 +449,7 @@ export {
447
449
  WornPagination,
448
450
  /* v3组件*/
449
451
  tableProV3,
452
+ ApprovalCardV3,
450
453
  SecondaryTabV3,
451
454
  Header,
452
455
  Footer,