fix: 一些基本功能

This commit is contained in:
2025-03-07 08:18:45 +08:00
parent 638462295d
commit 4cdb8351d4
453 changed files with 13336 additions and 24197 deletions

View File

@@ -0,0 +1,32 @@
import type { CustomComponentType } from './types';
import type { Component } from 'vue';
import { getFileNameWithoutExtension, toPascalCase } from '#/util/tool';
const componentMap = new Map<CustomComponentType | string, Component>();
// import.meta.glob() 直接引入所有的模块 Vite 独有的功能
const modules = import.meta.glob(
['./components/**/*.vue', '../../views/**/components/form/*.vue'],
{ eager: true },
);
// 加入到路由集合中
Object.keys(modules).forEach((key) => {
if (!key.includes('-ignore')) {
const mod = (modules as any)[key].default || {};
// ./components/ApiDict.vue
// 获取ApiDict
const compName = getFileNameWithoutExtension(key);
componentMap.set(toPascalCase(compName), mod);
}
});
/**
* 注册组件
* @param components
*/
export const registerComponent = (components: any) => {
componentMap.forEach((value, key) => {
components[key] = value as Component;
});
};
export { componentMap };

View File

@@ -0,0 +1,62 @@
<script setup lang="ts">
import { useVModel } from '@vueuse/core';
import { Upload } from 'ant-design-vue';
import { uploadFile } from '#/api/core/upload';
import { Icon } from '#/components/icon';
defineOptions({
inheritAttrs: false,
});
const props = defineProps({
value: {
type: String,
default: '',
},
});
const emits = defineEmits(['update:value']);
const mValue = useVModel(props, 'value', emits, {
defaultValue: props.value,
passive: true,
});
const customRequest = (e: any) => {
uploadFile({
file: e.file,
}).then((data: any) => {
mValue.value = data.url;
});
};
const handleRemove = (e: Event) => {
e.stopPropagation();
mValue.value = '';
};
</script>
<template>
<Upload
:custom-request="customRequest"
:show-upload-list="false"
list-type="picture-card"
>
<div v-if="mValue" class="m-avatar-wrap">
<Icon
class="m-avatar-icon-delete"
icon="ant-design:delete-outlined"
@click="handleRemove"
/>
<img :src="value" width="100%" />
</div>
<Icon v-else icon="ant-design:plus-outlined" />
</Upload>
</template>
<style lang="less" scoped>
.m-avatar-wrap {
position: relative;
height: 102px;
.m-avatar-icon-delete {
position: absolute;
top: 0;
right: 0;
cursor: pointer;
}
}
</style>

View File

@@ -0,0 +1,162 @@
<script setup lang="ts">
import { computed, onMounted, type PropType, ref, useAttrs, watch } from 'vue';
import { useAccessStore } from '@vben/stores';
import { Button } from 'ant-design-vue';
import {
message,
Upload,
type UploadChangeParam,
type UploadFile,
} from 'ant-design-vue';
import { getFileInfoByIds } from '#/api/core/upload';
import { Icon } from '#/components/icon';
import { omit } from '#/util/tool';
defineOptions({
name: 'Upload',
inheritAttrs: false,
});
const props = defineProps({
value: {
// 文件id多个使用英文逗号分割
type: [String] as PropType<string>,
default: undefined,
},
action: {
// 上传的地址
type: String,
default: `${import.meta.env.VITE_GLOB_API_URL}/sys/fileInfo/upload`,
},
headers: {
// 上传请求的 headers
type: Object,
default: () => ({}),
},
disabled: {
// 是否禁用
type: Boolean,
default: false,
},
listType: {
// 上传类型
type: String as PropType<'picture' | 'picture-card' | 'text'>,
default: 'text',
},
maxCount: {
// 最大上传数量
type: Number,
default: undefined,
},
uploadName: {
// 文件上传字段名,name被字段名覆写了要换成uploadName
type: String,
default: undefined,
},
separator: {
// 文件id分隔符
type: String,
default: ',',
},
});
const emits = defineEmits(['update:modelValue']);
const accessStore = useAccessStore();
const fileList = ref<UploadFile[]>([]);
const attrs = useAttrs();
const bindProps = computed(() => {
return {
...omit(attrs, ['onChange', 'onInput', 'onBlur', 'name']),
...props,
name: props.uploadName || 'file',
};
});
const _getFileInfoByIds = (val: string | string[] | undefined) => {
if (!val) return;
getFileInfoByIds({
fileInfoIds: val,
}).then((res) => {
fileList.value = res || [];
});
};
const mValue = computed({
get() {
return props.value;
},
set(val) {
emits('update:modelValue', val);
},
});
const mHeaders = {
Authorization: `Bearer ${accessStore.accessToken}`,
...props.headers,
};
const handleChange = (info: UploadChangeParam) => {
if (info.file.status === 'done') {
// 更新file-list当前file的uid
fileList.value = fileList.value.map((item) => {
if (item.uid === info.file.uid) {
return {
...item,
uid: info.file.response.data.fileInfoId,
};
}
return item;
});
mValue.value = fileList.value
.map((item) => {
return item.uid;
})
.join(props.separator);
} else if (info.file.status === 'error') {
message.error(`${info.file.name} file upload failed.`);
}
};
const handleRemove = (file: any) => {
mValue.value = fileList.value
.filter((item: any) => item.uid !== file.uid)
.map((item: any) => item.uid)
.join(props.separator);
return true;
};
watch(
() => props.value,
(val) => {
_getFileInfoByIds(val);
},
);
onMounted(() => {
_getFileInfoByIds(props.value);
});
</script>
<template>
<Upload
v-model:file-list="fileList"
v-bind="bindProps"
:action="action"
:disabled="disabled"
:headers="mHeaders"
:list-type="listType"
:max-count="maxCount"
@change="handleChange"
@remove="handleRemove"
>
<template #default>
<Icon
v-if="listType === 'picture-card'"
:size="24"
icon="ant-design:plus-outlined"
/>
<Button v-else>
上传文件
<template #icon>
<Icon icon="ant-design:cloud-upload-outlined" />
</template>
</Button>
</template>
<template v-for="item in Object.keys($slots)" #[item]="data">
<slot :name="item" v-bind="data || {}"></slot>
</template>
</Upload>
</template>

View File

@@ -0,0 +1,7 @@
// export { default as ApiCheckboxGroup } from './components/api-checkbox-group.vue';
// export { default as ApiDict } from './components/api-dict.vue';
// export { default as ApiRadioGroup } from './components/api-radio-group.vue';
// export { default as ApiSelect } from './components/api-select.vue';
// export { default as ApiTreeSelect } from './components/api-tree-select.vue';
// export { default as IconPicker } from './components/icon-picker.vue';
export { default as Upload } from './components/upload.vue';

View File

@@ -0,0 +1,7 @@
export type CustomComponentType =
| 'ApiCheckboxGroup'
| 'ApiDict'
| 'ApiRadioGroup'
| 'ApiSelect'
| 'ApiTreeSelect'
| 'IconPicker';