@jari-ace/element-plus-component 0.4.4 → 0.5.0

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 (46) hide show
  1. package/dist/components/avatar/JaAvatar.vue.d.ts.map +1 -1
  2. package/dist/components/avatar/JaAvatar.vue.js +4 -2
  3. package/dist/components/avatar/JaAvatar.vue.js.map +1 -1
  4. package/dist/components/flowShell/FlowFormShell.vue.d.ts +403 -0
  5. package/dist/components/flowShell/FlowFormShell.vue.d.ts.map +1 -0
  6. package/dist/components/flowShell/FlowFormShell.vue.js +671 -0
  7. package/dist/components/flowShell/FlowFormShell.vue.js.map +1 -0
  8. package/dist/components/flowShell/index.d.ts +4 -0
  9. package/dist/components/flowShell/index.d.ts.map +1 -0
  10. package/dist/components/flowShell/index.js +4 -0
  11. package/dist/components/flowShell/index.js.map +1 -0
  12. package/dist/components/index.d.ts +1 -0
  13. package/dist/components/index.d.ts.map +1 -1
  14. package/dist/components/index.js +1 -0
  15. package/dist/components/index.js.map +1 -1
  16. package/dist/components/upload/uploader.vue.d.ts.map +1 -1
  17. package/dist/components/upload/uploader.vue.js +1 -2
  18. package/dist/components/upload/uploader.vue.js.map +1 -1
  19. package/dist/components/userPicker/src/JaUserList.vue.d.ts.map +1 -1
  20. package/dist/components/userPicker/src/JaUserList.vue.js +0 -2
  21. package/dist/components/userPicker/src/JaUserList.vue.js.map +1 -1
  22. package/dist/components/userTag/UserInfoTag.vue.d.ts +1 -1
  23. package/dist/components/userTag/UserInfoTag.vue.d.ts.map +1 -1
  24. package/dist/components/userTag/UserInfoTag.vue.js +2 -2
  25. package/dist/components/userTag/UserInfoTag.vue.js.map +1 -1
  26. package/dist/hooks/useRouteableVisible.d.ts +12 -0
  27. package/dist/hooks/useRouteableVisible.d.ts.map +1 -0
  28. package/dist/hooks/useRouteableVisible.js +34 -0
  29. package/dist/hooks/useRouteableVisible.js.map +1 -0
  30. package/dist/index.d.ts +1 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +1 -0
  33. package/dist/index.js.map +1 -1
  34. package/lib/index.css +2 -2
  35. package/lib/index.js +10623 -9904
  36. package/lib/index.umd.cjs +39 -35
  37. package/package.json +4 -2
  38. package/packages/components/avatar/JaAvatar.vue +4 -2
  39. package/packages/components/flowShell/FlowFormShell.vue +628 -0
  40. package/packages/components/flowShell/index.ts +5 -0
  41. package/packages/components/index.ts +1 -0
  42. package/packages/components/upload/uploader.vue +1 -2
  43. package/packages/components/userPicker/src/JaUserList.vue +0 -1
  44. package/packages/components/userTag/UserInfoTag.vue +2 -2
  45. package/packages/hooks/useRouteableVisible.ts +35 -0
  46. package/packages/index.ts +1 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jari-ace/element-plus-component",
3
3
  "private": false,
4
- "version": "0.4.4",
4
+ "version": "0.5.0",
5
5
  "main": "lib/index.umd.cjs",
6
6
  "module": "lib/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -23,9 +23,11 @@
23
23
  "@uppy/vue": "^3.1.0",
24
24
  "@uppy/webcam": "^5.0.2",
25
25
  "animate.css": "^4.1.1",
26
+ "dayjs": "^1.11.13",
26
27
  "pretty-bytes": "^7.1.0",
27
28
  "vue-pdf-embed": "^2.1.3",
28
- "@jari-ace/app-bolts": "0.6.4"
29
+ "vue-router": "^5.0.1",
30
+ "@jari-ace/app-bolts": "0.7.0"
29
31
  },
