@ebiz/designer-components 0.1.48 → 0.1.50

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ebiz/designer-components",
3
- "version": "0.1.48",
3
+ "version": "0.1.50",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -15,6 +15,9 @@
15
15
  <t-textarea placeholder="请输入" :maxLength="200" :maxCharacter="true" class="component-base-style"
16
16
  v-model="comments"></t-textarea>
17
17
  </t-form-item>
18
+ <t-form-item label="附件">
19
+ <ebiz-upload v-model="attachments" :multiple="true" theme="file" />
20
+ </t-form-item>
18
21
  <t-form-item>
19
22
  <div class="button-group">
20
23
  <div class="more-actions">
@@ -95,6 +98,7 @@ import {
95
98
  } from 'tdesign-vue-next'
96
99
  import { defineProps, defineEmits, ref, reactive, computed, onMounted } from 'vue'
97
100
  import { dataService, EbizEmployeeSelector, EbizApproval } from '../index'
101
+ import { EbizUpload } from '../index.js'
98
102
 
99
103
  const request = (params = {}, apiConfig = {}, url = '') => {
100
104
  return dataService.fetch(params, apiConfig, url)
@@ -134,6 +138,7 @@ const emit = defineEmits(['pass', 'reject', 'refresh'])
134
138
 
135
139
  // 基础数据
136
140
  const comments = ref('')
141
+ const attachments = ref([])
137
142
  const showMoreActions = ref(false)
138
143
  const taskType = ref('')
139
144
 
@@ -295,6 +300,7 @@ const handlePass = async () => {
295
300
  const params = {
296
301
  taskId: props.taskId,
297
302
  comment: comments.value,
303
+ attachments: attachments.value,
298
304
  approve: true,
299
305
  ccList: selectedCCList.value || []
300
306
  }
@@ -307,6 +313,7 @@ const handlePass = async () => {
307
313
  try {
308
314
  await emit('pass', params)
309
315
  comments.value = ''
316
+ attachments.value = []
310
317
  } catch (err) {
311
318
  MessagePlugin.error(err.message || '操作失败')
312
319
  }
@@ -317,6 +324,7 @@ const handleReject = async () => {
317
324
  try {
318
325
  await emit('reject', comments.value)
319
326
  comments.value = ''
327
+ attachments.value = []
320
328
  } catch (err) {
321
329
  MessagePlugin.error(err.message || '操作失败')
322
330
  }
@@ -1,73 +1,49 @@
1
1
  <template>
2
- <div class="ebiz-file-list">
2
+ <div class="ebiz-file-list" :class="{ 'mini-mode': size === 'mini' }">
3
3
  <div v-if="computedFiles.length === 0" class="empty-state">
4
4
  <t-icon name="file" size="48px" />
5
5
  <p>暂无文件</p>
6
6
  </div>
7
- <div v-else class="file-list-container">
8
- <div
9
- v-for="(file, index) in computedFiles"
10
- :key="index"
11
- class="file-item"
12
- @click="handleFileClick(file)"
13
- >
7
+ <div v-else class="file-list-container" :class="{ 'mini-container': size === 'mini' }">
8
+ <div v-for="(file, index) in computedFiles" :key="index" class="file-item"
9
+ :class="{ 'mini-item': size === 'mini' }" @click="handleFileClick(file)">
14
10
  <!-- 图片文件直接显示缩略图 -->
15
11
  <div v-if="isImage(file.extension)" class="file-icon">
16
- <t-image
17
- :src="file.url"
18
- :alt="file.name"
19
- fit="cover"
20
- loading="lazy"
21
- :style="{ width: imageSize, height: imageSize, borderRadius: '4px' }"
22
- @error="handleImageError"
23
- />
12
+ <t-image :src="file.url" :alt="file.name" fit="cover" loading="lazy"
13
+ :style="{ width: actualImageSize, height: actualImageSize, borderRadius: '4px' }" error="" />
24
14
  </div>
25
-
15
+
26
16
  <!-- 非图片文件显示图标 -->
27
17
  <div v-else class="file-icon">
28
- <t-icon :name="getFileIcon(file.extension)" :size="imageSize" :style="{ color: getFileColor(file.extension) }" />
18
+ <t-icon :name="getFileIcon(file.extension)" :size="actualImageSize"
19
+ :style="{ color: getFileColor(file.extension) }" />
29
20
  </div>
30
-
31
- <div class="file-info">
21
+
22
+ <div v-if="size !== 'mini'" class="file-info">
32
23
  <div class="file-name" :title="file.name">{{ file.name }}</div>
33
24
  <div class="file-meta">
34
25
  <span v-if="showFileType" class="file-type">{{ file.extension?.toUpperCase() || '未知' }}</span>
35
26
  <span v-if="showFileSize && file.size" class="file-size">{{ formatFileSize(file.size) }}</span>
36
27
  </div>
37
28
  </div>
38
-
29
+
39
30
  <!-- 操作按钮 -->
40
- <div v-if="showActions" class="file-actions">
41
- <t-button
42
- size="small"
43
- variant="text"
44
- @click.stop="handleDownload(file)"
45
- :title="isImage(file.extension) ? '查看' : '下载'"
46
- >
31
+ <div v-if="showActions && size !== 'mini'" class="file-actions">
32
+ <t-button size="small" variant="text" @click.stop="handleDownload(file)"
33
+ :title="isImage(file.extension) ? '查看' : '下载'">
47
34
  <t-icon :name="isImage(file.extension) ? 'view-list' : 'download'" />
48
35
  </t-button>
49
- <t-button
50
- v-if="allowDelete"
51
- size="small"
52
- variant="text"
53
- theme="danger"
54
- @click.stop="handleDelete(file, index)"
55
- title="删除"
56
- >
36
+ <t-button v-if="allowDelete" size="small" variant="text" theme="danger"
37
+ @click.stop="handleDelete(file, index)" title="删除">
57
38
  <t-icon name="delete" />
58
39
  </t-button>
59
40
  </div>
60
41
  </div>
61
42
  </div>
62
-
43
+
63
44
  <!-- 图片预览弹窗 -->
64
- <t-image-viewer
65
- v-model:visible="previewVisible"
66
- :images="previewImages"
67
- :index="previewIndex"
68
- :close-on-esc="true"
69
- @close="previewVisible = false"
70
- />
45
+ <t-image-viewer v-model:visible="previewVisible" :images="previewImages" :index="previewIndex" :close-on-esc="true"
46
+ @close="previewVisible = false" />
71
47
  </div>
72
48
  </template>
73
49
 
@@ -87,6 +63,11 @@ const props = defineProps({
87
63
  type: [Array, String],
88
64
  default: () => []
89
65
  },
66
+ // 尺寸大小,支持default和mini
67
+ size: {
68
+ type: String,
69
+ default: 'default'
70
+ },
90
71
  // 图片尺寸
91
72
  imageSize: {
92
73
  type: String,
@@ -145,18 +126,18 @@ const getFileExtension = (filename) => {
145
126
  // 提取文件名
146
127
  const extractFileName = (url) => {
147
128
  if (!url) return '未知文件';
148
-
129
+
149
130
  // 移除查询参数和锚点
150
131
  const cleanUrl = url.split('?')[0].split('#')[0];
151
132
  const parts = cleanUrl.split('/');
152
133
  let fileName = parts[parts.length - 1] || '未知文件';
153
-
134
+
154
135
  // 如果文件名为空或只是扩展名,生成一个默认名称
155
136
  if (!fileName || fileName.startsWith('.')) {
156
137
  const extension = getFileExtension(fileName || url);
157
138
  fileName = extension ? `文件.${extension}` : '未知文件';
158
139
  }
159
-
140
+
160
141
  return fileName;
161
142
  };
162
143
 
@@ -165,7 +146,7 @@ const parseFileInfo = (url) => {
165
146
  const fullUrl = getFullUrl(url);
166
147
  const fileName = extractFileName(url);
167
148
  const extension = getFileExtension(fileName);
168
-
149
+
169
150
  return {
170
151
  name: fileName,
171
152
  url: fullUrl,
@@ -177,12 +158,12 @@ const parseFileInfo = (url) => {
177
158
  // 计算文件列表
178
159
  const computedFiles = computed(() => {
179
160
  let fileList = [];
180
-
161
+
181
162
  if (typeof props.files === 'string') {
182
163
  if (!props.files.trim()) {
183
164
  return [];
184
165
  }
185
-
166
+
186
167
  // 判断是单个URL还是逗号分隔的多个URL
187
168
  if (props.files.includes(',')) {
188
169
  // 逗号分隔的多个URL
@@ -219,7 +200,7 @@ const computedFiles = computed(() => {
219
200
  }
220
201
  }).filter(file => file.url); // 过滤掉没有URL的文件
221
202
  }
222
-
203
+
223
204
  return fileList;
224
205
  });
225
206
 
@@ -235,7 +216,7 @@ const getFileIcon = (extension) => {
235
216
  // 文档类
236
217
  'pdf': 'file-pdf',
237
218
  'doc': 'file-word',
238
- 'docx': 'file-word',
219
+ 'docx': 'file-word',
239
220
  'xls': 'file-excel',
240
221
  'xlsx': 'file-excel',
241
222
  'ppt': 'file-powerpoint',
@@ -263,7 +244,7 @@ const getFileIcon = (extension) => {
263
244
  'mov': 'file-video',
264
245
  'wmv': 'file-video'
265
246
  };
266
-
247
+
267
248
  return iconMap[extension?.toLowerCase()] || 'file';
268
249
  };
269
250
 
@@ -283,22 +264,27 @@ const getFileColor = (extension) => {
283
264
  'mp3': '#FF9800',
284
265
  'mp4': '#795548'
285
266
  };
286
-
267
+
287
268
  return colorMap[extension?.toLowerCase()] || '#666666';
288
269
  };
289
270
 
271
+ // 计算实际使用的图片尺寸
272
+ const actualImageSize = computed(() => {
273
+ return props.size === 'mini' ? '20px' : props.imageSize;
274
+ });
275
+
290
276
  // 格式化文件大小
291
277
  const formatFileSize = (bytes) => {
292
278
  if (!bytes) return '';
293
279
  const units = ['B', 'KB', 'MB', 'GB'];
294
280
  let size = bytes;
295
281
  let unitIndex = 0;
296
-
282
+
297
283
  while (size >= 1024 && unitIndex < units.length - 1) {
298
284
  size /= 1024;
299
285
  unitIndex++;
300
286
  }
301
-
287
+
302
288
  return `${size.toFixed(1)} ${units[unitIndex]}`;
303
289
  };
304
290
 
@@ -317,7 +303,7 @@ const handleDownload = (file) => {
317
303
  document.body.appendChild(link);
318
304
  link.click();
319
305
  document.body.removeChild(link);
320
-
306
+
321
307
  emit('file-download', file);
322
308
  };
323
309
 
@@ -327,14 +313,14 @@ const handlePreview = (file) => {
327
313
  previewImages.value = imageFiles.map(f => f.url);
328
314
  previewIndex.value = imageFiles.findIndex(f => f.url === file.url);
329
315
  previewVisible.value = true;
330
-
316
+
331
317
  emit('file-preview', file);
332
318
  };
333
319
 
334
320
  // 文件点击处理
335
321
  const handleFileClick = (file) => {
336
322
  emit('file-click', file);
337
-
323
+
338
324
  if (isImage(file.extension)) {
339
325
  // 图片文件,显示预览
340
326
  handlePreview(file);
@@ -361,7 +347,16 @@ const handleImageError = () => {
361
347
  <style lang="less" scoped>
362
348
  .ebiz-file-list {
363
349
  width: 100%;
364
-
350
+
351
+ &.mini-mode {
352
+ .file-list-container {
353
+ display: flex;
354
+ flex-direction: row;
355
+ flex-wrap: wrap;
356
+ gap: 4px;
357
+ }
358
+ }
359
+
365
360
  .empty-state {
366
361
  display: flex;
367
362
  flex-direction: column;
@@ -369,19 +364,24 @@ const handleImageError = () => {
369
364
  justify-content: center;
370
365
  padding: 40px;
371
366
  color: #999;
372
-
367
+
373
368
  p {
374
369
  margin: 8px 0 0 0;
375
370
  font-size: 14px;
376
371
  }
377
372
  }
378
-
373
+
379
374
  .file-list-container {
380
375
  display: flex;
381
376
  flex-direction: column;
382
377
  gap: 8px;
378
+
379
+ &.mini-container {
380
+ flex-direction: row;
381
+ flex-wrap: wrap;
382
+ }
383
383
  }
384
-
384
+
385
385
  .file-item {
386
386
  display: flex;
387
387
  align-items: center;
@@ -391,31 +391,49 @@ const handleImageError = () => {
391
391
  cursor: pointer;
392
392
  transition: all 0.3s;
393
393
  background: #fff;
394
-
394
+
395
+ &.mini-item {
396
+ padding: 0;
397
+ border: none;
398
+ background: transparent;
399
+ box-shadow: none;
400
+ margin-right: 8px;
401
+ margin-bottom: 8px;
402
+
403
+ &:hover {
404
+ border-color: transparent;
405
+ box-shadow: none;
406
+ }
407
+ }
408
+
395
409
  &:hover {
396
410
  border-color: #0052d9;
397
411
  box-shadow: 0 2px 8px rgba(0, 82, 217, 0.15);
398
-
412
+
399
413
  .file-actions {
400
414
  opacity: 1;
401
415
  }
402
416
  }
403
417
  }
404
-
418
+
405
419
  .file-icon {
406
420
  margin-right: 12px;
407
421
  flex-shrink: 0;
408
422
  display: flex;
409
423
  align-items: center;
410
424
  justify-content: center;
411
- width: v-bind(imageSize);
412
- height: v-bind(imageSize);
425
+ width: v-bind(actualImageSize);
426
+ height: v-bind(actualImageSize);
427
+
428
+ .mini-item & {
429
+ margin-right: 0;
430
+ }
413
431
  }
414
-
432
+
415
433
  .file-info {
416
434
  flex: 1;
417
435
  min-width: 0;
418
-
436
+
419
437
  .file-name {
420
438
  font-size: 14px;
421
439
  font-weight: 500;
@@ -425,11 +443,11 @@ const handleImageError = () => {
425
443
  text-overflow: ellipsis;
426
444
  white-space: nowrap;
427
445
  }
428
-
446
+
429
447
  .file-meta {
430
448
  display: flex;
431
449
  gap: 8px;
432
-
450
+
433
451
  .file-size,
434
452
  .file-type {
435
453
  font-size: 12px;
@@ -437,7 +455,7 @@ const handleImageError = () => {
437
455
  }
438
456
  }
439
457
  }
440
-
458
+
441
459
  .file-actions {
442
460
  display: flex;
443
461
  gap: 4px;
@@ -446,6 +464,4 @@ const handleImageError = () => {
446
464
  flex-shrink: 0;
447
465
  }
448
466
  }
449
-
450
-
451
- </style>
467
+ </style>