@skyfox2000/webui 1.4.6 → 1.4.7
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/lib/TemplateFile.d.ts +1 -0
- package/lib/assets/modules/{baseLayout-CUZpKmC0.js → baseLayout-DOrecPPK.js} +6 -6
- package/lib/assets/modules/{file-upload-BrTPBL29.js → file-upload-lbkluj3p.js} +1 -1
- package/lib/assets/modules/{index-DP1u2P1k.js → index-B1MSCuqH.js} +2 -2
- package/lib/assets/modules/{index-BqoqvtEv.js → index-BLNKcgk-.js} +1 -1
- package/lib/assets/modules/{index-NDOzpgzV.js → index-DQ-VDAr-.js} +10 -10
- package/lib/assets/modules/{menuTabs-BaZAmaXz.js → menuTabs-DOtYQ9Zu.js} +7 -7
- package/lib/assets/modules/{toolIcon-DHKszGtC.js → toolIcon-CqJWc5B7.js} +1 -1
- package/lib/assets/modules/{uploadList-C5R2XD5x.js → uploadList-3SuA7du0.js} +150 -138
- package/lib/assets/modules/{uploadList-DSN9t4r8.js → uploadList-DafDrGgh.js} +143 -133
- package/lib/components/content/tree/index.vue.d.ts +3 -14
- package/lib/components/form/upload/uploadList.vue.d.ts +3 -0
- package/lib/es/AceEditor/index.js +3 -3
- package/lib/es/BasicLayout/index.js +6 -6
- package/lib/es/Error403/index.js +1 -1
- package/lib/es/Error404/index.js +1 -1
- package/lib/es/ExcelForm/index.js +5 -5
- package/lib/es/MenuLayout/index.js +6 -6
- package/lib/es/TemplateFile/index.js +208 -0
- package/lib/es/UploadForm/index.js +6 -6
- package/lib/utils/tree.d.ts +9 -0
- package/lib/webui.css +1 -1
- package/lib/webui.es.js +396 -378
- package/package.json +8 -2
- package/src/components/content/dialog/templateFile.vue +259 -0
- package/src/components/content/dialog/uploadForm.vue +6 -14
- package/src/components/content/tree/index.vue +26 -25
- package/src/components/form/upload/uploadList.vue +66 -89
- package/src/utils/tree.ts +20 -0
- package/.eslintrc.js +0 -23
- package/.prettierrc +0 -11
- package/.vscode/settings.json +0 -25
- package/env.d.ts +0 -11
- package/index.html +0 -19
- package/plugins/vite-plugin-auto-generate-vue.ts +0 -105
- package/postcss.config.ts +0 -6
- package/tailwind.config.ts +0 -11
- package/tsconfig.json +0 -46
- package/vite.config.ts +0 -120
- package//344/273/243/347/240/201/350/247/204/350/214/203/345/217/212/351/243/216/346/240/274/346/214/207/345/215/227.md +0 -116
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skyfox2000/webui",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.7",
|
|
4
4
|
"description": "后台前端通用组件定义",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [],
|
|
@@ -24,6 +24,12 @@
|
|
|
24
24
|
"types": "./lib/*.d.ts"
|
|
25
25
|
}
|
|
26
26
|
},
|
|
27
|
+
"files": [
|
|
28
|
+
"lib",
|
|
29
|
+
"src",
|
|
30
|
+
"package.json",
|
|
31
|
+
"README.md"
|
|
32
|
+
],
|
|
27
33
|
"scripts": {
|
|
28
34
|
"build": "pnpm i && vue-tsc -b && vite build",
|
|
29
35
|
"build:dts": "vue-tsc -b && vite-dts-declaration",
|
|
@@ -65,4 +71,4 @@
|
|
|
65
71
|
"vite-plugin-dts": "^4.5.4",
|
|
66
72
|
"vue-tsc": "^2.2.0"
|
|
67
73
|
}
|
|
68
|
-
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { watch, ref, onMounted, PropType } from 'vue';
|
|
3
|
+
import { Button } from '../../common';
|
|
4
|
+
import { Modal, Space } from 'ant-design-vue';
|
|
5
|
+
import { doQuery, EditorControl, MinioFile, UploadFile, UploadStatus } from '@/index';
|
|
6
|
+
import { AnyData, ApiResponse, IUrlInfo, ResStatus } from '@skyfox2000/fapi';
|
|
7
|
+
import UploadList from '../../form/upload/uploadList.vue';
|
|
8
|
+
import { AsyncUploader } from '@/utils/file-upload';
|
|
9
|
+
|
|
10
|
+
const props = defineProps({
|
|
11
|
+
/**
|
|
12
|
+
* 模板API域名配置
|
|
13
|
+
* 默认使用父级API
|
|
14
|
+
*/
|
|
15
|
+
api: {
|
|
16
|
+
type: String as PropType<string>,
|
|
17
|
+
required: false,
|
|
18
|
+
},
|
|
19
|
+
/**
|
|
20
|
+
* 模板类别
|
|
21
|
+
*/
|
|
22
|
+
templateType: {
|
|
23
|
+
type: String as PropType<string>,
|
|
24
|
+
required: true,
|
|
25
|
+
},
|
|
26
|
+
/**
|
|
27
|
+
* 文件后缀限制
|
|
28
|
+
*/
|
|
29
|
+
fileExt: {
|
|
30
|
+
type: Array as PropType<string[]>,
|
|
31
|
+
required: false,
|
|
32
|
+
},
|
|
33
|
+
/**
|
|
34
|
+
* 弹窗控制器
|
|
35
|
+
*/
|
|
36
|
+
uploadForm: {
|
|
37
|
+
type: Object as PropType<EditorControl<AnyData>>,
|
|
38
|
+
required: true,
|
|
39
|
+
},
|
|
40
|
+
/**
|
|
41
|
+
* 文件列表地址
|
|
42
|
+
*/
|
|
43
|
+
fileListUrl: {
|
|
44
|
+
type: Object as PropType<IUrlInfo>,
|
|
45
|
+
required: false,
|
|
46
|
+
},
|
|
47
|
+
/**
|
|
48
|
+
* 上传地址和参数
|
|
49
|
+
*/
|
|
50
|
+
uploadUrl: {
|
|
51
|
+
type: Object as PropType<IUrlInfo>,
|
|
52
|
+
required: false,
|
|
53
|
+
},
|
|
54
|
+
/**
|
|
55
|
+
* 下载地址和参数
|
|
56
|
+
*/
|
|
57
|
+
downloadUrl: {
|
|
58
|
+
type: Object as PropType<IUrlInfo>,
|
|
59
|
+
required: false,
|
|
60
|
+
},
|
|
61
|
+
/**
|
|
62
|
+
* 删除地址和参数
|
|
63
|
+
*/
|
|
64
|
+
deleteUrl: {
|
|
65
|
+
type: Object as PropType<IUrlInfo>,
|
|
66
|
+
required: false,
|
|
67
|
+
},
|
|
68
|
+
/**
|
|
69
|
+
* 是否显示删除按钮
|
|
70
|
+
*/
|
|
71
|
+
showDelete: {
|
|
72
|
+
type: Boolean as PropType<boolean>,
|
|
73
|
+
required: false,
|
|
74
|
+
default: true,
|
|
75
|
+
},
|
|
76
|
+
/**
|
|
77
|
+
* 最多上传文件数
|
|
78
|
+
*/
|
|
79
|
+
maxCount: {
|
|
80
|
+
type: Number as PropType<number>,
|
|
81
|
+
required: false,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
/**
|
|
85
|
+
* minio桶名称template
|
|
86
|
+
*
|
|
87
|
+
* 子目录templateType
|
|
88
|
+
*/
|
|
89
|
+
const fileListUrl = ref(props.fileListUrl || {
|
|
90
|
+
api: props.api,
|
|
91
|
+
authorize: true,
|
|
92
|
+
url: '/api/TemplateFileSrv/list',
|
|
93
|
+
});
|
|
94
|
+
const uploadUrl = ref(props.uploadUrl || {
|
|
95
|
+
api: props.api,
|
|
96
|
+
authorize: true,
|
|
97
|
+
url: '/api/TemplateFileSrv/upload',
|
|
98
|
+
});
|
|
99
|
+
const downloadUrl = ref(props.downloadUrl || {
|
|
100
|
+
api: props.api,
|
|
101
|
+
authorize: true,
|
|
102
|
+
url: '/api/TemplateFileSrv/download',
|
|
103
|
+
});
|
|
104
|
+
const deleteUrl = ref(props.deleteUrl || {
|
|
105
|
+
api: props.api,
|
|
106
|
+
authorize: true,
|
|
107
|
+
url: '/api/TemplateFileSrv/delete',
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const uploadFormCtrl = props.uploadForm;
|
|
111
|
+
const open = ref<boolean>(false);
|
|
112
|
+
|
|
113
|
+
const fileList = ref<UploadFile[]>([]);
|
|
114
|
+
const emit = defineEmits<{
|
|
115
|
+
/**
|
|
116
|
+
* 显示预处理
|
|
117
|
+
*/
|
|
118
|
+
'before:file-list': [EditorControl<AnyData>, UploadFile[]];
|
|
119
|
+
/**
|
|
120
|
+
* 上传前预处理
|
|
121
|
+
*/
|
|
122
|
+
'before:upload': [UploadFile[]];
|
|
123
|
+
/**
|
|
124
|
+
* 上传结束,处理上传后的文件
|
|
125
|
+
*/
|
|
126
|
+
'after:upload': [boolean, UploadFile[]];
|
|
127
|
+
/**
|
|
128
|
+
* 上传进度
|
|
129
|
+
*/
|
|
130
|
+
'on:progress': [UploadFile];
|
|
131
|
+
}>();
|
|
132
|
+
|
|
133
|
+
watch(
|
|
134
|
+
() => uploadFormCtrl.visible.value,
|
|
135
|
+
() => {
|
|
136
|
+
open.value = uploadFormCtrl.visible.value;
|
|
137
|
+
},
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* 加载现有模板文件列表
|
|
143
|
+
*/
|
|
144
|
+
const loadFileList = async () => {
|
|
145
|
+
const res: ApiResponse<MinioFile[]> | null = await doQuery<MinioFile[]>(props.uploadForm, {
|
|
146
|
+
urlKey: 'list',
|
|
147
|
+
url: fileListUrl.value,
|
|
148
|
+
params: {
|
|
149
|
+
Query: {
|
|
150
|
+
PrefixPath: props.templateType,
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
if (res?.status === ResStatus.SUCCESS && res.data) {
|
|
155
|
+
const files = res.data as MinioFile[];
|
|
156
|
+
const list = files.map((file) => {
|
|
157
|
+
const uploadFile: UploadFile = {
|
|
158
|
+
uid: file.Key,
|
|
159
|
+
size: file.Size,
|
|
160
|
+
name: file.FileName!,
|
|
161
|
+
status: UploadStatus.Online,
|
|
162
|
+
lastModified: file.UpdateTime ? new Date(file.UpdateTime).getTime() : 0,
|
|
163
|
+
lastModifiedDate: file.UpdateTime ? new Date(file.UpdateTime) : new Date(),
|
|
164
|
+
params: {},
|
|
165
|
+
minioFile: file,
|
|
166
|
+
}
|
|
167
|
+
return uploadFile;
|
|
168
|
+
})
|
|
169
|
+
emit('before:file-list', uploadFormCtrl, list);
|
|
170
|
+
fileList.value.push(...list);
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const dialogSave = async () => {
|
|
175
|
+
emit('before:upload', fileList.value);
|
|
176
|
+
const uploader = new AsyncUploader(uploadUrl.value, 3);
|
|
177
|
+
await uploader.doUpload(
|
|
178
|
+
fileList.value,
|
|
179
|
+
uploadFormCtrl.isFormLoading,
|
|
180
|
+
true,
|
|
181
|
+
async (result, files) => {
|
|
182
|
+
emit('after:upload', result, files);
|
|
183
|
+
if (result) {
|
|
184
|
+
uploadFormCtrl.visible.value = false;
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
(file) => {
|
|
188
|
+
emit('on:progress', file);
|
|
189
|
+
},
|
|
190
|
+
);
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
onMounted(() => {
|
|
194
|
+
if (!fileListUrl.value.api) {
|
|
195
|
+
fileListUrl.value.api = uploadFormCtrl.page.api;
|
|
196
|
+
}
|
|
197
|
+
if (!uploadUrl.value.api) {
|
|
198
|
+
uploadUrl.value.api = uploadFormCtrl.page.api;
|
|
199
|
+
}
|
|
200
|
+
if (!downloadUrl.value.api) {
|
|
201
|
+
downloadUrl.value.api = uploadFormCtrl.page.api;
|
|
202
|
+
}
|
|
203
|
+
if (!deleteUrl.value.api) {
|
|
204
|
+
deleteUrl.value.api = uploadFormCtrl.page.api;
|
|
205
|
+
}
|
|
206
|
+
loadFileList();
|
|
207
|
+
|
|
208
|
+
open.value = uploadFormCtrl.visible.value;
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
const dialogClose = () => {
|
|
212
|
+
uploadFormCtrl.visible.value = false;
|
|
213
|
+
};
|
|
214
|
+
</script>
|
|
215
|
+
<template>
|
|
216
|
+
<Modal title="模板文件管理" v-model:open="open"
|
|
217
|
+
:wrapClassName="'modal mx-auto ' + ($attrs.width ? 'w-[' + $attrs.width + ']' : 'w-[500px]')"
|
|
218
|
+
@close="dialogClose">
|
|
219
|
+
<UploadList v-model:file-list="fileList" :parent-path="props.templateType" :upload-url="uploadUrl"
|
|
220
|
+
:download-url="downloadUrl" :delete-url="deleteUrl" :max-count="maxCount" :file-ext="fileExt"
|
|
221
|
+
:show-delete="showDelete" />
|
|
222
|
+
<template #footer>
|
|
223
|
+
<Space>
|
|
224
|
+
<Button @click="dialogClose">关闭</Button>
|
|
225
|
+
<Button @click="dialogSave" type="primary" :loading="uploadFormCtrl.isFormSaving.value"
|
|
226
|
+
:disabled="fileList.length === 0">
|
|
227
|
+
上传保存
|
|
228
|
+
</Button>
|
|
229
|
+
</Space>
|
|
230
|
+
</template>
|
|
231
|
+
</Modal>
|
|
232
|
+
</template>
|
|
233
|
+
<style>
|
|
234
|
+
.modal {
|
|
235
|
+
.ant-modal-content {
|
|
236
|
+
padding: 16px;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.full-modal {
|
|
241
|
+
.ant-modal {
|
|
242
|
+
width: 100% !important;
|
|
243
|
+
max-width: 100%;
|
|
244
|
+
top: 0;
|
|
245
|
+
padding-bottom: 0;
|
|
246
|
+
margin: 0;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.ant-modal-content {
|
|
250
|
+
display: flex;
|
|
251
|
+
flex-direction: column;
|
|
252
|
+
height: calc(100vh);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.ant-modal-body {
|
|
256
|
+
flex: 1;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
</style>
|
|
@@ -136,24 +136,16 @@ const dialogClose = () => {
|
|
|
136
136
|
};
|
|
137
137
|
</script>
|
|
138
138
|
<template>
|
|
139
|
-
<Modal
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
:
|
|
143
|
-
|
|
144
|
-
>
|
|
145
|
-
<UploadList
|
|
146
|
-
v-model:file-list="fileList"
|
|
147
|
-
:upload-url="uploadUrl!"
|
|
148
|
-
:download-url="downloadUrl"
|
|
149
|
-
:max-count="maxCount"
|
|
150
|
-
:file-ext="fileExt"
|
|
151
|
-
/>
|
|
139
|
+
<Modal title="文件上传" v-model:open="open"
|
|
140
|
+
:wrapClassName="'modal mx-auto ' + ($attrs.width ? 'w-[' + $attrs.width + ']' : 'w-[500px]')"
|
|
141
|
+
@close="dialogClose">
|
|
142
|
+
<UploadList v-model:file-list="fileList" :upload-url="uploadUrl!" :download-url="downloadUrl"
|
|
143
|
+
:max-count="maxCount" :file-ext="fileExt" />
|
|
152
144
|
<template #footer>
|
|
153
145
|
<Space>
|
|
154
146
|
<Button @click="dialogClose">取消</Button>
|
|
155
147
|
<Button @click="dialogSave" type="primary" :loading="uploadFormCtrl.isFormSaving.value">
|
|
156
|
-
|
|
148
|
+
上传保存
|
|
157
149
|
</Button>
|
|
158
150
|
</Space>
|
|
159
151
|
</template>
|
|
@@ -3,9 +3,8 @@ import { onMounted, ref, useAttrs, watch } from 'vue';
|
|
|
3
3
|
import { Tree } from 'ant-design-vue';
|
|
4
4
|
import { ToolIcon } from '../../common';
|
|
5
5
|
import { TreeControl, TreeNode, queryTree } from '@/index';
|
|
6
|
-
import { TreeDataNode } from 'ant-design-vue/es/vc-tree-select/interface';
|
|
7
|
-
import { EventDataNode } from 'ant-design-vue/es/vc-tree/interface';
|
|
8
6
|
import { fieldMapping } from '@skyfox2000/fapi';
|
|
7
|
+
import { findNodeByKey, TreeNodeInfo } from '@/utils/tree';
|
|
9
8
|
|
|
10
9
|
/**
|
|
11
10
|
* 组件属性定义
|
|
@@ -31,6 +30,7 @@ const attrs = useAttrs();
|
|
|
31
30
|
const treeCtrl = props.treeCtrl;
|
|
32
31
|
// 树节点数据
|
|
33
32
|
const treeData = ref<TreeNode[]>([]);
|
|
33
|
+
// const treeRef = ref();
|
|
34
34
|
|
|
35
35
|
// 监听树数据变化
|
|
36
36
|
watch(
|
|
@@ -38,7 +38,26 @@ watch(
|
|
|
38
38
|
(newVal) => {
|
|
39
39
|
if (newVal) {
|
|
40
40
|
treeData.value = treeCtrl.fieldMap ? fieldMapping(treeCtrl.fieldMap, newVal) : newVal;
|
|
41
|
-
|
|
41
|
+
// 获取当前选中的节点
|
|
42
|
+
const selectedNode = findNodeByKey(treeData.value, selectedKeys.value[0]);
|
|
43
|
+
|
|
44
|
+
if (selectedNode) {
|
|
45
|
+
// 构建info对象
|
|
46
|
+
const info: TreeNodeInfo = {
|
|
47
|
+
event: 'select',
|
|
48
|
+
node: {
|
|
49
|
+
// 根据你的TreeNode结构构建node对象
|
|
50
|
+
key: selectedNode.key,
|
|
51
|
+
title: selectedNode.title,
|
|
52
|
+
checked: true,
|
|
53
|
+
dataRef: selectedNode
|
|
54
|
+
},
|
|
55
|
+
selected: true,
|
|
56
|
+
selectedNodes: [selectedNode]
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
handleSelect(selectedKeys.value, info);
|
|
60
|
+
}
|
|
42
61
|
}
|
|
43
62
|
},
|
|
44
63
|
);
|
|
@@ -47,12 +66,7 @@ const emit = defineEmits<{
|
|
|
47
66
|
(
|
|
48
67
|
e: 'select',
|
|
49
68
|
selectedKeys: (string | number)[],
|
|
50
|
-
info?:
|
|
51
|
-
event: 'select';
|
|
52
|
-
node: EventDataNode;
|
|
53
|
-
selected: boolean;
|
|
54
|
-
selectedNodes: TreeDataNode[];
|
|
55
|
-
},
|
|
69
|
+
info?: TreeNodeInfo,
|
|
56
70
|
): void;
|
|
57
71
|
(e: 'update:selectedKeys', value: (string | number)[]): void;
|
|
58
72
|
}>();
|
|
@@ -81,12 +95,7 @@ watch(
|
|
|
81
95
|
// 值变化事件
|
|
82
96
|
const handleSelect = (
|
|
83
97
|
keys: (string | number)[],
|
|
84
|
-
info?:
|
|
85
|
-
event: 'select';
|
|
86
|
-
node: EventDataNode;
|
|
87
|
-
selected: boolean;
|
|
88
|
-
selectedNodes: TreeDataNode[];
|
|
89
|
-
},
|
|
98
|
+
info?: TreeNodeInfo,
|
|
90
99
|
) => {
|
|
91
100
|
if (keys.length === 0) {
|
|
92
101
|
keys.push(...selectedKeys.value);
|
|
@@ -118,16 +127,8 @@ onMounted(() => {
|
|
|
118
127
|
</script>
|
|
119
128
|
|
|
120
129
|
<template>
|
|
121
|
-
<Tree
|
|
122
|
-
|
|
123
|
-
:show-line="true"
|
|
124
|
-
:tree-data="treeData"
|
|
125
|
-
v-model:expanded-keys="expandedKeys"
|
|
126
|
-
:selected-keys="selectedKeys"
|
|
127
|
-
:loading="treeCtrl.isTreeLoading"
|
|
128
|
-
@select="handleSelect"
|
|
129
|
-
v-bind="attrs"
|
|
130
|
-
>
|
|
130
|
+
<Tree class="w-full" :show-line="true" :tree-data="treeData" v-model:expanded-keys="expandedKeys"
|
|
131
|
+
:selected-keys="selectedKeys" :loading="treeCtrl.isTreeLoading" @select="handleSelect" v-bind="attrs">
|
|
131
132
|
<template #title="{ key, label, icon }">
|
|
132
133
|
<slot name="title" :label="label" :key="key" :icon="icon">
|
|
133
134
|
<div class="flex items-center gap-1">
|
|
@@ -6,7 +6,7 @@ import type { UploadProps } from 'ant-design-vue';
|
|
|
6
6
|
import { Upload, Progress, Tag, Popconfirm } from 'ant-design-vue';
|
|
7
7
|
import { UploadFile, UploadStatus, donwloadFromMinio, path, Switch, previewFromMinio } from '@/index';
|
|
8
8
|
import { useInputFactory } from '@/utils/form-validate';
|
|
9
|
-
import { IUrlInfo } from '@skyfox2000/fapi';
|
|
9
|
+
import { ApiResponse, httpPost, IUrlInfo, ResStatus } from '@skyfox2000/fapi';
|
|
10
10
|
|
|
11
11
|
export interface UploadListProps {
|
|
12
12
|
/**
|
|
@@ -26,6 +26,10 @@ export interface UploadListProps {
|
|
|
26
26
|
* 预览Url
|
|
27
27
|
*/
|
|
28
28
|
previewUrl?: IUrlInfo;
|
|
29
|
+
/**
|
|
30
|
+
* 删除Url
|
|
31
|
+
*/
|
|
32
|
+
deleteUrl?: IUrlInfo;
|
|
29
33
|
/**
|
|
30
34
|
* 文件列表
|
|
31
35
|
*/
|
|
@@ -70,6 +74,10 @@ export interface UploadListProps {
|
|
|
70
74
|
* 是否显示上线或下线
|
|
71
75
|
*/
|
|
72
76
|
showOnlineSwitch?: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* 是否显示删除
|
|
79
|
+
*/
|
|
80
|
+
showDelete?: boolean;
|
|
73
81
|
}
|
|
74
82
|
|
|
75
83
|
const props = withDefaults(defineProps<UploadListProps>(), {
|
|
@@ -83,6 +91,7 @@ const props = withDefaults(defineProps<UploadListProps>(), {
|
|
|
83
91
|
fileExtTip: true,
|
|
84
92
|
showActionText: true,
|
|
85
93
|
showOnlineSwitch: false,
|
|
94
|
+
showDelete: true,
|
|
86
95
|
});
|
|
87
96
|
|
|
88
97
|
const inputFactory = useInputFactory();
|
|
@@ -210,7 +219,21 @@ const previewFile = (index: number) => {
|
|
|
210
219
|
};
|
|
211
220
|
|
|
212
221
|
const removeFile = (index: number) => {
|
|
213
|
-
fileList.value
|
|
222
|
+
const file = fileList.value[index];
|
|
223
|
+
if (props.deleteUrl) {
|
|
224
|
+
httpPost<ApiResponse>(props.deleteUrl, {
|
|
225
|
+
Query: {
|
|
226
|
+
FileKey: file.minioFile!.Key,
|
|
227
|
+
},
|
|
228
|
+
}).then((res) => {
|
|
229
|
+
if (res && res.status === ResStatus.SUCCESS) {
|
|
230
|
+
message.success('删除文件成功!');
|
|
231
|
+
fileList.value.splice(index, 1);
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
} else {
|
|
235
|
+
fileList.value.splice(index, 1);
|
|
236
|
+
}
|
|
214
237
|
confirmOpen.value = false;
|
|
215
238
|
};
|
|
216
239
|
|
|
@@ -270,11 +293,8 @@ const getStatus = (status?: UploadStatus) => {
|
|
|
270
293
|
<div class="w-full border border-solid border-gray-100 mt-1 rounded-md py-5" :class="[errInfo?.errClass]">
|
|
271
294
|
<div class="flex items-center justify-between w-full">
|
|
272
295
|
<div class="w-35 mx-3">
|
|
273
|
-
<Upload
|
|
274
|
-
|
|
275
|
-
v-bind="uploadProps"
|
|
276
|
-
v-auth="{ role: ['Super', 'Admin'], permit: ':uploadlist:upload' }"
|
|
277
|
-
>
|
|
296
|
+
<Upload ref="fileUploader" v-bind="uploadProps"
|
|
297
|
+
v-auth="{ role: ['Super', 'Admin'], permit: ':uploadlist:upload' }">
|
|
278
298
|
<Button :class="[errInfo?.errClass + '-text']">选择文件</Button>
|
|
279
299
|
</Upload>
|
|
280
300
|
</div>
|
|
@@ -288,99 +308,57 @@ const getStatus = (status?: UploadStatus) => {
|
|
|
288
308
|
<div v-for="(file, index) in fileList" :key="index" class="mb-2 pb-1">
|
|
289
309
|
<div class="flex items-center justify-between">
|
|
290
310
|
<div class="flex items-center">
|
|
291
|
-
<span
|
|
292
|
-
class="
|
|
293
|
-
|
|
294
|
-
>{{ file.fileName ?? file.name }}</span
|
|
295
|
-
>
|
|
311
|
+
<span class="text-gray-700 mr-2"
|
|
312
|
+
:class="[file.status == UploadStatus.Offline ? 'line-through' : '']">{{ file.fileName ?? file.name
|
|
313
|
+
}}</span>
|
|
296
314
|
<span>
|
|
297
315
|
<Tag :color="getStatusColor(file.status)">{{ getStatus(file.status) }}</Tag>
|
|
298
316
|
</span>
|
|
299
317
|
</div>
|
|
300
318
|
<div class="flex items-center">
|
|
301
|
-
<div
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
showOnlineSwitch && (file.status == UploadStatus.Online || file.status == UploadStatus.Offline)
|
|
305
|
-
"
|
|
306
|
-
>
|
|
319
|
+
<div class="mr-2" v-if="
|
|
320
|
+
showOnlineSwitch && (file.status == UploadStatus.Online || file.status == UploadStatus.Offline)
|
|
321
|
+
">
|
|
307
322
|
<Tooltip title="上线或下线">
|
|
308
|
-
<Switch
|
|
309
|
-
|
|
310
|
-
:
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
]"
|
|
314
|
-
@change="onlineOrOffline(file)"
|
|
315
|
-
v-auth="{ role: ['Super', 'Admin'], permit: ':uploadlist:online' }"
|
|
316
|
-
/>
|
|
323
|
+
<Switch v-model:checked="file.status" :data="[
|
|
324
|
+
{ label: '上线', value: UploadStatus.Online },
|
|
325
|
+
{ label: '下线', value: UploadStatus.Offline },
|
|
326
|
+
]" @change="onlineOrOffline(file)"
|
|
327
|
+
v-auth="{ role: ['Super', 'Admin'], permit: ':uploadlist:online' }" />
|
|
317
328
|
</Tooltip>
|
|
318
329
|
</div>
|
|
319
|
-
<div
|
|
320
|
-
|
|
321
|
-
v-if="downloadUrl && (file.status == UploadStatus.Online || file.status == UploadStatus.Offline)"
|
|
322
|
-
>
|
|
330
|
+
<div class="flex items-center text-blue-500 hover:text-blue-700 mr-1 cursor-pointer"
|
|
331
|
+
v-if="downloadUrl && (file.status == UploadStatus.Online || file.status == UploadStatus.Offline)">
|
|
323
332
|
<Tooltip title="下载">
|
|
324
|
-
<ToolIcon
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
@click="downloadFile(index)"
|
|
329
|
-
/>
|
|
330
|
-
<span
|
|
331
|
-
v-if="showActionText"
|
|
332
|
-
class="mr-2 text-sm text-nowrap"
|
|
333
|
+
<ToolIcon icon="icon-download"
|
|
334
|
+
v-auth="{ role: ['Super', 'Admin'], permit: ':uploadlist:download' }" clickable
|
|
335
|
+
@click="downloadFile(index)" />
|
|
336
|
+
<span v-if="showActionText" class="mr-2 text-sm text-nowrap"
|
|
333
337
|
v-auth="{ role: ['Super', 'Admin'], permit: ':uploadlist:download' }"
|
|
334
|
-
@click="downloadFile(index)"
|
|
335
|
-
>下载</span
|
|
336
|
-
>
|
|
338
|
+
@click="downloadFile(index)">下载</span>
|
|
337
339
|
</Tooltip>
|
|
338
340
|
</div>
|
|
339
|
-
<div
|
|
340
|
-
|
|
341
|
-
v-if="previewUrl && (file.status == UploadStatus.Online || file.status == UploadStatus.Offline)"
|
|
342
|
-
>
|
|
341
|
+
<div class="flex items-center text-blue-500 hover:text-blue-700 mr-1 cursor-pointer"
|
|
342
|
+
v-if="previewUrl && (file.status == UploadStatus.Online || file.status == UploadStatus.Offline)">
|
|
343
343
|
<Tooltip title="预览">
|
|
344
|
-
<ToolIcon
|
|
345
|
-
|
|
344
|
+
<ToolIcon icon="icon-eye" v-auth="{ role: ['Super', 'Admin'], permit: ':uploadlist:preview' }"
|
|
345
|
+
clickable @click="previewFile(index)" />
|
|
346
|
+
<span v-if="showActionText" class="mr-2 text-sm text-nowrap"
|
|
346
347
|
v-auth="{ role: ['Super', 'Admin'], permit: ':uploadlist:preview' }"
|
|
347
|
-
|
|
348
|
-
@click="previewFile(index)"
|
|
349
|
-
/>
|
|
350
|
-
<span
|
|
351
|
-
v-if="showActionText"
|
|
352
|
-
class="mr-2 text-sm text-nowrap"
|
|
353
|
-
v-auth="{ role: ['Super', 'Admin'], permit: ':uploadlist:preview' }"
|
|
354
|
-
@click="previewFile(index)"
|
|
355
|
-
>预览</span
|
|
356
|
-
>
|
|
348
|
+
@click="previewFile(index)">预览</span>
|
|
357
349
|
</Tooltip>
|
|
358
350
|
</div>
|
|
359
|
-
<div
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
title="确定删除该文件吗?"
|
|
365
|
-
:okButtonProps="{ size: 'small' }"
|
|
366
|
-
:cancelButtonProps="{ size: 'small' }"
|
|
367
|
-
@confirm="removeFile(index)"
|
|
368
|
-
@cancel="confirmOpen = false"
|
|
369
|
-
>
|
|
351
|
+
<div v-if="showDelete !== false"
|
|
352
|
+
class="flex items-center text-red-500 hover:text-red-700 cursor-pointer">
|
|
353
|
+
<Popconfirm v-model:open="confirmOpen" cancelText="否" okText="是" title="确定删除该文件吗?"
|
|
354
|
+
:okButtonProps="{ size: 'small' }" :cancelButtonProps="{ size: 'small' }"
|
|
355
|
+
@confirm="removeFile(index)" @cancel="confirmOpen = false">
|
|
370
356
|
<Tooltip title="删除">
|
|
371
357
|
<div @click="confirmDelFile(index, file.status)">
|
|
372
|
-
<ToolIcon
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
v-auth="{ role: ['Super', 'Admin'], permit: ':uploadlist:delete' }"
|
|
376
|
-
clickable
|
|
377
|
-
/>
|
|
378
|
-
<span
|
|
379
|
-
v-if="showActionText"
|
|
380
|
-
class="text-sm text-nowrap"
|
|
381
|
-
v-auth="{ role: ['Super', 'Admin'], permit: ':uploadlist:delete' }"
|
|
382
|
-
>删除</span
|
|
383
|
-
>
|
|
358
|
+
<ToolIcon icon="icon-new" :angle="45"
|
|
359
|
+
v-auth="{ role: ['Super', 'Admin'], permit: ':uploadlist:delete' }" clickable />
|
|
360
|
+
<span v-if="showActionText" class="text-sm text-nowrap"
|
|
361
|
+
v-auth="{ role: ['Super', 'Admin'], permit: ':uploadlist:delete' }">删除</span>
|
|
384
362
|
</div>
|
|
385
363
|
</Tooltip>
|
|
386
364
|
</Popconfirm>
|
|
@@ -389,13 +367,11 @@ const getStatus = (status?: UploadStatus) => {
|
|
|
389
367
|
</div>
|
|
390
368
|
|
|
391
369
|
<!-- 上传进度条 -->
|
|
392
|
-
<div
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
"
|
|
398
|
-
>
|
|
370
|
+
<div v-if="
|
|
371
|
+
file.status !== UploadStatus.Online &&
|
|
372
|
+
file.status !== UploadStatus.Offline &&
|
|
373
|
+
file.status !== UploadStatus.Success
|
|
374
|
+
">
|
|
399
375
|
<Progress :percent="file.percent" :stroke-width="2" :show-info="false" style="height: 2px"></Progress>
|
|
400
376
|
</div>
|
|
401
377
|
</div>
|
|
@@ -408,6 +384,7 @@ const getStatus = (status?: UploadStatus) => {
|
|
|
408
384
|
border-color: #ff4d4f80;
|
|
409
385
|
box-shadow: 0 0 3px 0 #ff4d4f;
|
|
410
386
|
}
|
|
387
|
+
|
|
411
388
|
.error-text {
|
|
412
389
|
color: #ff4d4f !important;
|
|
413
390
|
}
|
package/src/utils/tree.ts
CHANGED
|
@@ -2,7 +2,15 @@ import { TreeControl, TreeNode } from '@/typings/tree.d';
|
|
|
2
2
|
import { ApiResponse, ReqParams, ResStatus } from '@skyfox2000/fapi';
|
|
3
3
|
import { doQuery } from './data';
|
|
4
4
|
import { combineParams } from '@skyfox2000/microbase';
|
|
5
|
+
import { TreeDataNode } from 'ant-design-vue/es/vc-tree-select/interface';
|
|
6
|
+
import { EventDataNode } from 'ant-design-vue/es/vc-tree/interface';
|
|
5
7
|
|
|
8
|
+
export interface TreeNodeInfo {
|
|
9
|
+
event: 'select';
|
|
10
|
+
node: EventDataNode;
|
|
11
|
+
selected: boolean;
|
|
12
|
+
selectedNodes: TreeDataNode[];
|
|
13
|
+
}
|
|
6
14
|
/**
|
|
7
15
|
* 树加载数据
|
|
8
16
|
* @param treeCtrl 树控制对象
|
|
@@ -28,3 +36,15 @@ export const queryTree = (treeCtrl: TreeControl, params?: ReqParams): Promise<Tr
|
|
|
28
36
|
return [];
|
|
29
37
|
});
|
|
30
38
|
};
|
|
39
|
+
|
|
40
|
+
// 辅助函数:根据key查找节点
|
|
41
|
+
export const findNodeByKey = (nodes: TreeNode[], key: string | number): TreeNode | null => {
|
|
42
|
+
for (const node of nodes) {
|
|
43
|
+
if (node.key === key) return node;
|
|
44
|
+
if (node.children) {
|
|
45
|
+
const found = findNodeByKey(node.children, key);
|
|
46
|
+
if (found) return found;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
};
|