30
32
  "devDependencies": {
31
33
  "@types/lodash-es": "^4.17.12",
@@ -71,13 +71,15 @@ const lazyUrl = computed(() => {
71
71
  }
72
72
  if (props.userId && props.userId.length > 0) {
73
73
  const path = '/ace-app-service/avatar/' + props.userId
74
- return window.appDescriptor.env.isDevMode()
74
+ const win = window.rawWindow? window.rawWindow: window
75
+ return win.appDescriptor.env.isDevMode()
75
76
  ? new URL("/ace" + path, location.origin).toString() + "?t=" + token.value.toString() //开发模式下使用vite代理,添加ace前缀,由vite代理修改路由
76
77
  : new URL(path, location.origin).toString() + "?t=" + token.value.toString()
77
78
  }
78
79
  if (props.realm && props.realm.length > 0 && props.username && props.username.length > 0) {
79
80
  const path = '/ace-app-service/avatar/' + props.realm + '/' + props.username
80
- return window.appDescriptor.env.isDevMode()
81
+ const win = window.rawWindow? window.rawWindow: window
82
+ return win.appDescriptor.env.isDevMode()
81
83
  ? new URL("/ace" + path, location.origin).toString() + "?t=" + token.value.toString() //开发模式下使用vite代理,添加ace前缀,由vite代理修改路由
82
84
  : new URL(path, location.origin).toString() + "?t=" + token.value.toString()
83
85
  }
@@ -0,0 +1,628 @@
1
+ <template>
2
+ <div v-if="dialogVisible" class="flow-shell-wrapper">
3
+ <div class="flow-shell-container" v-loading="isLoading">
4
+ <header class="flow-shell-header">
5
+ <div class="header-content">
6
+ <div class="header-top">
7
+ <div class="back-button-wrapper" @click="dialogVisible = false">
8
+ <el-icon :size="40">
9
+ <ArrowLeft />
10
+ </el-icon>
11
+ </div>
12
+ <div>
13
+ <div class="title-section">
14
+ <h2 class="flow-title">{{ flowFormParam?.flowDefinition?.flowModel?.flowCaption || '流程详情' }}</h2>
15
+ <el-tag :type="flowStateTagType">{{
16
+ getFlowStatus(flowFormParam?.flowInstance?.state) }}</el-tag>
17
+ </div>
18
+ <div class="header-bottom" v-if="flowFormParam?.flowInstance">
19
+ <div class="info-item">
20
+ <span class="label">发起人:</span>
21
+ <ja-user-info-tag :user-id="flowFormParam?.flowInstance?.initiator" show-avatar-in-tag
22
+ :full-name="flowFormParam?.flowInstance?.initiatorName">
23
+ </ja-user-info-tag>
24
+ </div>
25
+ <div class="info-item">
26
+ <span class="label">发起时间:</span>
27
+ <el-tooltip :content="formatDate(flowFormParam?.flowInstance?.createTime)" placement="top"
28
+ v-if="flowFormParam?.flowInstance?.createTime">
29
+ <span class="value">{{ formatFriendlyTime(flowFormParam?.flowInstance?.createTime) }}</span>
30
+ </el-tooltip>
31
+ <span class="value" v-else>-</span>
32
+ </div>
33
+ <div class="info-item" v-if="flowFormParam?.flowInstance?.workNo">
34
+ <span class="label">工作文号:</span>
35
+ <span class="value">{{ flowFormParam?.flowInstance?.workNo }}</span>
36
+ </div>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ </div>
41
+ </header>
42
+
43
+ <!-- 主体内容区 -->
44
+ <main class="flow-shell-main">
45
+ <!-- 左侧内容区 -->
46
+ <section class="main-content">
47
+ <div class="content-wrapper">
48
+ <div v-if="error" class="error-tip">
49
+ <el-result icon="error" title="加载失败" :sub-title="error">
50
+ <template #extra>
51
+ <el-button type="primary" @click="loadFlowFormParam">重试</el-button>
52
+ </template>
53
+ </el-result>
54
+ </div>
55
+ <div v-else-if="flowFormParam">
56
+ <slot name="default" :flowParam="flowFormParam"></slot>
57
+ </div>
58
+ <div class="no-form-tip" v-else>
59
+ <el-empty description="暂无表单内容" />
60
+ </div>
61
+ </div>
62
+ </section>
63
+
64
+ <!-- 右侧辅助面板 -->
65
+ <aside class="side-panel">
66
+ <div class="side-panel-header">
67
+ <h3>办理历史</h3>
68
+ </div>
69
+ <div class="side-panel-content">
70
+ <el-timeline v-if="sortedTasks.length > 0">
71
+ <el-timeline-item v-for="(task, index) in sortedTasks" :key="index"
72
+ :timestamp="formatFriendlyTime(task.finishTime)" placement="top">
73
+ <el-card>
74
+ <div class="task-info">
75
+ <div class="handler-info">
76
+ <span class="handler-name">{{ task.handlerName || '未知' }}</span>
77
+ <el-tooltip :content="formatDate(task.finishTime)" placement="top">
78
+ <span class="task-status">{{ getTaskStatus(task.state) }}</span>
79
+ </el-tooltip>
80
+ </div>
81
+ <div class="task-comment" v-if="task.comment">
82
+ <span class="comment-label">意见:</span>
83
+ <span class="comment-content">{{ task.comment }}</span>
84
+ </div>
85
+ <div class="task-comment" v-else>
86
+ <span class="comment-label">意见:</span>
87
+ <span class="comment-content empty">无</span>
88
+ </div>
89
+ </div>
90
+ </el-card>
91
+ </el-timeline-item>
92
+ </el-timeline>
93
+ <div v-else class="no-history-tip">
94
+ <el-empty description="暂无办理历史" :image-size="100" />
95
+ </div>
96
+ </div>
97
+ </aside>
98
+ </main>
99
+
100
+ <!-- 底部工具栏 -->
101
+ <footer class="flow-shell-footer">
102
+ <ja-button @click="handleSave" shortcut="Ctrl+S" tooltip="保存" :loading="saving"
103
+ :disabled="saving">保存</ja-button>
104
+ <ja-button @click="handleForward" type="danger" shortcut="Ctrl+F" v-if="showForwardButton"
105
+ :tooltip="'保存并' + (flowFormParam?.taskInstance ? '结束当前工作步骤办理' : '发起工作')" :loading="saving"
106
+ :disabled="saving">{{
107
+ flowFormParam?.taskInstance ? "办理结束" : "发起工作" }}</ja-button>
108
+ </footer>
109
+ </div>
110
+ </div>
111
+ </template>
112
+
113
+ <script setup lang="ts" generic="T extends Record<string, any>">
114
+ import { ref, watch, computed, nextTick } from 'vue'
115
+ import { useTaskQueryApi, useFlowDefinitionApi, type FlowFormParamDto } from '@jari-ace/app-bolts'
116
+ import type { FlowProcessRequest, TaskInstanceDto } from '@jari-ace/app-bolts'
117
+ import { createAxiosWithoutCache, useLoading } from '@jari-ace/app-bolts'
118
+ import { ElMessage, ElTag, ElCard, ElButton, ElTimeline, ElTimelineItem, ElEmpty, ElIcon, ElResult } from 'element-plus'
119
+ import { JaButton } from '../button'
120
+ import { JaUserInfoTag } from '../userTag'
121
+ import { ArrowLeft } from '@element-plus/icons-vue'
122
+ import dayjs from 'dayjs'
123
+ import relativeTime from 'dayjs/plugin/relativeTime'
124
+ import 'dayjs/locale/zh-cn'
125
+ import { ElTooltip } from 'element-plus'
126
+ import { isVisible } from 'element-plus/es/utils/index.mjs'
127
+ dayjs.extend(relativeTime)
128
+ dayjs.locale('zh-cn')
129
+
130
+
131
+ const props = withDefaults(defineProps<{
132
+ /**
133
+ * 应用名称
134
+ */
135
+ appName?: string
136
+ /**
137
+ * 流程定义Key
138
+ */
139
+ flowKey?: string
140
+ /**
141
+ * 开始节点Key
142
+ */
143
+ startNodeKey?: string
144
+ /**
145
+ * 任务实例ID
146
+ */
147
+ taskId?: string
148
+ /**
149
+ * 表单数据
150
+ */
151
+ formData: T
152
+ /**
153
+ * 保存表单数据的方法,保存成功时发起或转交工作
154
+ */
155
+ saveAndForward: (formData: FlowProcessRequest<T>) => Promise<any>
156
+ }>(), {
157
+ appName: undefined,
158
+ flowKey: undefined,
159
+ startNodeKey: undefined,
160
+ taskInstanceId: undefined
161
+ })
162
+
163
+ // 内部状态
164
+ const dialogVisible = defineModel<boolean>({
165
+ default: false
166
+ })
167
+ const axios = createAxiosWithoutCache()
168
+ const loading = useLoading(axios)
169
+ const localLoading = ref(false)
170
+ const isLoading = computed(() => loading.value || localLoading.value)
171
+ const saving = ref(false)
172
+ const error = ref('')
173
+ const flowFormParam = ref<FlowFormParamDto | undefined>(undefined)
174
+ const taskQueryApi = useTaskQueryApi(axios)
175
+ const flowDefinitionApi = useFlowDefinitionApi(axios)
176
+ const emits = defineEmits<{
177
+ (e: 'open'): void
178
+ (e: 'closed'): void
179
+ }>();
180
+
181
+ const flowStateTagType = computed(() => {
182
+ switch (flowFormParam.value?.flowInstance?.state) {
183
+ case 0:
184
+ return 'warning'
185
+ case 1:
186
+ return 'success'
187
+ case 2:
188
+ return 'danger'
189
+ default:
190
+ return 'info'
191
+ }
192
+ })
193
+
194
+ const showForwardButton = computed(() => {
195
+ return flowFormParam.value?.taskInstance || !flowFormParam.value?.flowInstance
196
+ })
197
+
198
+ // 获取排序后的任务列表
199
+ const sortedTasks = computed(() => {
200
+ if (!flowFormParam.value?.flowInstance?.nodes) return []
201
+
202
+ let allTasks: TaskInstanceDto[] = []
203
+
204
+ // 收集所有节点的任务
205
+ Object.values(flowFormParam.value.flowInstance.nodes).forEach(node => {
206
+ allTasks = [...allTasks, ...node.tasks]
207
+ })
208
+
209
+ // 按办结时间排序,最新的在前面
210
+ return allTasks.sort((a, b) => {
211
+ const timeA = a.finishTime ? new Date(a.finishTime).getTime() : 0
212
+ const timeB = b.finishTime ? new Date(b.finishTime).getTime() : 0
213
+ return timeB - timeA
214
+ })
215
+ })
216
+
217
+ // 格式化日期
218
+ const formatDate = (dateStr?: string) => {
219
+ if (!dateStr) return ''
220
+ return dayjs(dateStr).format('YYYY-MM-DD HH:mm:ss')
221
+ }
222
+
223
+ // 友好显示时间
224
+ const formatFriendlyTime = (dateStr?: string) => {
225
+ if (!dateStr) return ''
226
+ return dayjs(dateStr).fromNow()
227
+ }
228
+
229
+ // 获取流程状态文本
230
+ const getFlowStatus = (state?: number) => {
231
+ switch (state) {
232
+ case 0:
233
+ return '进行中'
234
+ case 1:
235
+ return '已完成'
236
+ case 2:
237
+ return '已中止'
238
+ default:
239
+ return '待发起'
240
+ }
241
+ }
242
+
243
+ // 获取任务状态文本
244
+ const getTaskStatus = (state?: number) => {
245
+ switch (state) {
246
+ case 0:
247
+ return '未激活'
248
+ case 1:
249
+ return '未接收'
250
+ case 2:
251
+ return '已接收'
252
+ case 3:
253
+ return '已办结'
254
+ default:
255
+ return '未知状态'
256
+ }
257
+ }
258
+
259
+ // 监听dialogVisible变化
260
+ watch(
261
+ () => dialogVisible.value,
262
+ async (newValue) => {
263
+ resetState()
264
+ if (newValue) {
265
+ emits('open')
266
+ // 等待下一个事件循环,确保父组件的open事件处理完成(可能包含异步loadData)
267
+ await nextTick()
268
+ loadFlowFormParam()
269
+ } else {
270
+ emits('closed')
271
+ }
272
+ }
273
+ )
274
+
275
+ // 重置状态
276
+ const resetState = () => {
277
+ error.value = ''
278
+ flowFormParam.value = undefined
279
+ }
280
+
281
+ // 加载流程表单参数
282
+ const loadFlowFormParam = async () => {
283
+ resetState()
284
+ const formId = props.formData?.flowFormId;
285
+ // 检查参数
286
+ // 必须提供 taskId,或者 appName 和 flowKey
287
+ if (!props.taskId && !(props.appName && props.flowKey)) {
288
+ ElMessage.error('参数错误: 必须提供taskId或appName、flowKey和flowFormId')
289
+ return
290
+ }
291
+
292
+ try {
293
+ if (props.taskId) {
294
+ // 优先使用Id
295
+ flowFormParam.value = await taskQueryApi.getTaskInstanceById(props.taskId)
296
+ } else if (formId) {
297
+ // 使用appName、flowKey、formId
298
+ flowFormParam.value = await taskQueryApi.getTaskByFormIdAndAppAndFlowKey(formId, props.appName!, props.flowKey!)
299
+ } else {
300
+ // 新建模式:只有 appName 和 flowKey,没有 formId
301
+ // 获取流程定义
302
+ const flowDefinition = await flowDefinitionApi.getEffectiveDefinition(props.appName!, props.flowKey!)
303
+ // 构造部分 flowFormParam
304
+ flowFormParam.value = {
305
+ flowDefinition: flowDefinition,
306
+ // 其他字段为空
307
+ } as FlowFormParamDto
308
+ }
309
+ } catch (e: any) {
310
+ ElMessage.error(e.message || '加载流程信息失败')
311
+ error.value = e.message
312
+ }
313
+ }
314
+
315
+ // 底部按钮处理
316
+ const handleSave = async () => {
317
+ saving.value = true
318
+ try {
319
+ await props.saveAndForward({
320
+ flowArgs: {
321
+ taskId: flowFormParam.value?.taskInstance?.id || '',
322
+ processRequestType: "SAVE_FORM",
323
+ appName: props.appName || '',
324
+ flowKey: props.flowKey || '',
325
+ startNodeKey: props.startNodeKey || '',
326
+ forwardTo: []
327
+ },
328
+ formCommand: props.formData as T
329
+ })
330
+ } finally {
331
+ saving.value = false
332
+ }
333
+ }
334
+
335
+ const handleForward = async () => {
336
+ saving.value = true
337
+ try {
338
+ const p = flowFormParam.value
339
+ await props.saveAndForward({
340
+ flowArgs: {
341
+ taskId: p?.taskInstance?.id || '',
342
+ processRequestType: p?.taskInstance ? "FORWARD" : "INITIATE",
343
+ appName: props.appName || '',
344
+ flowKey: props.flowKey || '',
345
+ startNodeKey: props.startNodeKey || '',
346
+ forwardTo: []
347
+ },
348
+ formCommand: props.formData as T
349
+ })
350
+ dialogVisible.value = false
351
+
352
+ } finally {
353
+ saving.value = false
354
+ }
355
+ }
356
+
357
+ </script>
358
+
359
+ <style>
360
+ /* 移除旧的 dialog 样式 */
361
+ </style>
362
+
363
+ <style scoped>
364
+ .flow-shell-wrapper {
365
+ position: fixed;
366
+ top: 0;
367
+ left: 0;
368
+ width: 100%;
369
+ height: 100%;
370
+ background-color: #f5f7fa;
371
+ z-index: 2000;
372
+ display: flex;
373
+ flex-direction: column;
374
+ }
375
+
376
+ .flow-shell-container {
377
+ width: 100%;
378
+ height: 100%;
379
+ display: flex;
380
+ flex-direction: column;
381
+ }
382
+
383
+ /* 标题区样式 */
384
+ .flow-shell-header {
385
+ background-color: #ffffff;
386
+ border-bottom: 1px solid #e0e0e0;
387
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
388
+ z-index: 10;
389
+ padding: 16px 12px;
390
+ }
391
+
392
+ .header-content {
393
+ width: 100%;
394
+ }
395
+
396
+ .header-top {
397
+ display: flex;
398
+ align-items: center;
399
+ margin-bottom: 12px;
400
+ }
401
+
402
+ .back-button-wrapper {
403
+ display: flex;
404
+ align-items: center;
405
+ justify-content: center;
406
+ width: 64px;
407
+ height: 64px;
408
+ border-radius: 50%;
409
+ cursor: pointer;
410
+ transition: all 0.3s;
411
+ margin-right: 12px;
412
+ color: var(--el-color-primary-light-3);
413
+ /* background-color: var(--el-color-primary-light-9); */
414
+ }
415
+
416
+ .back-button-wrapper:hover {
417
+ background-color: var(--el-color-primary-light-9);
418
+ color: var(--el-color-primary);
419
+ }
420
+
421
+ .title-section {
422
+ display: flex;
423
+ align-items: center;
424
+ gap: 12px;
425
+ flex: 1;
426
+ }
427
+
428
+ .flow-title {
429
+ margin: 0;
430
+ font-size: 20px;
431
+ font-weight: 600;
432
+ color: #303133;
433
+ }
434
+
435
+ .flow-status {
436
+ font-size: 12px;
437
+ height: 24px;
438
+ line-height: 24px;
439
+ padding: 0 12px;
440
+ color: #409eff;
441
+ border: none;
442
+ }
443
+
444
+ .header-bottom {
445
+ display: flex;
446
+ gap: 32px;
447
+ align-items: center;
448
+ padding-top: 8px;
449
+ }
450
+
451
+ .info-item {
452
+ display: flex;
453
+ align-items: center;
454
+ font-size: 14px;
455
+ }
456
+
457
+ .info-item .label {
458
+ color: #606266;
459
+ }
460
+
461
+ .info-item .value {
462
+ color: #303133;
463
+ font-weight: 500;
464
+ }
465
+
466
+ .info-item .dept-name {
467
+ color: #909399;
468
+ font-size: 13px;
469
+ }
470
+
471
+ /* 主体内容区样式 */
472
+ .flow-shell-main {
473
+ flex: 1;
474
+ display: flex;
475
+ gap: 16px;
476
+ overflow: hidden;
477
+ /* 防止被内容撑开 */
478
+ min-height: 0;
479
+ /* flex子项高度计算修复 */
480
+ }
481
+
482
+ /* 左侧内容区 */
483
+ .main-content {
484
+ flex: 1;
485
+ padding: 0;
486
+ display: flex;
487
+ flex-direction: column;
488
+ overflow: hidden;
489
+ /* 确保内部滚动生效 */
490
+ }
491
+
492
+ .content-wrapper {
493
+ background-color: #ffffff;
494
+ border-radius: 4px;
495
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
496
+ height: 100%;
497
+ overflow-y: auto;
498
+ padding: 24px;
499
+ }
500
+
501
+ .no-form-tip {
502
+ width: 100%;
503
+ height: 100%;
504
+ display: flex;
505
+ align-items: center;
506
+ justify-content: center;
507
+ font-size: 16px;
508
+ color: #909399;
509
+ background-color: #ffffff;
510
+ border-radius: 8px;
511
+ }
512
+
513
+ /* 右侧辅助面板 */
514
+ .side-panel {
515
+ background-color: #ffffff;
516
+ border-left: 1px solid #e0e0e0;
517
+ display: flex;
518
+ flex-direction: column;
519
+ width: 400px;
520
+ }
521
+
522
+ .side-panel-header {
523
+ height: 60px;
524
+ border-bottom: 1px solid #e0e0e0;
525
+ display: flex;
526
+ align-items: center;
527
+ padding: 0 20px;
528
+ background-color: #fafafa;
529
+ }
530
+
531
+ .side-panel-header h3 {
532
+ margin: 0;
533
+ font-size: 16px;
534
+ font-weight: 600;
535
+ color: #303133;
536
+ }
537
+
538
+ .side-panel-content {
539
+ flex: 1;
540
+ overflow-y: auto;
541
+ padding: 20px;
542
+ position: relative;
543
+ }
544
+
545
+ .no-history-tip {
546
+ height: 100%;
547
+ display: flex;
548
+ align-items: center;
549
+ justify-content: center;
550
+ }
551
+
552
+ /* 时间轴样式 */
553
+ .el-timeline {
554
+ padding: 0;
555
+ }
556
+
557
+ .el-timeline-item {
558
+ margin-bottom: 20px;
559
+ }
560
+
561
+ .el-timeline-item__timestamp {
562
+ font-size: 12px;
563
+ color: #909399;
564
+ margin-bottom: 8px;
565
+ }
566
+
567
+ /* 任务卡片样式 */
568
+ .task-info {
569
+ display: flex;
570
+ flex-direction: column;
571
+ gap: 12px;
572
+ }
573
+
574
+ .handler-info {
575
+ display: flex;
576
+ align-items: center;
577
+ justify-content: space-between;
578
+ }
579
+
580
+ .handler-name {
581
+ font-size: 14px;
582
+ font-weight: 500;
583
+ color: #303133;
584
+ }
585
+
586
+ .task-status {
587
+ font-size: 12px;
588
+ padding: 2px 8px;
589
+ border-radius: 10px;
590
+ background-color: #ecf5ff;
591
+ color: #409eff;
592
+ }
593
+
594
+ .task-comment {
595
+ display: flex;
596
+ gap: 8px;
597
+ align-items: flex-start;
598
+ }
599
+
600
+ .comment-label {
601
+ font-size: 14px;
602
+ color: #606266;
603
+ flex-shrink: 0;
604
+ }
605
+
606
+ .comment-content {
607
+ font-size: 14px;
608
+ color: #303133;
609
+ line-height: 1.5;
610
+ }
611
+
612
+ .comment-content.empty {
613
+ color: #909399;
614
+ font-style: italic;
615
+ }
616
+
617
+ .flow-shell-footer {
618
+ background-color: #ffffff;
619
+ border-top: 1px solid #e0e0e0;
620
+ padding: 12px 24px;
621
+ display: flex;
622
+ justify-content: flex-start;
623
+ align-items: center;
624
+ gap: 12px;
625
+ box-shadow: 0 -1px 4px rgba(0, 0, 0, 0.05);
626
+ z-index: 100;
627
+ }
628
+ </style>
@@ -0,0 +1,5 @@
1
+
2
+ import { type SFCWithInstall, withInstall } from "../../utils/install";
3
+ import FlowFormShell from "./FlowFormShell.vue";
4
+
5
+ export const JaFlowFormShell: SFCWithInstall<typeof FlowFormShell> = withInstall(FlowFormShell);
@@ -32,3 +32,4 @@ export * from "./properyPicker"
32
32
  export * from "./inputI18n"
33
33
  export * from "./checkboxGroup"
34
34
  export * from "./enumPicker"
35
+ export * from "./flowShell"
@@ -16,7 +16,6 @@ import {
16
16
  useSystemClassificationLevels
17
17
  } from "../../hooks/useClassificationLevels";
18
18
  import Ace = Jari.Ace;
19
- import { ElMessageBox } from "element-plus";
20
19
  import "@uppy/core/css/style.min.css";
21
20
  import "@uppy/dashboard/css/style.min.css";
22
21
  import "@uppy/audio/css/style.min.css";
@@ -33,8 +32,8 @@ import Dashboard from "@uppy/dashboard";
33
32
  import ImageEditor from "@uppy/image-editor";
34
33
  import prettyBytes from "pretty-bytes";
35
34
  import PdfViewerModal from "./pdf-viewer/PdfViewerModal.vue";
36
-
37
35
  import {
36
+ ElMessageBox,
38
37
  ElTable,
39
38
  ElTableColumn,
40
39
  ElButton,
@@ -255,7 +255,6 @@ onUnmounted(() => {
255
255
  <Check v-if="userSelected(u)"/>
256
256
  </el-icon>
257
257
  <ja-user-info-tag :user-id="u.id" :full-name="u.fullName"
258
- :has-avatar="u.hasAvatar"
259
258
  placement="left-start"
260
259
  :theme="userTagTheme(u)"></ja-user-info-tag>
261
260
  </li>