From a7acd273c2f25b3a91d31ad7c69d49cbf56d272c Mon Sep 17 00:00:00 2001 From: shenhan Date: Sun, 2 Feb 2025 21:18:39 +0800 Subject: [PATCH 1/4] update --- .vscode/settings.json | 3 +- app/api/common.ts | 15 +- app/assets/img/alipay.png | Bin 0 -> 293 bytes app/assets/img/heart.png | Bin 0 -> 279 bytes app/components.d.ts | 21 + app/components/Authentication.vue | 141 ++ app/components/EditUserInfo.vue | 166 ++ app/components/GlobalConfig.vue | 26 +- app/components/Header.vue | 231 ++ app/components/HeaderSearchInput.vue | 50 + app/components/LoginModal.vue | 168 +- app/components/Payment.vue | 168 ++ app/components/PersonalCenterCard.vue | 278 +++ app/components/ScrollListView.vue | 206 ++ app/components/WechatLoginQr.vue | 105 +- app/constants/index.ts | 2 + app/layouts/default.vue | 243 +-- app/layouts/header.vue | 226 +- app/layouts/home.vue | 124 -- app/middleware/auth.global.ts | 25 +- app/pages/index.vue | 8 +- app/pages/member-center/index.vue | 368 +++- app/pages/model-details/[id].vue | 33 + app/pages/model-square.vue | 39 +- app/pages/personal-center/index.vue | 462 ++++ app/pages/publish-model/index.vue | 177 ++ app/pages/publish-workflow/index.vue | 182 ++ app/pages/publishDetails/[id].vue | 48 - app/pages/workflow-details/[id].vue | 290 +++ app/plugins/naive-ui.ts | 38 +- app/plugins/pinia.ts | 6 + app/stores/menu.ts | 23 +- app/stores/modal.ts | 9 +- app/stores/user.ts | 55 +- app/utils/index.ts | 7 + app/utils/request.ts | 58 +- app/utils/uploadImg.ts | 37 + assets/img/alipay.png | Bin 0 -> 293 bytes assets/img/default-avatar.png | Bin 0 -> 20726 bytes assets/img/heart.png | Bin 0 -> 279 bytes assets/img/phone.png | Bin 0 -> 783 bytes assets/img/wechat.png | Bin 0 -> 860 bytes assets/scss/_tailwind.scss | 9 + assets/scss/_variables.scss | 8 + assets/scss/main.scss | 26 + eslint.config.js | 1 - nuxt.config.ts | 78 +- package.json | 23 +- pnpm-lock.yaml | 2829 ++++++++++--------------- tailwind.config.js | 43 +- tsconfig.json | 3 + 51 files changed, 4393 insertions(+), 2665 deletions(-) create mode 100644 app/assets/img/alipay.png create mode 100644 app/assets/img/heart.png create mode 100644 app/components/Authentication.vue create mode 100644 app/components/EditUserInfo.vue create mode 100644 app/components/Header.vue create mode 100644 app/components/HeaderSearchInput.vue create mode 100644 app/components/Payment.vue create mode 100644 app/components/PersonalCenterCard.vue create mode 100644 app/components/ScrollListView.vue create mode 100644 app/pages/model-details/[id].vue create mode 100644 app/pages/personal-center/index.vue create mode 100644 app/pages/publish-model/index.vue create mode 100644 app/pages/publish-workflow/index.vue delete mode 100644 app/pages/publishDetails/[id].vue create mode 100644 app/pages/workflow-details/[id].vue create mode 100644 app/plugins/pinia.ts create mode 100644 app/utils/index.ts create mode 100644 app/utils/uploadImg.ts create mode 100644 assets/img/alipay.png create mode 100644 assets/img/default-avatar.png create mode 100644 assets/img/heart.png create mode 100644 assets/img/phone.png create mode 100644 assets/img/wechat.png create mode 100644 assets/scss/_tailwind.scss create mode 100644 assets/scss/_variables.scss create mode 100644 assets/scss/main.scss diff --git a/.vscode/settings.json b/.vscode/settings.json index e099f81..4393ffe 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -41,5 +41,6 @@ ], "interline-translate.knownPopularWordCount": 6000, "iconify.annotations": true, - "iconify.inplace": true + "iconify.inplace": true, + "vue3snippets.enable-compile-vue-file-on-did-save-code": false } diff --git a/app/api/common.ts b/app/api/common.ts index 1eb8954..adafe81 100644 --- a/app/api/common.ts +++ b/app/api/common.ts @@ -1,6 +1,6 @@ +import type { ApiResponse, PaginationParams, PaginationResponse } from '~/types/api' // api/common.ts import request from '~/utils/request' -import type { ApiResponse, PaginationParams, PaginationResponse } from '~/types/api' export const commonApi = { // 上传文件 @@ -9,13 +9,18 @@ export const commonApi = { formData.append('file', file) return request.post>('/upload', formData, { headers: { - 'Content-Type': 'multipart/form-data' - } + 'Content-Type': 'multipart/form-data', + }, }) }, // 获取配置信息 getConfig() { return request.get>>('/config') - } -} \ No newline at end of file + }, + + // 获取用户字典 + dictType(query: any) { + return request.get>>(`/system/dict/data/type/${query.type}`) + }, +} diff --git a/app/assets/img/alipay.png b/app/assets/img/alipay.png new file mode 100644 index 0000000000000000000000000000000000000000..eb73f6238d86546152c03f3e341a9933880bfcc4 GIT binary patch literal 293 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|?t8j8hFJKo z4L&H?Y``;lw{zm0Luv+|i9$D8O6FXWEjaJ#WfWX+P`hIPc-Z+4jsGdNF5Ct=PNk^M|fKk*gw#`WzWDx~z;M z3(eD(Gq9cMU!$IT^C{1L=9YIKCd=PUXHatw_x=+7);qO!Vs%Px#(Md!>R5(wilRXxLAP|I^^GItQa}--I5pxM!j}onI^*oz5{5@1iFtPCgyC2Lj zK>hh4?ia25XgpzECyv9s$V~xHKhHG-Fa;uy#9<}{%*i$2P7p-vMv1(Nl+?9S%Mwbl zZGco2o0}p)*V(dRuz$+A1YpwvLVy(Q?u#gckhbNF@gQelnmdkGg7xADK#b*0e7VNF zI-7?+1nH6#*BpZ6w<&@|%_o49lMj|l-_C}VNS97V+Xsx1-n{|ew|h)Sm +import { ref } from 'vue' +const message = useMessage() +const userStore = useUserStore() +const userInfo = userStore.userInfo +const rules = ref({ + name: [ + { + required: true, + validator( value: string) { + if (!value) { + return new Error('请填写姓名') + } + else if (!/^(?:[\u4e00-\u9fa5·]{2,16})$/.test(value)) { + return new Error('输入正确的姓名') + } + return true + }, + trigger:'blur' + }, + ], + idCard: [ + { + required: true, + validator( value: string) { + if (!value) { + return new Error('请填身份证号') + } + else if (!/(^\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(^\d{6}(18|19|20)\d{2}(0\d|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)$)/.test(value)) { + return new Error('输入正确的身份证号') + } + return true + }, + trigger:'blur' + }, + ], +}) + +const ruleForm = ref({ + name: '', + idCard:'', + userId: userInfo.userId +}) +const formRef = ref(null) +const saveInfo = async(e: MouseEvent) => { + e.preventDefault() + formRef.value?.validate(async(errors) => { + if (!errors) { + // try{ + // const res = await request.post('/system/user/updateIdCard',ruleForm.value) + // if(res.code === 200){ + // const data = await userStore.getUserInfo() + // message.success('认证成功') + // ruleForm.value.name = '' + // ruleForm.value.idCard = '' + // onCloseModel() + // } + // }catch(err) { + // console.log('err',err) + // } + const res = await request.post('/system/user/updateIdCard',ruleForm.value) + const data = await userStore.getUserInfo() + isVisible.value = false + } + else { + } + }) +} + +const isVisible = ref(false) +function onCloseModel() { + isVisible.value = false + ruleForm.value.name = '' + ruleForm.value.idCard = '' +} +defineExpose({ + isVisible, +}) +// 生命周期钩子 +onMounted(() => { + // initFormData() +}) + + + + + diff --git a/app/components/EditUserInfo.vue b/app/components/EditUserInfo.vue new file mode 100644 index 0000000..280ef23 --- /dev/null +++ b/app/components/EditUserInfo.vue @@ -0,0 +1,166 @@ + + + + + diff --git a/app/components/GlobalConfig.vue b/app/components/GlobalConfig.vue index 244355f..f58ec3e 100644 --- a/app/components/GlobalConfig.vue +++ b/app/components/GlobalConfig.vue @@ -1,18 +1,18 @@ - - \ No newline at end of file + + + diff --git a/app/components/Header.vue b/app/components/Header.vue new file mode 100644 index 0000000..6b56140 --- /dev/null +++ b/app/components/Header.vue @@ -0,0 +1,231 @@ + + + + + diff --git a/app/components/HeaderSearchInput.vue b/app/components/HeaderSearchInput.vue new file mode 100644 index 0000000..493b19a --- /dev/null +++ b/app/components/HeaderSearchInput.vue @@ -0,0 +1,50 @@ + + + + + + diff --git a/app/components/LoginModal.vue b/app/components/LoginModal.vue index 6dd8e3b..d757015 100644 --- a/app/components/LoginModal.vue +++ b/app/components/LoginModal.vue @@ -1,100 +1,101 @@ --> -
- 测试按钮-3-3 - + + 测试按钮-3-3 + +
- diff --git a/app/pages/member-center/index.vue b/app/pages/member-center/index.vue index 62337ba..000d6cb 100644 --- a/app/pages/member-center/index.vue +++ b/app/pages/member-center/index.vue @@ -1,55 +1,349 @@ \ No newline at end of file + diff --git a/app/pages/model-details/[id].vue b/app/pages/model-details/[id].vue new file mode 100644 index 0000000..634c1f1 --- /dev/null +++ b/app/pages/model-details/[id].vue @@ -0,0 +1,33 @@ + + + diff --git a/app/pages/model-square.vue b/app/pages/model-square.vue index 1146f9e..1967f4b 100644 --- a/app/pages/model-square.vue +++ b/app/pages/model-square.vue @@ -1,12 +1,47 @@ \ No newline at end of file + diff --git a/app/pages/personal-center/index.vue b/app/pages/personal-center/index.vue new file mode 100644 index 0000000..bc232ba --- /dev/null +++ b/app/pages/personal-center/index.vue @@ -0,0 +1,462 @@ + + + + + diff --git a/app/pages/publish-model/index.vue b/app/pages/publish-model/index.vue new file mode 100644 index 0000000..0542e4c --- /dev/null +++ b/app/pages/publish-model/index.vue @@ -0,0 +1,177 @@ + + + diff --git a/app/pages/publish-workflow/index.vue b/app/pages/publish-workflow/index.vue new file mode 100644 index 0000000..5018503 --- /dev/null +++ b/app/pages/publish-workflow/index.vue @@ -0,0 +1,182 @@ + + + diff --git a/app/pages/publishDetails/[id].vue b/app/pages/publishDetails/[id].vue deleted file mode 100644 index 2cdcc64..0000000 --- a/app/pages/publishDetails/[id].vue +++ /dev/null @@ -1,48 +0,0 @@ - - - diff --git a/app/pages/workflow-details/[id].vue b/app/pages/workflow-details/[id].vue new file mode 100644 index 0000000..cc08e2e --- /dev/null +++ b/app/pages/workflow-details/[id].vue @@ -0,0 +1,290 @@ + + + + + diff --git a/app/plugins/naive-ui.ts b/app/plugins/naive-ui.ts index f116856..ed3ae0e 100644 --- a/app/plugins/naive-ui.ts +++ b/app/plugins/naive-ui.ts @@ -1,20 +1,21 @@ -import { setup } from '@css-render/vue3-ssr' import { defineNuxtPlugin } from '#app' +import { setup } from '@css-render/vue3-ssr' export default defineNuxtPlugin((nuxtApp) => { - if (process.server) { + if (import.meta.server) { const { collect } = setup(nuxtApp.vueApp) const originalRenderMeta = nuxtApp.ssrContext?.renderMeta nuxtApp.ssrContext!.renderMeta = () => { if (!originalRenderMeta) { return { - headTags: collect() + headTags: collect(), } } const originalMeta = originalRenderMeta() if ('headTags' in originalMeta) { originalMeta.headTags += collect() - } else { + } + else { originalMeta.headTags = collect() } return originalMeta @@ -22,19 +23,18 @@ export default defineNuxtPlugin((nuxtApp) => { } const { collect } = setup(nuxtApp.vueApp) - useServerHead({ - style: () => { - const stylesString = collect() - const stylesArray = stylesString.split(/<\/style>/g).filter(style => style) - return stylesArray.map((styleString: string) => { - const match = styleString.match(/ diff --git a/app/components/publishModel/EditVersion.vue b/app/components/publishModel/EditVersion.vue new file mode 100644 index 0000000..6d44460 --- /dev/null +++ b/app/components/publishModel/EditVersion.vue @@ -0,0 +1,314 @@ + + + + + diff --git a/app/middleware/auth.global.ts b/app/middleware/auth.global.ts index 2535afa..8bc8b46 100644 --- a/app/middleware/auth.global.ts +++ b/app/middleware/auth.global.ts @@ -1,6 +1,6 @@ // middleware/auth.ts import { authRoutes, verifyBlankRoute } from '@/constants/index' -import { useRouter } from 'vue-router' +// import { useRouter } from 'vue-router' // const router = useRouter() /** diff --git a/app/pages/personal-center/index.vue b/app/pages/personal-center/index.vue index bc232ba..5408922 100644 --- a/app/pages/personal-center/index.vue +++ b/app/pages/personal-center/index.vue @@ -6,8 +6,14 @@ import EditUserInfo from '@/components/EditUserInfo.vue' import { useUserStore } from '@/stores/user' import { formatDate } from '@/utils/index.ts' import { debounce } from 'lodash-es' -import { onMounted, onUnmounted, ref } from 'vue' +import { nextTick, onMounted, onUnmounted, ref } from 'vue' +const loading = ref(false) +const finished = ref(false) +const total = ref(0) // 总条数 +const loadingTrigger = ref(null) + +const observer = ref(null) definePageMeta({ layout: 'default', }) @@ -145,6 +151,7 @@ function initChangeParams() { else { initLikesParams() } + finished.value = false // 重置加载完成状态 getList() } // 切换发布/点赞 @@ -159,6 +166,7 @@ function changeType(id: string) { currentType.value = id initChangeParams() } + function initPageNUm() { if (currentState.value === 'mallProduct') { publishParams.value.pageNum = 1 @@ -166,6 +174,7 @@ function initPageNUm() { else { likesParams.value.pageNum = 1 } + finished.value = false // 重置加载完成状态 getList() } @@ -242,6 +251,10 @@ interface UserData { // 查询发布模型接口 const dataList = ref([]) async function getList() { + if (loading.value || finished.value) + return + + loading.value = true let params = {} if (currentState.value === 'mallProduct') { params = publishParams.value @@ -249,24 +262,77 @@ async function getList() { else { params = likesParams.value } + const url = urlList.value[currentState.value][currentType.value] try { const res = await request.post>(url, params) if (res.code === 200) { - dataList.value = res.rows - } - else { - dataList.value = [] + // 如果是第一页,直接赋值,否则追加数据 + if (params.pageNum === 1) { + dataList.value = res.rows + } + else { + dataList.value = [...dataList.value, ...res.rows] + } + + total.value = res.total // 假设接口返回了总条数 + + // 判断是否加载完所有数据 + if (dataList.value.length >= total.value) { + finished.value = true + } + + // 自动增加页码 + if (currentState.value === 'mallProduct') { + publishParams.value.pageNum++ + } + else { + likesParams.value.pageNum++ + } } } catch (err) { dataList.value = [] + finished.value = true console.log(err) } + finally { + loading.value = false + } } getList() -function topedRefresh() { +onMounted(() => { + window.addEventListener('scroll', topedRefresh) + observer.value = new IntersectionObserver(([entry]) => { + if (entry.isIntersecting && !loading.value && !finished.value) { + getList() + } + }, { + threshold: 0.1, + }) + + if (loadingTrigger.value) { + observer.value.observe(loadingTrigger.value) + } +}) + +onUnmounted(() => { + window.removeEventListener('scroll', topedRefresh) + if (observer.value) { + observer.value.disconnect() + } +}) + +async function topedRefresh() { + if (import.meta.client) { + await nextTick() + window.scrollTo({ + top: 0, + behavior: 'smooth', + }) + } + initPageNUm() } @@ -319,7 +385,7 @@ function topedRefresh() { diff --git a/app/pages/publish-model/index.vue b/app/pages/publish-model/index.vue index 0542e4c..6f8b19a 100644 --- a/app/pages/publish-model/index.vue +++ b/app/pages/publish-model/index.vue @@ -1,129 +1,93 @@ - - + + diff --git a/app/pages/workflow-details/[id].vue b/app/pages/workflow-details/[id].vue index cc08e2e..a13f82a 100644 --- a/app/pages/workflow-details/[id].vue +++ b/app/pages/workflow-details/[id].vue @@ -1,5 +1,5 @@ + + + + diff --git a/app/components/BaseComment.vue b/app/components/BaseComment.vue new file mode 100644 index 0000000..6731b03 --- /dev/null +++ b/app/components/BaseComment.vue @@ -0,0 +1,569 @@ + + + + + diff --git a/app/components/EditUserInfo.vue b/app/components/EditUserInfo.vue index a1d3110..4e14f35 100644 --- a/app/components/EditUserInfo.vue +++ b/app/components/EditUserInfo.vue @@ -1,5 +1,4 @@ + + + + diff --git a/app/components/Payment.vue b/app/components/Payment.vue index 456b693..541cd7f 100644 --- a/app/components/Payment.vue +++ b/app/components/Payment.vue @@ -1,77 +1,175 @@ diff --git a/app/components/PersonalCenterCard.vue b/app/components/PersonalCenterCard.vue index 385784b..35f026c 100644 --- a/app/components/PersonalCenterCard.vue +++ b/app/components/PersonalCenterCard.vue @@ -39,6 +39,22 @@ function toDetails() { } } +// 获取图片详情进行编辑 +const publishPictureData = ref({}) +async function getPublishPicture() { + try { + const res = await request.get(`/image/detail?id=${props.item.id}`) + if (res.code === 200) { + publishPictureData.value = res.data + publishPictureData.value.imagePaths = res.data.imagePaths.split(',') + // publishPictureData.value.tags = res.data.tags.split(',') + } + } + catch (e) { + console.log(e) + } +} + // 处理下拉菜单选项 编辑/删除/置顶 function handleSelect(event: Event, key: string) { event.stopPropagation() // 阻止事件冒泡 @@ -48,6 +64,21 @@ function handleSelect(event: Event, key: string) { else if (key === 'delete') { handleDelete() } + else if (key === 'edit') { + if (props.currentType === '2') { + getPublishPicture() + showPublishImg() + } + else if (props.currentType === '1') { + router.push({ + path: `/publish-workflow`, + query: { + type: 'edit', + id: props.item.id, + }, + }) + } + } } // 置顶 @@ -112,6 +143,23 @@ function getFirstImagePath(imagePaths: string): string { return '' return imagePaths.split(',')[0] || '' } +// 关闭图片 +const isShowPublishPicture = ref(false) +const PublishPictureRef = ref(null) + +function showPublishImg() { + isShowPublishPicture.value = true + if (PublishPictureRef.value) { + PublishPictureRef.value.isVisible = true + } +} + +function closePublishImg() { + isShowPublishPicture.value = false + if (PublishPictureRef.value) { + PublishPictureRef.value.isVisible = false + } +} diff --git a/app/components/Publish-picture.vue b/app/components/Publish-picture.vue new file mode 100644 index 0000000..6921eeb --- /dev/null +++ b/app/components/Publish-picture.vue @@ -0,0 +1,183 @@ + + + + + diff --git a/app/components/TimeLine.vue b/app/components/TimeLine.vue new file mode 100644 index 0000000..d02df7b --- /dev/null +++ b/app/components/TimeLine.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/app/components/WangEditor.vue b/app/components/WangEditor.vue index 3d737cd..a480a53 100644 --- a/app/components/WangEditor.vue +++ b/app/components/WangEditor.vue @@ -1,49 +1,117 @@ + + diff --git a/app/components/WechatLoginQr.vue b/app/components/WechatLoginQr.vue index 26d6827..ca19798 100644 --- a/app/components/WechatLoginQr.vue +++ b/app/components/WechatLoginQr.vue @@ -56,18 +56,6 @@ async function onGetUUid() { }) userStore.setUserInfo(res1.user) window.location.href = '/' - // const parent = getCurrentInstance().parent - // parent.exposed.onCloseLogin() - // store.userInfo = res.data - // router.push('./home') - - // store.dispatch("uuidLogin", res) - // that.$store.dispatch("uuidLogin", res) - // setTimeout(() => { - // that.$router.push({ - // path: that.redirect || "/" - // }).catch(() => {}); - // }, 1500) } }).catch(() => { clearTimeout(pollingTimer) diff --git a/app/components/publishModel/EditVersion.vue b/app/components/publishModel/EditVersion.vue index 6d44460..faf1ffc 100644 --- a/app/components/publishModel/EditVersion.vue +++ b/app/components/publishModel/EditVersion.vue @@ -14,24 +14,25 @@ const props = defineProps({ }) const emit = defineEmits(['update:modelValue', 'createModelsNext']) const acceptTypes = '.safetensors,.ckpt,.pt,.bin,.pth,.zip,.json,.flow,.lightflow,.yaml,.yml,.onnx,.gguf,.sft' -const isDataReady = ref(false) const modelVersionItem = { - versionName: '1.0', // 版本名称 - modelId: 1, // 基础模型 + versionName: '', // 版本名称 + modelId: null, // 基础模型 versionDescription: '"

这是一个描述

这是两张图片之间的一些文字说明

"', // 版本描述 - filePath: 'https://ybl2112.oss-cn-beijing.aliyuncs.com/2025/JANUARY/2/19/4/877e449c-3c0d-4630-a304-91ec110499f2.png', // 文件路径 - fileName: 'ddefd反问句说的', // 文档里没有 ,表里没有 + filePath: '', // 文件路径 + fileName: '', // 文档里没有 ,表里没有 sampleImagePaths: 'https://ybl2112.oss-cn-beijing.aliyuncs.com/2025/JANUARY/2/19/4/877e449c-3c0d-4630-a304-91ec110499f2.png,https://ybl2112.oss-cn-beijing.aliyuncs.com/2025/JANUARY/2/19/4/877e449c-3c0d-4630-a304-91ec110499f2.png,https://ybl2112.oss-cn-beijing.aliyuncs.com/2025/JANUARY/2/19/4/877e449c-3c0d-4630-a304-91ec110499f2.png', // 第三部的图片路径最多20张,切割 - triggerWords: '触发词', // 触发词 + triggerWords: '', // 触发词 isPublic: 1, // 权限是否公开权限 1公开 2自见 - allowFusion: 1, // 待确定 - allowDownloadImage: 1, // 允许下载生图 - allowUsage: 1, // 是否允许使用 isFree: 0, // 是否免费 0免费 1会员 ???? + isOnlineUse: 1, // 是否允许在线使用 + allowDownloadImage: 1, // 允许下载生图 allowSoftwareUse: 1, // 允许在软件旗下使用 + allowFusion: 1, // 是否允许融合 allowCommercialUse: 1, // 是否允许商用 + + // allowUsage: 1, // 是否允许使用 // 允许模型转售或者融合手出售字段没找到? - isExclusiveModel: 1, // 是否为独家模型这个字段 + isExclusiveModel: 0, // 是否为独家模型这个字段 } const isPublicList = [ { @@ -46,12 +47,7 @@ const isPublicList = [ defineExpose({ addVersion, }) -onMounted(() => { - // 确保数据初始化完成 - nextTick(() => { - isDataReady.value = true - }) -}) + const localForm = computed({ get() { return props.modelValue @@ -74,17 +70,22 @@ const formRef = ref(null) const rules = { versionName: { required: true, - message: '请输入模型名称', + message: '', trigger: 'blur', }, modelId: { + required: true, + message: '', + trigger: 'blur', + }, + triggerWords: { required: true, message: '请输入模型名称', trigger: 'blur', }, } function addVersion() { - localForm.value.modelVersionList.push(modelVersionItem) + localForm.value.modelVersionList.unshift(modelVersionItem) } const originalBtnList = ref([ { @@ -132,165 +133,198 @@ async function handleFileChange(event: Event) {