@ebiz/designer-components 0.1.18 → 0.1.21

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.18",
3
+ "version": "0.1.21",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -133,20 +133,33 @@ const getFullUrl = (url) => {
133
133
  return props.baseUrl + url;
134
134
  };
135
135
 
136
- // 提取文件名
137
- const extractFileName = (url) => {
138
- if (!url) return '未知文件';
139
- const parts = url.split('/');
140
- return parts[parts.length - 1] || '未知文件';
141
- };
142
-
143
136
  // 获取文件扩展名
144
137
  const getFileExtension = (filename) => {
145
138
  if (!filename) return '';
146
- const parts = filename.split('.');
139
+ // 移除查询参数和锚点
140
+ const cleanFilename = filename.split('?')[0].split('#')[0];
141
+ const parts = cleanFilename.split('.');
147
142
  return parts.length > 1 ? parts.pop().toLowerCase() : '';
148
143
  };
149
144
 
145
+ // 提取文件名
146
+ const extractFileName = (url) => {
147
+ if (!url) return '未知文件';
148
+
149
+ // 移除查询参数和锚点
150
+ const cleanUrl = url.split('?')[0].split('#')[0];
151
+ const parts = cleanUrl.split('/');
152
+ let fileName = parts[parts.length - 1] || '未知文件';
153
+
154
+ // 如果文件名为空或只是扩展名,生成一个默认名称
155
+ if (!fileName || fileName.startsWith('.')) {
156
+ const extension = getFileExtension(fileName || url);
157
+ fileName = extension ? `文件.${extension}` : '未知文件';
158
+ }
159
+
160
+ return fileName;
161
+ };
162
+
150
163
  // 解析文件信息
