tree-upload-vue3 1.1.10 → 1.1.12

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/README.md CHANGED
@@ -37,7 +37,7 @@ npm install tree-upload-vue3 element-plus @element-plus/icons-vue file-preview-v
37
37
  <TreeUpload
38
38
  ref="treeUploadRef"
39
39
  :schema="mySchema"
40
- mode="edit"
40
+ mode="view"
41
41
  @upload-before="handleBeforeUpload"
42
42
  @upload-success="handleUploadSuccess"
43
43
  />
@@ -51,9 +51,9 @@ import 'tree-upload-vue3/dist/tree-upload-vue3.css'; // 引入样式
51
51
 
52
52
  const treeUploadRef = ref();
53
53
 
54
- const handleBeforeUpload = ({ file, context }) => {
54
+ const handleBeforeUpload = ({ file, context, currentNode }) => {
55
55
  console.log('准备上传:', file.name);
56
- console.log('当前选中节点:', context.variables.currentNode);
56
+ console.log('当前选中节点:', currentNode || context.variables.currentNode);
57
57
  };
58
58
 
59
59
  const handleUploadSuccess = (response) => {
@@ -69,9 +69,18 @@ const mySchema = reactive<TreeUploadSchema>({
69
69
  {
70
70
  id: '1',
71
71
  label: '必填文档',
72
+ mode: 'edit',
72
73
  required: true,
73
74
  minCount: 1,
74
75
  accept: '.pdf',
76
+ beforeUpload: ({ file }) => {
77
+ return !file.name.startsWith('tmp_');
78
+ },
79
+ meta: {
80
+ upload: {
81
+ beforeUpload: ({ file }) => file.name.endsWith('.pdf')
82
+ }
83
+ },
75
84
  children: []
76
85
  }
77
86
  ]
@@ -85,6 +94,17 @@ const mySchema = reactive<TreeUploadSchema>({
85
94
  enabled: true,
86
95
  action: 'https://api.example.com/upload',
87
96
  multiple: true,
97
+ beforeUpload: ({ file }) => {
98
+ return file.size < 20 * 1024 * 1024;
99
+ },
100
+ // 可选:转换接口响应为组件所需的 FileItem 格式
101
+ transformResponse: (response) => {
102
+ return {
103
+ id: response.data.id,
104
+ name: response.data.filename,
105
+ url: response.data.url
106
+ };
107
+ },
88
108
  ui: {
89
109
  showTip: true // 自动显示 "支持 .pdf (最少 1 个)" 等提示
90
110
  }
@@ -110,13 +130,13 @@ const mySchema = reactive<TreeUploadSchema>({
110
130
  | 属性名 | 类型 | 默认值 | 说明 |
111
131
  | --- | --- | --- | --- |
112
132
  | `schema` | `TreeUploadSchema` | **必填** | 组件的核心配置对象 |
113
- | `mode` | `'view' \| 'edit'` | `'edit'` | 视图模式。`view` 模式下隐藏上传和模板区域 |
133
+ | `mode` | `'view' \| 'edit'` | `'edit'` | 全局视图模式。可被节点级 `mode` 覆盖 |
114
134
 
115
135
  ### Events
116
136
 
117
137
  | 事件名 | 参数 | 说明 |
118
138
  | --- | --- | --- |
119
- | `upload-before` | `{ file: File, context: SchemaContext }` | 上传前触发,包含文件对象和当前上下文(如选中节点) |
139
+ | `upload-before` | `{ file: File, context: SchemaContext, currentNode?: CategoryNode }` | 上传前触发,包含文件对象和当前上下文(如选中节点) |
120
140
  | `upload-success` | `response: any` | 上传成功后触发 |
121
141
  | `upload-error` | `error: any` | 上传失败后触发 |
122
142
  | `delete-before` | `file: FileItem` | 用户点击删除确认后,执行删除逻辑前触发 |
@@ -140,19 +160,38 @@ const mySchema = reactive<TreeUploadSchema>({
140
160
 
141
161
  **节点配置 (CategoryNode)**:
142
162
  - `required`: 是否必须上传文件。
163
+ - `mode`: 节点模式。可单独设置为 `view` 或 `edit`,优先级高于组件 `mode`。
143
164
  - `minCount` / `maxCount`: 文件数量限制。
144
165
  - `maxSize`: 单个文件大小限制 (Bytes)。
145
166
  - `accept`: 允许的文件类型 (如 `.jpg,.png`)。
167
+ - `beforeUpload`: 节点级上传前校验钩子,返回 `false` 可阻止上传。
168
+ - `meta.upload.beforeUpload`: 节点 meta 内上传前校验钩子,返回 `false` 可阻止上传。
146
169
  - `meta.templates`: 模板文件列表,选中节点时会自动显示在顶部。
147
170
 
171
+ 模式优先级:
172
+ - `currentNode.mode`
173
+ - `currentNode.meta.mode`
174
+ - 组件 `mode`(`<TreeUpload mode="...">`)
175
+
176
+ 当最终模式为 `view` 时,上传区隐藏且文件删除操作不可用;为 `edit` 时可上传并可删除。
177
+
148
178
  #### 2. Upload Schema (`upload`)
149
179
 
150
180
  | 字段 | 类型 | 说明 |
151
181
  | --- | --- | --- |
152
182
  | `action` | `string` | 上传接口地址 |
153
183
  | `accept` | `string` | 全局文件类型限制 (会被节点配置覆盖) |
184
+ | `beforeUpload` | `(payload) => boolean \| void \| Promise<boolean \| void>` | 全局上传前校验钩子,返回 `false` 阻止上传 |
185
+ | `transformResponse` | `(res: any) => Partial<FileItem>` | 将接口响应转换为组件所需的 `FileItem` 格式 |
154
186
  | `ui.showTip` | `boolean` | 是否显示动态提示文案 (自动聚合大小、数量、格式限制) |
155
187
 
188
+ 上传前钩子执行顺序:
189
+ - `upload.beforeUpload`(全局)
190
+ - `currentNode.meta.upload.beforeUpload`(节点 meta)
191
+ - `currentNode.beforeUpload`(节点字段)
192
+
193
+ 任意一个钩子返回 `false` 都会取消上传。
194
+
156
195
  #### 3. Table Schema (`table`)
157
196
 
158
197
  | 字段 | 类型 | 说明 |
@@ -161,6 +200,10 @@ const mySchema = reactive<TreeUploadSchema>({
161
200
  | `columns` | `TableColumnSchema[]` | 列定义。支持 `formatter: 'fileSize'` |
162
201
  | `ui.pagination` | `{ enabled: boolean, pageSize: number }` | 分页配置 (无数据时自动隐藏) |
163
202
 
203
+ 行级按钮控制(FileItem):
204
+ - `actions`: 当前行允许的操作列表,支持字符串数组或对象数组。
205
+ - `disabledActions`: 当前行禁用的操作 key 列表。
206
+
164
207
  ## 🛠 开发与构建
165
208
 
166
209
  ```bash
@@ -2,6 +2,7 @@ import { TableSchema, FileItem } from '../types';
2
2
  type __VLS_Props = {
3
3
  schema: TableSchema;
4
4
  files?: FileItem[];
5
+ mode?: 'view' | 'edit';
5
6
  };
6
7
  declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
7
8
  action: (...args: any[]) => void;
@@ -1 +1 @@
1
- .schema-tree{height:100%;position:relative;display:flex;flex-direction:column}.tree-toolbar{padding:8px;border-bottom:1px solid var(--el-border-color-lighter);display:flex;gap:8px}.custom-tree-node{flex:1;display:flex;align-items:center;justify-content:space-between;font-size:14px;padding-right:8px}.custom-tree-node .actions{display:none}.custom-tree-node:hover .actions{display:inline-flex}.context-menu{position:fixed;z-index:1000;background:var(--el-bg-color-overlay);border:1px solid var(--el-border-color);box-shadow:var(--el-box-shadow-light);border-radius:4px;padding:5px 0;min-width:120px}.context-menu-item{padding:8px 16px;cursor:pointer;display:flex;align-items:center;gap:8px;font-size:13px;color:var(--el-text-color-regular)}.context-menu-item:hover{background-color:var(--el-fill-color-light);color:var(--el-color-primary)}.node-label{display:flex;align-items:center}.required-star{color:var(--el-color-danger);margin-right:4px;font-weight:700}.schema-upload[data-v-607d52fb]{width:100%}.schema-table{height:100%;display:flex;flex-direction:column}.table-toolbar{margin-bottom:12px}.pagination-wrapper{margin-top:12px;display:flex;justify-content:flex-end}.schema-template{margin-bottom:12px;background-color:var(--el-fill-color-light);padding:8px 12px;border-radius:4px;border:1px solid var(--el-border-color-light)}.template-container{display:flex;align-items:center;flex-wrap:wrap;gap:8px}.label{font-size:13px;color:var(--el-text-color-secondary);white-space:nowrap}.tags-wrapper{display:flex;flex-wrap:wrap;gap:8px}.template-tag{cursor:pointer;border:none;background-color:var(--el-bg-color);transition:all .2s}.template-tag:hover{color:var(--el-color-primary);transform:translateY(-1px);box-shadow:var(--el-box-shadow-lighter)}.tag-content{display:flex;align-items:center;gap:4px}.tpl-name{line-height:1}.preview-content{height:100%;min-height:500px;display:flex;flex-direction:column}.custom-header{display:flex;justify-content:space-between;align-items:center;padding-right:0}.header-controls{display:flex;align-items:center;gap:8px}.tree-upload-container{display:flex;height:100%;min-height:500px;border:1px solid var(--el-border-color);background:var(--el-bg-color);box-sizing:border-box;position:relative}.tree-pane{border-right:1px solid var(--el-border-color);height:100%;overflow-y:auto;padding:10px;box-sizing:border-box;flex-shrink:0}.resize-handle{width:5px;height:100%;cursor:col-resize;background-color:transparent;margin-left:-3px;z-index:10;position:relative;transition:background-color .2s}.resize-handle:hover,.resize-handle:active{background-color:var(--el-color-primary);opacity:.5}.tree-upload-content-pane{flex:1;display:flex;flex-direction:column;padding:16px;overflow:hidden}.top-section{margin-bottom:16px}.table-section{flex:1;overflow:hidden}
1
+ .schema-tree{height:100%;position:relative;display:flex;flex-direction:column}.tree-toolbar{padding:8px;border-bottom:1px solid var(--el-border-color-lighter);display:flex;gap:8px}.custom-tree-node{flex:1;display:flex;align-items:center;justify-content:space-between;font-size:14px;padding-right:8px}.custom-tree-node .actions{display:none}.custom-tree-node:hover .actions{display:inline-flex}.context-menu{position:fixed;z-index:1000;background:var(--el-bg-color-overlay);border:1px solid var(--el-border-color);box-shadow:var(--el-box-shadow-light);border-radius:4px;padding:5px 0;min-width:120px}.context-menu-item{padding:8px 16px;cursor:pointer;display:flex;align-items:center;gap:8px;font-size:13px;color:var(--el-text-color-regular)}.context-menu-item:hover{background-color:var(--el-fill-color-light);color:var(--el-color-primary)}.node-label{display:flex;align-items:center}.required-star{color:var(--el-color-danger);margin-right:4px;font-weight:700}.schema-upload[data-v-47902547]{width:100%}.schema-table{height:100%;display:flex;flex-direction:column}.table-toolbar{margin-bottom:12px}.pagination-wrapper{margin-top:12px;display:flex;justify-content:flex-end}.schema-template{margin-bottom:12px;background-color:var(--el-fill-color-light);padding:8px 12px;border-radius:4px;border:1px solid var(--el-border-color-light)}.template-container{display:flex;align-items:center;flex-wrap:wrap;gap:8px}.label{font-size:13px;color:var(--el-text-color-secondary);white-space:nowrap}.tags-wrapper{display:flex;flex-wrap:wrap;gap:8px}.template-tag{cursor:pointer;border:none;background-color:var(--el-bg-color);transition:all .2s}.template-tag:hover{color:var(--el-color-primary);transform:translateY(-1px);box-shadow:var(--el-box-shadow-lighter)}.tag-content{display:flex;align-items:center;gap:4px}.tpl-name{line-height:1}.preview-content{height:100%;min-height:500px;display:flex;flex-direction:column}.custom-header{display:flex;justify-content:space-between;align-items:center;padding-right:0}.header-controls{display:flex;align-items:center;gap:8px}.tree-upload-container{display:flex;height:100%;min-height:500px;border:1px solid var(--el-border-color);background:var(--el-bg-color);box-sizing:border-box;position:relative}.tree-pane{border-right:1px solid var(--el-border-color);height:100%;overflow-y:auto;padding:10px;box-sizing:border-box;flex-shrink:0}.resize-handle{width:5px;height:100%;cursor:col-resize;background-color:transparent;margin-left:-3px;z-index:10;position:relative;transition:background-color .2s}.resize-handle:hover,.resize-handle:active{background-color:var(--el-color-primary);opacity:.5}.tree-upload-content-pane{flex:1;display:flex;flex-direction:column;padding:16px;overflow:hidden}.top-section{margin-bottom:16px}.table-section{flex:1;overflow:hidden}