@skyfox2000/webui 1.2.7 → 1.2.9
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/assets/modules/_plugin-vue_export-helper-CHgC5LLL.js +9 -0
- package/lib/assets/modules/file-upload-BYIvmkmy.js +204 -0
- package/lib/assets/modules/form-excel-BUX2QW11.js +235 -0
- package/lib/assets/modules/{index-BZvwPKou.js → index-DcRkhkn2.js} +1 -1
- package/lib/assets/modules/index-jl5Ie2tZ.js +112 -0
- package/lib/assets/modules/{menuTabs-izaFx0kk.js → menuTabs-DY1XBq16.js} +134 -139
- package/lib/assets/modules/{toolIcon-66dclHit.js → toolIcon-BDIOj_3l.js} +1 -1
- package/lib/assets/modules/uploadList-BPG5whz7.js +369 -0
- package/lib/assets/modules/{index-CKMDsqve.js → uploadList-D2Ux4h1N.js} +553 -513
- package/lib/components/common/index.d.ts +2 -0
- package/lib/components/common/loading/index.vue.d.ts +3 -0
- package/lib/components/content/dialog/index.vue.d.ts +1 -0
- package/lib/components/content/drawer/index.vue.d.ts +2 -0
- package/lib/components/content/form/formItem.vue.d.ts +2 -0
- package/lib/components/form/autoComplete/index.vue.d.ts +41 -4
- package/lib/components/form/cascader/index.vue.d.ts +13 -2
- package/lib/components/form/checkbox/index.vue.d.ts +13 -2
- package/lib/components/form/index.d.ts +1 -0
- package/lib/components/form/propEditor/index.vue.d.ts +10 -1
- package/lib/components/form/radio/index.vue.d.ts +13 -2
- package/lib/components/form/select/index.vue.d.ts +13 -2
- package/lib/components/form/upload/uploadList.vue.d.ts +14 -3
- package/lib/components/index.d.ts +2 -2
- package/lib/es/AceEditor/index.js +3 -3
- package/lib/es/BasicLayout/index.js +15 -15
- package/lib/es/Error403/index.js +1 -1
- package/lib/es/Error404/index.js +1 -1
- package/lib/es/ExcelForm/index.js +4 -4
- package/lib/es/UploadForm/index.js +60 -87
- package/lib/index.d.ts +3 -3
- package/lib/typings/form.d.ts +18 -2
- package/lib/typings/option.d.ts +16 -2
- package/lib/typings/page.d.ts +1 -0
- package/lib/utils/file-upload.d.ts +4 -2
- package/lib/utils/form-excel.d.ts +3 -0
- package/lib/utils/main-openapis.d.ts +0 -1
- package/lib/webui.css +1 -1
- package/lib/webui.es.js +1088 -1064
- package/package.json +1 -1
- package/scripts/userInput.py +2 -0
- package/src/components/common/index.ts +3 -0
- package/src/components/common/loading/index.vue +11 -0
- package/src/components/content/dialog/index.vue +15 -8
- package/src/components/content/dialog/uploadForm.vue +38 -79
- package/src/components/content/drawer/index.vue +26 -6
- package/src/components/content/form/formItem.vue +21 -5
- package/src/components/content/form/index.vue +4 -2
- package/src/components/content/search/searchItem.vue +1 -1
- package/src/components/form/autoComplete/index.vue +12 -4
- package/src/components/form/cascader/index.vue +1 -1
- package/src/components/form/datePicker/index.vue +1 -1
- package/src/components/form/index.ts +1 -0
- package/src/components/form/input/inputIcon.vue +1 -1
- package/src/components/form/propEditor/index.vue +113 -33
- package/src/components/form/select/index.vue +27 -17
- package/src/components/form/upload/uploadList.vue +174 -34
- package/src/components/index.ts +13 -1
- package/src/directives/permission.ts +11 -11
- package/src/index.ts +4 -10
- package/src/stores/appInfo.ts +6 -6
- package/src/typings/form.d.ts +18 -2
- package/src/typings/option.d.ts +16 -2
- package/src/typings/page.d.ts +1 -0
- package/src/utils/download.ts +1 -1
- package/src/utils/eventbus.ts +1 -1
- package/src/utils/file-upload.ts +75 -14
- package/src/utils/form-excel.ts +50 -1
- package/src/utils/form-validate.ts +18 -1
- package/src/utils/form.ts +3 -1
- package/src/utils/options.ts +38 -12
- package/lib/assets/modules/file-upload-D4bA7go8.js +0 -179
- package/lib/assets/modules/form-excel-DL2_SNiS.js +0 -211
- package/lib/assets/modules/index-D16E7UbH.js +0 -111
- package/lib/assets/modules/uploadList-Dw6eRrJT.js +0 -210
|
@@ -2,44 +2,108 @@
|
|
|
2
2
|
import { ref, watch } from 'vue';
|
|
3
3
|
import { Input, Button } from 'ant-design-vue';
|
|
4
4
|
|
|
5
|
-
interface
|
|
5
|
+
export interface PropConfigItem {
|
|
6
6
|
id: number;
|
|
7
|
+
/**
|
|
8
|
+
* 文本
|
|
9
|
+
*/
|
|
10
|
+
text?: string;
|
|
11
|
+
/**
|
|
12
|
+
* 配置名
|
|
13
|
+
*/
|
|
7
14
|
field: string;
|
|
15
|
+
/**
|
|
16
|
+
* 配置值
|
|
17
|
+
*/
|
|
8
18
|
value: string;
|
|
9
19
|
}
|
|
10
20
|
|
|
11
21
|
const props = defineProps<{
|
|
22
|
+
/**
|
|
23
|
+
* 配置值
|
|
24
|
+
*/
|
|
12
25
|
value: Record<string, string>;
|
|
26
|
+
/**
|
|
27
|
+
* 配置项
|
|
28
|
+
*/
|
|
29
|
+
selectList?: PropConfigItem[];
|
|
30
|
+
/**
|
|
31
|
+
* 配置名宽度%
|
|
32
|
+
*/
|
|
33
|
+
fieldWidth?: number;
|
|
34
|
+
/**
|
|
35
|
+
* 标签占位符
|
|
36
|
+
*/
|
|
13
37
|
labelHolder?: string;
|
|
14
|
-
|
|
38
|
+
/**
|
|
39
|
+
* 值占位符
|
|
40
|
+
*/
|
|
41
|
+
valueHolder?: string;
|
|
42
|
+
/**
|
|
43
|
+
* 新增配置行
|
|
44
|
+
*/
|
|
45
|
+
addMore?: boolean;
|
|
15
46
|
}>();
|
|
16
47
|
const emit = defineEmits(['update:value']);
|
|
17
48
|
|
|
18
|
-
|
|
19
|
-
|
|
49
|
+
// 当有selectList时,不允许添加更多配置项
|
|
50
|
+
const addMore = ref(props.selectList && props.selectList.length > 0 ? false : (props.addMore ?? true));
|
|
20
51
|
|
|
52
|
+
const configList = ref<PropConfigItem[]>([]);
|
|
53
|
+
// 标记是否为内部输入变更
|
|
54
|
+
// 避免重复触发 update:value
|
|
55
|
+
let isInnerChange = false;
|
|
56
|
+
|
|
57
|
+
// 初始化配置列表
|
|
58
|
+
// 1、有selectList时,只能使用selectList里面的内容,且不允许重复
|
|
59
|
+
// 2、没有selectList时,可动态添加新的项目
|
|
60
|
+
// 3、value有值时,能够正常显示值,value的key对应field
|
|
21
61
|
const initConfigList = () => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
62
|
+
if (props.selectList && props.selectList.length > 0) {
|
|
63
|
+
// 有selectList的情况:使用selectList作为基础,合并value值
|
|
64
|
+
configList.value = props.selectList.map((item) => ({
|
|
65
|
+
id: Date.now() + Math.random(),
|
|
66
|
+
text: item.text,
|
|
67
|
+
field: item.field,
|
|
68
|
+
// 如果value中有对应的field,使用value中的值,否则使用selectList中的默认值
|
|
69
|
+
value: props.value[item.field] ?? item.value,
|
|
70
|
+
}));
|
|
71
|
+
} else {
|
|
72
|
+
// 没有selectList的情况:基于value动态生成配置项
|
|
73
|
+
if (props.value && Object.keys(props.value).length > 0) {
|
|
74
|
+
configList.value = Object.entries(props.value).map(([field, value]) => ({
|
|
75
|
+
id: Date.now() + Math.random(),
|
|
76
|
+
field,
|
|
77
|
+
value,
|
|
78
|
+
}));
|
|
79
|
+
} else {
|
|
80
|
+
// value为空时,初始化为空数组
|
|
81
|
+
configList.value = [];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
27
84
|
};
|
|
28
85
|
|
|
29
|
-
watch(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
86
|
+
watch(
|
|
87
|
+
() => props.value,
|
|
88
|
+
() => {
|
|
89
|
+
if (!isInnerChange) {
|
|
90
|
+
initConfigList();
|
|
91
|
+
}
|
|
92
|
+
isInnerChange = false;
|
|
93
|
+
},
|
|
94
|
+
{ immediate: true },
|
|
95
|
+
);
|
|
35
96
|
|
|
36
97
|
const updateConfig = () => {
|
|
37
|
-
const newConfig = configList.value.reduce(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
98
|
+
const newConfig = configList.value.reduce(
|
|
99
|
+
(acc: Record<string, string>, item: PropConfigItem) => {
|
|
100
|
+
if (item.field) {
|
|
101
|
+
acc[item.field] = item.value;
|
|
102
|
+
}
|
|
103
|
+
return acc;
|
|
104
|
+
},
|
|
105
|
+
{} as Record<string, string>,
|
|
106
|
+
);
|
|
43
107
|
isInnerChange = true;
|
|
44
108
|
emit('update:value', newConfig);
|
|
45
109
|
};
|
|
@@ -60,22 +124,38 @@ const handleInputChange = () => {
|
|
|
60
124
|
<template>
|
|
61
125
|
<div class="flex flex-col gap-2">
|
|
62
126
|
<div v-for="item in configList" :key="item.id" class="flex items-center gap-2">
|
|
63
|
-
<div class="w-[33%]">
|
|
64
|
-
<Input
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
127
|
+
<div :class="[fieldWidth ? `w-[${fieldWidth}%]` : 'w-[33%]']">
|
|
128
|
+
<Input
|
|
129
|
+
v-if="!selectList || selectList.length === 0"
|
|
130
|
+
v-model:value="item.field"
|
|
131
|
+
:title="item.text || item.field"
|
|
132
|
+
class="w-full"
|
|
133
|
+
:placeholder="item.text || labelHolder || '配置名'"
|
|
134
|
+
@input="handleInputChange"
|
|
135
|
+
:disabled="!addMore"
|
|
136
|
+
/>
|
|
137
|
+
<div v-else>
|
|
138
|
+
<Input v-model:value="item.text" :title="item.text" :disabled="true" class="w-[100%]" />
|
|
139
|
+
<Input type="hidden" v-model:value="item.field" />
|
|
140
|
+
</div>
|
|
69
141
|
</div>
|
|
70
|
-
<div class="w-[
|
|
71
|
-
|
|
72
|
-
|
|
142
|
+
<div class="w-[3%]">=</div>
|
|
143
|
+
<div :class="[fieldWidth ? `w-[${97 - fieldWidth}%]` : 'w-[64%]']">
|
|
144
|
+
<Input
|
|
145
|
+
v-model:value="item.value"
|
|
146
|
+
:placeholder="valueHolder || '请输入' + item.text || '请输入配置值'"
|
|
147
|
+
@input="handleInputChange"
|
|
148
|
+
:title="item.value"
|
|
149
|
+
/>
|
|
73
150
|
</div>
|
|
74
151
|
</div>
|
|
75
|
-
<Button
|
|
152
|
+
<Button
|
|
153
|
+
v-if="addMore"
|
|
154
|
+
@click="addNewLine"
|
|
76
155
|
class="mt-1 w-[80px] !text-[12px] text-[#666] bg-[#e6f7ff] border-[#b3e0ff] hover:bg-[#b3e0ff] hover:border-[#8abeff]"
|
|
77
|
-
size="small"
|
|
156
|
+
size="small"
|
|
157
|
+
>
|
|
78
158
|
新增配置行
|
|
79
159
|
</Button>
|
|
80
160
|
</div>
|
|
81
|
-
</template>
|
|
161
|
+
</template>
|
|
@@ -73,28 +73,45 @@ const url = ref<IUrlInfo>({
|
|
|
73
73
|
const inputFactory = useInputFactory();
|
|
74
74
|
const { editorCtrl, errInfo, labelText } = inputFactory;
|
|
75
75
|
|
|
76
|
+
const defaultVal = ref(props.value);
|
|
77
|
+
const placeholder = ref(attrs.placeholder);
|
|
78
|
+
|
|
76
79
|
/// 避免类型错误
|
|
77
80
|
const innerValue = ref<string | number | string[] | number[] | undefined>(undefined);
|
|
78
81
|
const emit = defineEmits(['change', 'update:value', 'update:labels']);
|
|
79
82
|
inputFactory.inputEmit = emit;
|
|
83
|
+
|
|
80
84
|
/**
|
|
81
85
|
* 实际的选择项
|
|
82
86
|
*/
|
|
83
87
|
const selectOptions = ref<OptionItemProps[]>([]);
|
|
88
|
+
const onChanged = (value: any) => {
|
|
89
|
+
const selectedOptions = onOptionChanged(props, value as SelectValue, selectOptions, inputFactory);
|
|
90
|
+
const labels: string[] = getSelectedLabels(selectedOptions);
|
|
91
|
+
|
|
92
|
+
innerValue.value = value;
|
|
93
|
+
emit('update:labels', labels);
|
|
94
|
+
emit('change', value);
|
|
95
|
+
|
|
96
|
+
if (errInfo?.value.errClass && editorCtrl) {
|
|
97
|
+
/// 重新开始验证
|
|
98
|
+
formValidate(editorCtrl);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
84
102
|
watch(
|
|
85
103
|
() => selectOptions.value,
|
|
86
104
|
(newVal) => {
|
|
87
|
-
if (newVal.length
|
|
105
|
+
if (newVal.length && isEmpty(props.value) && props.selectFirst) {
|
|
88
106
|
innerValue.value = newVal[0].value;
|
|
89
|
-
onChanged(newVal[0].value);
|
|
90
107
|
} else {
|
|
91
108
|
// 当选项加载完成后,设置实际的 value 值
|
|
92
109
|
innerValue.value = props.value === null ? undefined : props.value;
|
|
93
110
|
}
|
|
111
|
+
if (newVal.length > 0 && innerValue.value) onChanged(innerValue.value);
|
|
94
112
|
},
|
|
95
113
|
);
|
|
96
114
|
|
|
97
|
-
const placeholder = ref(attrs.placeholder);
|
|
98
115
|
watch(
|
|
99
116
|
() => url.value.loading,
|
|
100
117
|
(newVal) => {
|
|
@@ -104,7 +121,6 @@ watch(
|
|
|
104
121
|
{ immediate: true },
|
|
105
122
|
);
|
|
106
123
|
|
|
107
|
-
const defaultVal = ref(props.value);
|
|
108
124
|
watch(
|
|
109
125
|
() => innerValue.value,
|
|
110
126
|
(newVal) => {
|
|
@@ -118,19 +134,13 @@ watch(
|
|
|
118
134
|
},
|
|
119
135
|
);
|
|
120
136
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (errInfo?.value.errClass && editorCtrl) {
|
|
130
|
-
/// 重新开始验证
|
|
131
|
-
formValidate(editorCtrl);
|
|
132
|
-
}
|
|
133
|
-
};
|
|
137
|
+
watch(
|
|
138
|
+
() => props.params,
|
|
139
|
+
(newVal) => {
|
|
140
|
+
loadOption(true, props, selectOptions, inputFactory, url.value, newVal);
|
|
141
|
+
},
|
|
142
|
+
{ deep: true },
|
|
143
|
+
);
|
|
134
144
|
|
|
135
145
|
onMounted(() => {
|
|
136
146
|
if (url.value && !url.value.fieldMap) {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { Button } from '../../common';
|
|
2
|
+
import { Button, ToolIcon, Tooltip } from '../../common';
|
|
3
3
|
import { computed, ref, watch } from 'vue';
|
|
4
4
|
import message from 'vue-m-message';
|
|
5
5
|
import type { UploadProps } from 'ant-design-vue';
|
|
6
|
-
import { Upload, Progress, Tag } from 'ant-design-vue';
|
|
7
|
-
import { UploadFile, UploadStatus, donwloadFromMinio, path } from '@/index';
|
|
6
|
+
import { Upload, Progress, Tag, Popconfirm } from 'ant-design-vue';
|
|
7
|
+
import { UploadFile, UploadStatus, donwloadFromMinio, path, Switch } from '@/index';
|
|
8
|
+
import { useInputFactory } from '@/utils/form-validate';
|
|
8
9
|
import { IUrlInfo } from '@skyfox2000/fapi';
|
|
9
10
|
|
|
10
11
|
export interface UploadListProps {
|
|
@@ -12,14 +13,19 @@ export interface UploadListProps {
|
|
|
12
13
|
* 是否自动上传
|
|
13
14
|
*/
|
|
14
15
|
autoUpload?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* 上传Url
|
|
18
|
+
*/
|
|
19
|
+
uploadUrl: IUrlInfo;
|
|
20
|
+
/**
|
|
15
21
|
/*
|
|
16
22
|
* 下载Url
|
|
17
23
|
*/
|
|
18
|
-
downloadUrl
|
|
24
|
+
downloadUrl?: IUrlInfo;
|
|
19
25
|
/**
|
|
20
26
|
* 文件列表
|
|
21
27
|
*/
|
|
22
|
-
fileList: UploadFile
|
|
28
|
+
fileList: UploadFile[];
|
|
23
29
|
/**
|
|
24
30
|
* 提示文字
|
|
25
31
|
*/
|
|
@@ -36,10 +42,30 @@ export interface UploadListProps {
|
|
|
36
42
|
* 最大数量
|
|
37
43
|
*/
|
|
38
44
|
maxCount?: number;
|
|
45
|
+
/**
|
|
46
|
+
* 最大数量提示
|
|
47
|
+
*/
|
|
48
|
+
maxCountTip?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* 文件大小提示
|
|
51
|
+
*/
|
|
52
|
+
maxFileSizeTip?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* 文件类型提示
|
|
55
|
+
*/
|
|
56
|
+
fileExtTip?: boolean;
|
|
39
57
|
/**
|
|
40
58
|
* 文件路径
|
|
41
59
|
*/
|
|
42
60
|
parentPath?: string;
|
|
61
|
+
/**
|
|
62
|
+
* 显示操作文字
|
|
63
|
+
*/
|
|
64
|
+
showActionText?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* 是否显示上线或下线
|
|
67
|
+
*/
|
|
68
|
+
showOnlineSwitch?: boolean;
|
|
43
69
|
}
|
|
44
70
|
|
|
45
71
|
const props = withDefaults(defineProps<UploadListProps>(), {
|
|
@@ -48,12 +74,20 @@ const props = withDefaults(defineProps<UploadListProps>(), {
|
|
|
48
74
|
placeholder: '',
|
|
49
75
|
maxFileSize: 20,
|
|
50
76
|
maxCount: 5,
|
|
77
|
+
maxCountTip: false,
|
|
78
|
+
maxFileSizeTip: true,
|
|
79
|
+
fileExtTip: true,
|
|
80
|
+
showActionText: true,
|
|
81
|
+
showOnlineSwitch: false,
|
|
51
82
|
});
|
|
52
83
|
|
|
84
|
+
const inputFactory = useInputFactory();
|
|
85
|
+
const { errInfo } = inputFactory;
|
|
86
|
+
|
|
53
87
|
const fileList = ref<UploadFile[]>(props.fileList);
|
|
54
88
|
const fileUploader = ref();
|
|
55
89
|
const emit = defineEmits(['update:file-list']);
|
|
56
|
-
|
|
90
|
+
const confirmOpen = ref(false);
|
|
57
91
|
const acceptString = computed(() => (props.fileExt?.length ? props.fileExt.map((ext) => `.${ext}`).join(',') : ''));
|
|
58
92
|
|
|
59
93
|
const beforeUpload: UploadProps['beforeUpload'] = (file) => {
|
|
@@ -75,13 +109,18 @@ const beforeUpload: UploadProps['beforeUpload'] = (file) => {
|
|
|
75
109
|
return false;
|
|
76
110
|
}
|
|
77
111
|
|
|
112
|
+
confirmOpen.value = false;
|
|
78
113
|
return props.autoUpload;
|
|
79
114
|
};
|
|
80
115
|
|
|
81
116
|
const updateFileList: UploadProps['onUpdate:fileList'] = (fileList) => {
|
|
82
117
|
fileList.forEach((file) => {
|
|
83
|
-
|
|
84
|
-
if (
|
|
118
|
+
const fileInfo = file as UploadFile<any>;
|
|
119
|
+
if (!fileInfo.fileName) fileInfo.fileName = fileInfo.name;
|
|
120
|
+
if (props.parentPath) fileInfo.name = path.join('/', props.parentPath, fileInfo.fileName);
|
|
121
|
+
if (!fileInfo.params) fileInfo.params = {};
|
|
122
|
+
fileInfo.params.FileKey = fileInfo.name;
|
|
123
|
+
fileInfo.status = UploadStatus.Pending;
|
|
85
124
|
});
|
|
86
125
|
};
|
|
87
126
|
|
|
@@ -101,6 +140,14 @@ const uploadProps = computed<UploadProps>(() => ({
|
|
|
101
140
|
},
|
|
102
141
|
}));
|
|
103
142
|
|
|
143
|
+
watch(
|
|
144
|
+
() => props.fileList,
|
|
145
|
+
(newVal) => {
|
|
146
|
+
fileList.value = newVal;
|
|
147
|
+
},
|
|
148
|
+
{ deep: true, immediate: true },
|
|
149
|
+
);
|
|
150
|
+
|
|
104
151
|
watch(
|
|
105
152
|
() => fileList.value,
|
|
106
153
|
(newVal) => {
|
|
@@ -109,16 +156,27 @@ watch(
|
|
|
109
156
|
{ deep: true },
|
|
110
157
|
);
|
|
111
158
|
|
|
159
|
+
watch(
|
|
160
|
+
() => props.parentPath,
|
|
161
|
+
(newVal) => {
|
|
162
|
+
if (newVal) {
|
|
163
|
+
fileList.value.forEach((file) => {
|
|
164
|
+
file.params.FileKey = path.join('/', newVal, file.fileName);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
);
|
|
169
|
+
|
|
112
170
|
const downloadFile = (index: number) => {
|
|
113
|
-
|
|
171
|
+
if (!props.downloadUrl) return;
|
|
172
|
+
const minioFile = fileList.value[index].minioFile!;
|
|
114
173
|
const url: IUrlInfo = {
|
|
115
174
|
api: props.downloadUrl.api,
|
|
116
175
|
authorize: props.downloadUrl.authorize,
|
|
117
176
|
url: props.downloadUrl.url,
|
|
118
|
-
// url: props.pageData.urls.download!.url,
|
|
119
177
|
params: {
|
|
120
178
|
Query: {
|
|
121
|
-
FileKey:
|
|
179
|
+
FileKey: minioFile.Key,
|
|
122
180
|
},
|
|
123
181
|
},
|
|
124
182
|
};
|
|
@@ -127,23 +185,34 @@ const downloadFile = (index: number) => {
|
|
|
127
185
|
};
|
|
128
186
|
|
|
129
187
|
const onlineOrOffline = (file: UploadFile) => {
|
|
130
|
-
|
|
131
|
-
|
|
188
|
+
if (file.minioFile) {
|
|
189
|
+
file.minioFile.Status = file.status!;
|
|
190
|
+
}
|
|
132
191
|
};
|
|
133
192
|
|
|
134
193
|
// const previewFile = (index: number) => {
|
|
135
|
-
// const fileInfo =
|
|
194
|
+
// const fileInfo = internalFileList.value[index].minioFile;
|
|
136
195
|
// console.log(fileInfo);
|
|
137
196
|
// };
|
|
138
197
|
|
|
139
198
|
const removeFile = (index: number) => {
|
|
140
199
|
fileList.value.splice(index, 1);
|
|
200
|
+
confirmOpen.value = false;
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const confirmDelFile = (index: number, status?: UploadStatus) => {
|
|
204
|
+
if (status === UploadStatus.Pending) {
|
|
205
|
+
removeFile(index);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
confirmOpen.value = true;
|
|
141
209
|
};
|
|
142
210
|
|
|
143
211
|
const getPlaceholder = (): string => {
|
|
144
|
-
const typeMsg =
|
|
145
|
-
|
|
146
|
-
const
|
|
212
|
+
const typeMsg =
|
|
213
|
+
props.fileExt && props.fileExt.length && props.fileExtTip ? `文件必须为 ${props.fileExt.join('/')}` : '';
|
|
214
|
+
const sizeMsg = props.maxFileSize !== 0 && props.maxFileSizeTip ? `单文件最大 ${props.maxFileSize}MB` : '';
|
|
215
|
+
const countMsg = props.maxCount !== 0 && props.maxCountTip ? `最多 ${props.maxCount} 个文件` : '';
|
|
147
216
|
|
|
148
217
|
return [sizeMsg, typeMsg, countMsg].filter(Boolean).join(',');
|
|
149
218
|
};
|
|
@@ -164,12 +233,13 @@ const getStatusColor = (status?: UploadStatus) => {
|
|
|
164
233
|
return 'cyan';
|
|
165
234
|
}
|
|
166
235
|
};
|
|
236
|
+
|
|
167
237
|
const getStatus = (status?: UploadStatus) => {
|
|
168
238
|
switch (status) {
|
|
169
239
|
case UploadStatus.Uploading:
|
|
170
240
|
return '上传中';
|
|
171
241
|
case UploadStatus.Success:
|
|
172
|
-
return '
|
|
242
|
+
return '上传完成';
|
|
173
243
|
case UploadStatus.Error:
|
|
174
244
|
return '上传失败';
|
|
175
245
|
case UploadStatus.Online:
|
|
@@ -183,14 +253,20 @@ const getStatus = (status?: UploadStatus) => {
|
|
|
183
253
|
</script>
|
|
184
254
|
|
|
185
255
|
<template>
|
|
186
|
-
<div class="w-full border border-solid border-gray-100 mt-1 rounded-md py-5">
|
|
256
|
+
<div class="w-full border border-solid border-gray-100 mt-1 rounded-md py-5" :class="[errInfo?.errClass]">
|
|
187
257
|
<div class="flex items-center justify-between w-full">
|
|
188
258
|
<div class="w-35 mx-3">
|
|
189
|
-
<Upload
|
|
190
|
-
|
|
259
|
+
<Upload
|
|
260
|
+
ref="fileUploader"
|
|
261
|
+
v-bind="uploadProps"
|
|
262
|
+
v-auth="{ role: ['Super', 'Admin'], permit: ':uploadlist:upload' }"
|
|
263
|
+
>
|
|
264
|
+
<Button :class="[errInfo?.errClass + '-text']">选择文件</Button>
|
|
191
265
|
</Upload>
|
|
192
266
|
</div>
|
|
193
|
-
<div class="flex-1 text-sm text-gray-500"
|
|
267
|
+
<div class="flex-1 text-sm text-gray-500" :class="[errInfo?.errClass + '-text']">
|
|
268
|
+
{{ getPlaceholder() }}
|
|
269
|
+
</div>
|
|
194
270
|
<!-- <Button v-if="!autoUpload" @click="manualUpload" class="mr-3">开始上传</Button> -->
|
|
195
271
|
</div>
|
|
196
272
|
|
|
@@ -208,28 +284,92 @@ const getStatus = (status?: UploadStatus) => {
|
|
|
208
284
|
</span>
|
|
209
285
|
</div>
|
|
210
286
|
<div class="flex items-center">
|
|
211
|
-
<
|
|
212
|
-
class="
|
|
213
|
-
v-if="
|
|
214
|
-
|
|
215
|
-
|
|
287
|
+
<div
|
|
288
|
+
class="mr-2"
|
|
289
|
+
v-if="
|
|
290
|
+
showOnlineSwitch && (file.status == UploadStatus.Online || file.status == UploadStatus.Offline)
|
|
291
|
+
"
|
|
216
292
|
>
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
293
|
+
<Tooltip title="上线或下线">
|
|
294
|
+
<Switch
|
|
295
|
+
v-model:checked="file.status"
|
|
296
|
+
:data="[
|
|
297
|
+
{ label: '上线', value: UploadStatus.Online },
|
|
298
|
+
{ label: '下线', value: UploadStatus.Offline },
|
|
299
|
+
]"
|
|
300
|
+
@change="onlineOrOffline(file)"
|
|
301
|
+
v-auth="{ role: ['Super', 'Admin'], permit: ':uploadlist:online' }"
|
|
302
|
+
/>
|
|
303
|
+
</Tooltip>
|
|
304
|
+
</div>
|
|
305
|
+
<div
|
|
306
|
+
class="flex items-center text-blue-500 hover:text-blue-700 mr-1 cursor-pointer"
|
|
307
|
+
v-if="downloadUrl && (file.status == UploadStatus.Online || file.status == UploadStatus.Offline)"
|
|
222
308
|
>
|
|
309
|
+
<Tooltip title="下载">
|
|
310
|
+
<ToolIcon
|
|
311
|
+
icon="icon-download"
|
|
312
|
+
v-auth="{ role: ['Super', 'Admin'], permit: ':uploadlist:download' }"
|
|
313
|
+
clickable
|
|
314
|
+
@click="downloadFile(index)"
|
|
315
|
+
/>
|
|
316
|
+
<span
|
|
317
|
+
v-if="showActionText"
|
|
318
|
+
class="mr-2 text-sm text-nowrap"
|
|
319
|
+
v-auth="{ role: ['Super', 'Admin'], permit: ':uploadlist:download' }"
|
|
320
|
+
@click="downloadFile(index)"
|
|
321
|
+
>下载</span
|
|
322
|
+
>
|
|
323
|
+
</Tooltip>
|
|
324
|
+
</div>
|
|
223
325
|
<!-- <span class="text-blue-500 hover:text-blue-700 mr-4" @click="previewFile(index)">预览</span> -->
|
|
224
|
-
<
|
|
326
|
+
<div class="flex items-center text-red-500 hover:text-red-700 cursor-pointer">
|
|
327
|
+
<Popconfirm
|
|
328
|
+
v-model:open="confirmOpen"
|
|
329
|
+
cancelText="否"
|
|
330
|
+
okText="是"
|
|
331
|
+
title="确定删除该文件吗?"
|
|
332
|
+
:okButtonProps="{ size: 'small' }"
|
|
333
|
+
:cancelButtonProps="{ size: 'small' }"
|
|
334
|
+
@confirm="removeFile(index)"
|
|
335
|
+
@cancel="confirmOpen = false"
|
|
336
|
+
>
|
|
337
|
+
<Tooltip title="删除">
|
|
338
|
+
<div @click="confirmDelFile(index, file.status)">
|
|
339
|
+
<ToolIcon
|
|
340
|
+
icon="icon-new"
|
|
341
|
+
:angle="45"
|
|
342
|
+
v-auth="{ role: ['Super', 'Admin'], permit: ':uploadlist:delete' }"
|
|
343
|
+
clickable
|
|
344
|
+
/>
|
|
345
|
+
<span
|
|
346
|
+
v-if="showActionText"
|
|
347
|
+
class="text-sm text-nowrap"
|
|
348
|
+
v-auth="{ role: ['Super', 'Admin'], permit: ':uploadlist:delete' }"
|
|
349
|
+
>删除</span
|
|
350
|
+
>
|
|
351
|
+
</div>
|
|
352
|
+
</Tooltip>
|
|
353
|
+
</Popconfirm>
|
|
354
|
+
</div>
|
|
225
355
|
</div>
|
|
226
356
|
</div>
|
|
227
357
|
|
|
228
358
|
<!-- 上传进度条 -->
|
|
229
|
-
<div>
|
|
359
|
+
<div v-if="file.status !== UploadStatus.Online && file.status !== UploadStatus.Offline && file.status !== UploadStatus.Success">
|
|
230
360
|
<Progress :percent="file.percent" :stroke-width="2" :show-info="false" style="height: 2px"></Progress>
|
|
231
361
|
</div>
|
|
232
362
|
</div>
|
|
233
363
|
</div>
|
|
234
364
|
</div>
|
|
235
365
|
</template>
|
|
366
|
+
|
|
367
|
+
<style scoped>
|
|
368
|
+
.error {
|
|
369
|
+
border-color: #ff4d4f80;
|
|
370
|
+
box-shadow: 0 0 3px 0 #ff4d4f;
|
|
371
|
+
}
|
|
372
|
+
.error-text {
|
|
373
|
+
color: #ff4d4f !important;
|
|
374
|
+
}
|
|
375
|
+
</style>
|
package/src/components/index.ts
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
// 基础组件
|
|
2
|
-
export {
|
|
2
|
+
export {
|
|
3
|
+
Button,
|
|
4
|
+
Tooltip,
|
|
5
|
+
AppIcon,
|
|
6
|
+
Fullscreen,
|
|
7
|
+
Helper,
|
|
8
|
+
Icon,
|
|
9
|
+
LayoutIcon,
|
|
10
|
+
ProjectIcon,
|
|
11
|
+
ToolIcon,
|
|
12
|
+
Loading,
|
|
13
|
+
} from './common';
|
|
3
14
|
|
|
4
15
|
// 内容组件
|
|
5
16
|
export {
|
|
@@ -27,6 +38,7 @@ export {
|
|
|
27
38
|
InputPassword,
|
|
28
39
|
InputNumber,
|
|
29
40
|
PropEditor,
|
|
41
|
+
type PropConfigItem,
|
|
30
42
|
Radio,
|
|
31
43
|
RadioStatus,
|
|
32
44
|
RangePicker,
|
|
@@ -12,7 +12,7 @@ type BehaviorType = 'remove' | 'disable';
|
|
|
12
12
|
* 权限参数类型
|
|
13
13
|
*/
|
|
14
14
|
interface PermissionParams {
|
|
15
|
-
url
|
|
15
|
+
url?: string;
|
|
16
16
|
role?: string | string[];
|
|
17
17
|
permit?: string;
|
|
18
18
|
behavior?: BehaviorType;
|
|
@@ -77,13 +77,18 @@ const checkPermission = (params: PermissionParams): boolean => {
|
|
|
77
77
|
const userInfoStore = useUserInfo();
|
|
78
78
|
const { url, role, permit } = params;
|
|
79
79
|
|
|
80
|
+
// 如果都没有配置,默认允许访问
|
|
81
|
+
if (isEmpty(role) && isEmpty(permit)) {
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
|
|
80
85
|
// 获取当前URL,优先使用传入的url,否则使用当前路由路径
|
|
81
|
-
const currentUrl = url;
|
|
86
|
+
const currentUrl = url ?? window.location.hash?.split('#')?.[1] ?? window.location.pathname;
|
|
82
87
|
|
|
83
88
|
if (EnvConfig.VITE_PERMISSION_MODE === 'role') {
|
|
84
89
|
// 仅判断角色权限
|
|
85
|
-
if (!isEmpty(role)
|
|
86
|
-
return userInfoStore.hasRole(role);
|
|
90
|
+
if (!isEmpty(role)) {
|
|
91
|
+
return userInfoStore.hasRole(role!);
|
|
87
92
|
}
|
|
88
93
|
// 如果没有配置角色,默认允许访问
|
|
89
94
|
return true;
|
|
@@ -102,13 +107,8 @@ const checkPermission = (params: PermissionParams): boolean => {
|
|
|
102
107
|
}
|
|
103
108
|
|
|
104
109
|
// 检查功能权限
|
|
105
|
-
if (!isEmpty(permit)
|
|
106
|
-
hasPermitPermission = userInfoStore.hasPermit(currentUrl, permit);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// 如果都没有配置,默认允许访问
|
|
110
|
-
if (isEmpty(role) && isEmpty(permit)) {
|
|
111
|
-
return true;
|
|
110
|
+
if (!isEmpty(permit)) {
|
|
111
|
+
hasPermitPermission = userInfoStore.hasPermit(currentUrl, permit!);
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
// 任意一个有权限即可
|