tree-upload-vue3 1.1.11 → 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,9 @@ 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
+ },
88
100
  // 可选:转换接口响应为组件所需的 FileItem 格式
89
101
  transformResponse: (response) => {
90
102
  return {
@@ -118,13 +130,13 @@ const mySchema = reactive<TreeUploadSchema>({
118
130
  | 属性名 | 类型 | 默认值 | 说明 |
119
131
  | --- | --- | --- | --- |
120
132
  | `schema` | `TreeUploadSchema` | **必填** | 组件的核心配置对象 |
121
- | `mode` | `'view' \| 'edit'` | `'edit'` | 视图模式。`view` 模式下隐藏上传和模板区域 |
133
+ | `mode` | `'view' \| 'edit'` | `'edit'` | 全局视图模式。可被节点级 `mode` 覆盖 |
122
134
 
123
135
  ### Events
124
136
 
125
137
  | 事件名 | 参数 | 说明 |
126
138
  | --- | --- | --- |
127
- | `upload-before` | `{ file: File, context: SchemaContext }` | 上传前触发,包含文件对象和当前上下文(如选中节点) |
139
+ | `upload-before` | `{ file: File, context: SchemaContext, currentNode?: CategoryNode }` | 上传前触发,包含文件对象和当前上下文(如选中节点) |
128
140
  | `upload-success` | `response: any` | 上传成功后触发 |
129
141
  | `upload-error` | `error: any` | 上传失败后触发 |
130
142
  | `delete-before` | `file: FileItem` | 用户点击删除确认后,执行删除逻辑前触发 |
@@ -148,20 +160,38 @@ const mySchema = reactive<TreeUploadSchema>({
148
160
 
149
161
  **节点配置 (CategoryNode)**:
150
162
  - `required`: 是否必须上传文件。
163
+ - `mode`: 节点模式。可单独设置为 `view` 或 `edit`,优先级高于组件 `mode`。
151
164
  - `minCount` / `maxCount`: 文件数量限制。
152
165
  - `maxSize`: 单个文件大小限制 (Bytes)。
153
166
  - `accept`: 允许的文件类型 (如 `.jpg,.png`)。
167
+ - `beforeUpload`: 节点级上传前校验钩子,返回 `false` 可阻止上传。
168
+ - `meta.upload.beforeUpload`: 节点 meta 内上传前校验钩子,返回 `false` 可阻止上传。
154
169
  - `meta.templates`: 模板文件列表,选中节点时会自动显示在顶部。
155
170
 
171
+ 模式优先级:
172
+ - `currentNode.mode`
173
+ - `currentNode.meta.mode`
174
+ - 组件 `mode`(`<TreeUpload mode="...">`)
175
+
176
+ 当最终模式为 `view` 时,上传区隐藏且文件删除操作不可用;为 `edit` 时可上传并可删除。
177
+
156
178
  #### 2. Upload Schema (`upload`)
157
179
 
158
180
  | 字段 | 类型 | 说明 |
159
181
  | --- | --- | --- |
160
182
  | `action` | `string` | 上传接口地址 |
161
183
  | `accept` | `string` | 全局文件类型限制 (会被节点配置覆盖) |
184
+ | `beforeUpload` | `(payload) => boolean \| void \| Promise<boolean \| void>` | 全局上传前校验钩子,返回 `false` 阻止上传 |
162
185
  | `transformResponse` | `(res: any) => Partial<FileItem>` | 将接口响应转换为组件所需的 `FileItem` 格式 |
163
186
  | `ui.showTip` | `boolean` | 是否显示动态提示文案 (自动聚合大小、数量、格式限制) |
164
187
 
188
+ 上传前钩子执行顺序:
189
+ - `upload.beforeUpload`(全局)
190
+ - `currentNode.meta.upload.beforeUpload`(节点 meta)
191
+ - `currentNode.beforeUpload`(节点字段)
192
+
193
+ 任意一个钩子返回 `false` 都会取消上传。
194
+
165
195
  #### 3. Table Schema (`table`)
166
196
 
167
197
  | 字段 | 类型 | 说明 |
@@ -170,6 +200,10 @@ const mySchema = reactive<TreeUploadSchema>({
170
200
  | `columns` | `TableColumnSchema[]` | 列定义。支持 `formatter: 'fileSize'` |
171
201
  | `ui.pagination` | `{ enabled: boolean, pageSize: number }` | 分页配置 (无数据时自动隐藏) |
172
202
 
203
+ 行级按钮控制(FileItem):
204
+ - `actions`: 当前行允许的操作列表,支持字符串数组或对象数组。
205
+ - `disabledActions`: 当前行禁用的操作 key 列表。
206
+
173
207
  ## 🛠 开发与构建
174
208
 
175
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}