JS默认参数传递额外参数(多文件上传, uploading)
<!-- module name: 应用介绍 --> <template> <div class="main-intro" :style="{ padding: props.type === 'serviceType' ? '0' : '0 16px', margin: props.type === 'serviceType' ? '0 0' : '0 auto', }" > <div class="main-intro-form"> <a-form :model="formState" :label-col="labelCol" > <a-form-item :label="props.appName" :rules="[{ required: true }]"> <a-textarea v-model:value="formState.appIntro" style="width: 100%" :disabled="props.disabledType" /> </a-form-item> <a-form-item label="功能介绍" name="funcIntros"> <div v-for="(intro, index) in formState.funcIntros" :key="index" style="margin-top: 5px; display: flex" > <a-input v-model:value="intro.name" placeholder="输入功能名称" style="width: 45%; border-radius: 5px" autoclear :disabled="props.disabledType" > </a-input> <a-input v-model:value="intro.desc" style="width: 45%; margin-left: 20px; border-radius: 5px" placeholder="功能描述" autoclear :disabled="props.disabledType" > </a-input> <PlusOutlined style="font-size: 20px; margin-left: 10px; color: #cccccc" v-show="index === 0" type="plus" @click="addDomain" /> <MinusOutlined v-show="index !== 0 && index >= state.initIntrosSize" :disabled="index === 1" style="font-size: 20px; margin-left: 10px; color: #cccccc" @click="removeDomain(intro)" /> </div> </a-form-item> <a-form-item label="系统截图" name="osScreenshots"> <div class="snapshot-container"> <div class="img-box" v-for="(item, index) in formState.osFiles"> <div v-if="item.response === undefined"> <a-spin style="position: relative; top: 80px; left: 60px" /> </div> <div v-if="item.response !== undefined"> <div> <img alt="系统截图" class="img" :src="item.response.data.address" /> <CloseCircleFilled class="close-circle" @click="delOsImg(index)" /> </div> <a-input placeholder="截图名称" v-model:value="item.response.data.name" style=" width: 150px; border: 1px solid #e9e9e9; margin-top: 15px; " :disabled="props.disabledType" /> </div> </div> <div class="upload-container"> <a-upload v-model:file-list="formState.osFiles" action="/empower/attachment/upload" list-type="picture-card" :show-upload-list="false" :disabled="props.disabledType" > <plus-outlined /> <div class="ant-upload-text">图片上传</div> </a-upload> </div> </div> </a-form-item> <a-form-item label="学习视频" name="learningVideos"> <div class="videos-container"> <div v-for="(item, index) in formState.learningVideos" class="video-box" > <div v-if="item.response === undefined"> <a-spin style="position: relative; top: 80px; left: 60px" /> </div> <div v-if="item.response !== undefined" style="margin-right: 20px" > <video :src="item.response.data.address" controls class="video" /> <CloseCircleFilled class="close-circle" @click="delVideo(index)" /> </div> <a-input v-model:value="item.response.data.name" style="width: 285px" v-if="item.response !== undefined" placeholder="视频名称" :disabled="props.disabledType" /> </div> <div class="upload-video"> <a-upload v-model:file-list="formState.learningVideos" action="/empower/attachment/upload" list-type="picture-card" :show-upload-list="false" :disabled="props.disabledType" > <div v-if="formState.learningVideos.length < 8"> <plus-outlined /> <div class="ant-upload-text">视频上传</div> </div> </a-upload> </div> </div> </a-form-item> <a-form-item label="学习文档"> <a-upload v-model:file-list="formState.learningFiles" name="file" :multiple="true" action="/empower/attachment/upload" @change="learningFilesChange" :disabled="props.disabledType" style="width: 350px" > <a-button style="background-color: #eaf2ff; border: none" :loading="fileLoading" > <upload-outlined /> <span style="color: #2c79ff">上传</span> </a-button> </a-upload> </a-form-item> </a-form> </div> </div> </template> <script setup> import { defineComponent, ref, reactive, watch, watchEffect, defineExpose, defineEmits, toRaw, onBeforeUnmount, onMounted, } from "vue"; import { useRoute, useRouter } from "vue-router"; import { message } from "ant-design-vue"; import { nanoid } from "nanoid"; import mitt from "@/utils/mitt"; import { MinusOutlined, PlusOutlined, UploadOutlined, CloseCircleFilled, } from "@ant-design/icons-vue"; const router = useRouter(); const route = useRoute(); const labelCol = { style: { width: "75px" } }; const state = reactive({ fileLoading: false, initIntrosSize: 1, }); const previewVisible = ref(false); const previewImage = ref(""); const props = defineProps(["type", "disabledType", "appName"]); const formState = reactive({ appIntro: "", // 应用简介 funcIntros: [ { name: "", desc: "", }, ], // 功能介绍 osFiles: [], // 系统截图文件对象 osScreenshots: [], // 系统截图 learningVideos: [], // 学习视频 learningFiles: [], // 学习文档 fileList: [], }); // ======================= 功能介绍 start ==================== const removeDomain = (intro) => { let index = formState.funcIntros.indexOf(intro); if (index !== -1) { formState.funcIntros.splice(index, 1); } }; const addDomain = () => { formState.funcIntros.push({ name: "", // 功能名称 desc: "", // 功能描述 }); }; // ======================= 功能介绍 end ==================== // ====================== 系统截图 start =================== const handleCancel = () => { previewVisible.value = false; }; const handlePreview = async (file) => { console.log("file 预览", file); if (!file.url && !file.preview) { file.preview = await getBase64(file.originFileObj); } previewImage.value = file.url || file.preview; previewVisible.value = true; }; const handleChange = ({ fileList: newFileList }) => { fileList.value = newFileList; }; const getBase64 = (file) => { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => resolve(reader.result); reader.onerror = (error) => reject(error); }); }; // ====================== 系统截图 end =================== const osFileChange = (e) => { console.log("e:::: ", e); let fileList = [...e.fileList]; let formFiles = fileList.map((file) => { let resArr = []; if (file.response !== undefined) { resArr.push({ snapshotName: "", ...file, address: file.response.data.address, id: file.response.data.id, }); } return resArr; }); if (formFiles.length > 0) { formState.osScreenshots = formFiles.map((formFile) => { return formFile; }); } }; const delOsImg = (index) => { formState.osFiles.splice(index, 1); }; // ====================== 学习视频 start =================== const delVideo = (index) => { formState.learningVideos.splice(index, 1); }; // TODO videosChange const videosChange = (e) => { let fileList = [...e.fileList]; let formFiles = fileList.map((file) => { let resArr = []; if (file.response !== undefined) { resArr.push({ name: "", ...file, address: file.response.data.address, id: file.response.data.id, }); } return resArr; }); if (formFiles.length > 0) { formState.osScreenshots = formFiles.map((formFile) => { return formFile; }); } }; // ====================== 学习视频 end =================== // ====================== 学习文档 start =================== const learningFilesChange = (e) => { let fileList = [...e.fileList]; console.log("fileList::: ", fileList); //read from response and show file link fileList = fileList.map((file) => { if (file.response) { // Component will show file.url as link file.url = file.response.address; } return file; }); formState.fileList = fileList; }; const delFiles = (index) => { formState.learningFiles.splice(index, 1); }; // ====================== 学习文档 end =================== defineExpose({ formState, }); </script> <style lang="scss" scoped> .ant-upload-select-picture-card i { font-size: 32px; color: #999; } .ant-upload-select-picture-card .ant-upload-text { margin-top: 8px; color: #666; } .main-intro { padding: 0 16px; width: 600px; text-align: left; margin: 0 auto; &-form { border-radius: 8px; padding: 0 24px; background-color: #fff; .snapshot-container { display: flex; flex-wrap: wrap; .img-box { width: 150px; margin-right: 15px; position: relative; .img { width: 100%; height: 150px; &:hover { border: 4px solid #4688f1; } } &:hover .close-circle { display: inline-block; } .close-circle { font-size: 20px; color: #4688f1; position: absolute; right: -13px; top: -10px; cursor: pointer; display: none; } } } .videos-container { display: flex; flex-wrap: wrap; .video-box { position: relative; width: 305px; margin-right: 8px; .video { width: 100%; height: 170px; border-radius: 5px; &:hover { border: 4px solid #4688f1; } } &:hover .close-circle { display: inline-block; } .close-circle { font-size: 20px; color: #4688f1; position: absolute; right: 6px; top: -7px; cursor: pointer; display: none; } } } .upload-video { height: 222px; } :deep(.ant-upload.ant-upload-select-picture-card) { width: 150px; height: 171px; border: 1px solid #d0d4da; background-color: #fff; } :deep(.anticon-plus) { color: #d7d7d7; font-size: 30px; font-weight: 900; } :deep(.ant-btn .anticon) { color: #2c79ff; } :deep(.ant-upload-list) { width: 40% !important; } } } :deep(.ant-upload-list-item) { width: 450px; } </style>
学而不思则罔,思而不学则殆!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具