151
164
  const parseFileInfo = (url) => {
152
165
  const fullUrl = getFullUrl(url);
@@ -166,24 +179,45 @@ const computedFiles = computed(() => {
166
179
  let fileList = [];
167
180
 
168
181
  if (typeof props.files === 'string') {
169
- // 字符串格式,按逗号分割
170
- const urls = props.files.split(',').map(url => url.trim()).filter(url => url);
171
- fileList = urls.map(url => parseFileInfo(url));
182
+ if (!props.files.trim()) {
183
+ return [];
184
+ }
185
+
186
+ // 判断是单个URL还是逗号分隔的多个URL
187
+ if (props.files.includes(',')) {
188
+ // 逗号分隔的多个URL
189
+ const urls = props.files.split(',').map(url => url.trim()).filter(url => url);
190
+ fileList = urls.map(url => parseFileInfo(url));
191
+ } else {
192
+ // 单个URL
193
+ fileList = [parseFileInfo(props.files.trim())];
194
+ }
172
195
  } else if (Array.isArray(props.files)) {
173
196
  // 数组格式
174
197
  fileList = props.files.map(file => {
175
198
  if (typeof file === 'string') {
199
+ // 数组中的字符串元素,直接作为URL处理
176
200
  return parseFileInfo(file);
177
- } else {
201
+ } else if (file && typeof file === 'object') {
202
+ // 对象格式的文件信息
203
+ const url = file.url || file.src || file.path || '';
178
204
  return {
179
- name: file.name || extractFileName(file.url),
180
- url: getFullUrl(file.url),
181
- size: file.size,
182
- extension: file.extension || getFileExtension(file.url || file.name),
205
+ name: file.name || extractFileName(url),
206
+ url: getFullUrl(url),
207
+ size: file.size || null,
208
+ extension: file.extension || getFileExtension(url || file.name || ''),
183
209
  ...file
184
210
  };
211
+ } else {
212
+ // 其他情况,返回默认对象
213
+ return {
214
+ name: '未知文件',
215
+ url: '',
216
+ extension: '',
217
+ size: null
218
+ };
185
219
  }
186
- });
220
+ }).filter(file => file.url); // 过滤掉没有URL的文件
187
221
  }
188
222
 
189
223
  return fileList;
@@ -254,11 +254,9 @@ const computedModelValue = computed({
254
254
  get() {
255
255
  if (props.modelValue instanceof Array) {
256
256
  return props.modelValue.map(file => {
257
-
258
257
  // 从文件路径获取文件名
259
- const fileName = file.split('/').pop();
260
258
  return {
261
- name: fileName,
259
+ name: file.name,
262
260
  url: file
263
261
  }
264
262
  });
@@ -277,11 +275,18 @@ const computedModelValue = computed({
277
275
  }
278
276
  },
279
277
  set(value) {
280
- const urls = value.map(file => file.url).filter(Boolean);
278
+ const files = [];
279
+ for( const fileItem of value ){
280
+ files.push({
281
+ url: fileItem.url,
282
+ name: fileItem.name,
283
+ size: fileItem.size
284
+ })
285
+ }
281
286
  if (props.multiple) {
282
- emit('update:modelValue', urls);
287
+ emit('update:modelValue', files);
283
288
  } else {
284
- emit('update:modelValue', urls[0]);
289
+ emit('update:modelValue', files[0]);
285
290
  }
286
291
  }
287
292
  });
@@ -23,6 +23,24 @@
23
23
  @file-download="handleFileDownload"
24
24
  />
25
25
  </div>
26
+
27
+ <div class="demo-block">
28
+ <h3>单个URL字符串</h3>
29
+ <EbizFileList
30
+ :files="singleUrlFile"
31
+ @file-click="handleFileClick"
32
+ @file-download="handleFileDownload"
33
+ />
34
+ </div>
35
+
36
+ <div class="demo-block">
37
+ <h3>纯URL数组</h3>
38
+ <EbizFileList
39
+ :files="urlArrayFiles"
40
+ @file-click="handleFileClick"
41
+ @file-download="handleFileDownload"
42
+ />
43
+ </div>
26
44
  </div>
27
45
 
28
46
  <div class="demo-section">
@@ -118,6 +136,18 @@ const arrayFiles = ref([
118
136
 
119
137
  const stringFiles = ref('https://picsum.photos/300/200?random=2,https://picsum.photos/300/200?random=3,https://example.com/document.pdf,https://example.com/music.mp3');
120
138
 
139
+ // 单个URL字符串
140
+ const singleUrlFile = ref('https://picsum.photos/400/300?random=6');
141
+
142
+ // 纯URL数组
143
+ const urlArrayFiles = ref([
144
+ 'https://picsum.photos/300/200?random=7',
145
+ 'https://picsum.photos/300/200?random=8',
146
+ 'https://example.com/sample-document.pdf',
147
+ 'https://example.com/presentation.pptx',
148
+ 'https://example.com/data-export.xlsx'
149
+ ]);
150
+
121
151
  const mixedFiles = ref([
122
152
  {
123
153
  name: '技术文档.pdf',
@@ -1,12 +1,16 @@
1
1
  <template>
2
2
  <div class="upload-demo-container">
3
3
  <h2>上传组件示例</h2>
4
+
5
+ {{ fileList }}
4
6
 
5
7
  <div class="demo-section">
6
8
  <h3>基础用法</h3>
7
9
  <EbizUpload
10
+ v-model="fileList"
8
11
  :useInternalUpload="true"
9
12
  :auto-upload="true"
13
+ :multiple="true"
10
14
  tips="请选择文件上传"
11
15
  >
12
16
  <template #default>
@@ -99,6 +103,9 @@ export default {
99
103
  <script setup>
100
104
  import { EbizUpload } from '../index.js';
101
105
  import { Button as TButton } from 'tdesign-vue-next';
106
+ import { ref } from "vue";
107
+
108
+ const fileList = ref([])
102
109
  </script>
103
110
 
104
111
  <style lang="less" scoped>
@@ -1,239 +0,0 @@
1
- <template>
2
- <div class="ebiz-file-upload">
3
- <t-upload
4
- ref="uploadRef"
5
- v-model="fileList"
6
- :action="uploadUrl"
7
- :headers="headers"
8
- :multiple="multiple"
9
- :accept="accept"
10
- :max-size="sizeLimit"
11
- :disabled="disabled"
12
- :drag="draggable"
13
- :theme="tTheme"
14
- :tips="tips"
15
- :show-upload-progress="showUploadProgress"
16
- :before-upload="handleBeforeUpload"
17
- :on-success="handleSuccess"
18
- :on-error="handleError"
19
- @change="handleChange"
20
- >
21
- <template #default v-if="customTrigger">
22
- <slot name="trigger"></slot>
23
- </template>
24
- <template #trigger v-else>
25
- <t-button :size="btnSize" :theme="buttonType" :variant="buttonPlain ? 'outline' : 'base'">
26
- {{ buttonText }}
27
- </t-button>
28
- </template>
29
- </t-upload>
30
- </div>
31
- </template>
32
-
33
- <script setup>
34
- import { ref, computed, watch } from 'vue';
35
- import { Upload as TUpload, Button as TButton } from 'tdesign-vue-next';
36
-
37
- /**
38
- * EbizFileUpload 组件 - 基于 TDesign Upload 的文件上传组件
39
- * 支持自定义上传地址、文件类型限制、大小限制、多文件上传等
40
- * 提供成功和失败的回调处理
41
- */
42
- const props = defineProps({
43
- /**
44
- * 上传地址
45
- */
46
- action: {
47
- type: String,
48
- default: '/api/file/upload'
49
- },
50
- /**
51
- * 上传请求头信息
52
- */
53
- headers: {
54
- type: Object,
55
- default: () => ({})
56
- },
57
- /**
58
- * 是否允许多文件上传
59
- */
60
- multiple: {
61
- type: Boolean,
62
- default: false
63
- },
64
- /**
65
- * 接受上传的文件类型
66
- */
67
- accept: {
68
- type: String,
69
- default: '*'
70
- },
71
- /**
72
- * 文件大小限制,单位 KB
73
- */
74
- sizeLimit: {
75
- type: Number,
76
- default: 5 * 1024 // 默认5MB
77
- },
78
- /**
79
- * 是否禁用上传
80
- */
81
- disabled: {
82
- type: Boolean,
83
- default: false
84
- },
85
- /**
86
- * 是否支持拖拽上传
87
- */
88
- draggable: {
89
- type: Boolean,
90
- default: false
91
- },
92
- /**
93
- * 上传组件主题
94
- */
95
- theme: {
96
- type: String,
97
- default: 'text',
98
- validator: (val) => ['text', 'button', 'both'].includes(val)
99
- },
100
- /**
101
- * 上传按钮文本
102
- */
103
- buttonText: {
104
- type: String,
105
- default: '上传文件'
106
- },
107
- /**
108
- * 上传提示文本
109
- */
110
- tips: {
111
- type: String,
112
- default: ''
113
- },
114
- /**
115
- * 是否显示上传进度
116
- */
117
- showUploadProgress: {
118
- type: Boolean,
119
- default: true
120
- },
121
- /**
122
- * 初始文件列表
123
- */
124
- initialFiles: {
125
- type: Array,
126
- default: () => []
127
- },
128
- /**
129
- * 按钮大小
130
- */
131
- buttonSize: {
132
- type: String,
133
- default: 'medium',
134
- validator: (val) => ['medium', 'small', 'mini'].includes(val)
135
- },
136
- /**
137
- * 按钮类型
138
- */
139
- buttonType: {
140
- type: String,
141
- default: 'primary',
142
- validator: (val) => ['default', 'primary', 'success', 'warning', 'danger', 'info', 'text'].includes(val)
143
- },
144
- /**
145
- * 按钮是否为朴素按钮
146
- */
147
- buttonPlain: {
148
- type: Boolean,
149
- default: false
150
- },
151
- /**
152
- * 是否使用自定义触发器
153
- */
154
- customTrigger: {
155
- type: Boolean,
156
- default: false
157
- }
158
- });
159
-
160
- const emit = defineEmits(['update:files', 'success', 'error', 'change']);
161
-
162
- // 文件列表状态
163
- const fileList = ref(props.initialFiles || []);
164
-
165
- // 计算上传地址
166
- const uploadUrl = computed(() => props.action);
167
-
168
- // 将tiny主题映射到tdesign主题
169
- const tTheme = computed(() => {
170
- switch (props.theme) {
171
- case 'button': return 'button';
172
- case 'both': return 'file-input';
173
- default: return 'file';
174
- }
175
- });
176
-
177
- // 将tiny按钮大小映射到tdesign按钮大小
178
- const btnSize = computed(() => {
179
- switch (props.buttonSize) {
180
- case 'mini': return 'small';
181
- case 'small': return 'medium';
182
- default: return 'large';
183
- }
184
- });
185
-
186
- // 上传前处理
187
- const handleBeforeUpload = (_file) => {
188
- // 可以在这里添加文件校验逻辑
189
- return true;
190
- };
191
-
192
- // 上传成功处理
193
- const handleSuccess = (result, file, fileList) => {
194
- emit('success', { file: file.raw, response: result, fileList });
195
- };
196
-
197
- // 上传失败处理
198
- const handleError = (error, file, fileList) => {
199
- emit('error', { file: file.raw, error, fileList });
200
- };
201
-
202
- // 文件变更处理
203
- const handleChange = (value, context) => {
204
- emit('update:files', value);
205
- emit('change', { file: context.file?.raw, fileList: value });
206
- };
207
-
208
- // 监听initialFiles变化更新文件列表
209
- watch(() => props.initialFiles, (newFiles) => {
210
- if (newFiles && newFiles.length > 0) {
211
- fileList.value = [...newFiles];
212
- }
213
- });
214
- </script>
215
-
216
- <style scoped>
217
- .ebiz-file-upload {
218
- width: 100%;
219
- margin: 16px 0;
220
- }
221
-
222
- :deep(.t-upload__tips) {
223
- font-size: 12px;
224
- color: rgba(0, 0, 0, 0.6);
225
- }
226
-
227
- :deep(.t-upload__dragger) {
228
- padding: 24px;
229
- background-color: var(--td-bg-color-container);
230
- border: 1px dashed var(--td-component-border);
231
- border-radius: 3px;
232
- cursor: pointer;
233
- transition: border-color 0.2s cubic-bezier(0.38, 0, 0.24, 1);
234
- }
235
-
236
- :deep(.t-upload__dragger:hover) {
237
- border-color: var(--td-brand-color);
238
- }
239
- </style>