feat: 添加原料库上传功能和相关组件

- 在 `raw-material` 组件中添加上传按钮和抽屉组件
- 新增 `add-raw-material-drawer` 组件及其样式文件
- 更新样式文件以支持新的上传界面
- 调整 Ant Select 和 Drawer 组件的样式
This commit is contained in:
rd
2025-09-16 17:03:06 +08:00
parent f1348469f3
commit c357e4a885
9 changed files with 407 additions and 24 deletions

View File

@ -0,0 +1,244 @@
<script lang="tsx">
import { Drawer, Button, Upload, Table, Input } from 'ant-design-vue';
const { Column } = Table;
const { TextArea } = Input;
import CommonSelect from '@/components/common-select';
import ImgLazyLoad from '@/components/img-lazy-load';
import { formatFileSize, getVideoInfo, formatDuration, formatUploadSpeed } from '@/utils/tools';
import { getRawMaterialTagsList } from '@/api/all/generationWorkshop';
import icon1 from '@/assets/img/media-account/icon-delete.png';
const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp'];
const videoExtensions = ['.mp4', '.mov', '.avi', '.flv', '.wmv', '.m4v'];
const documentExtensions = ['.txt', '.doc', '.docx', '.pdf', '.xls', '.xlsx'];
export default defineComponent({
setup(_, { attrs, slots, expose }) {
const visible = ref(false);
const submitLoading = ref(false);
const uploadData = ref([]);
const tagData = ref([]);
const checkSuccessNum = computed(() => {
return uploadData.value.filter((item) => item.status === 'success').length;
});
const getTagData = async () => {
const { code, data } = await getRawMaterialTagsList();
if (code === 200) {
tagData.value = data ?? [];
}
};
const open = () => {
getTagData();
visible.value = true;
};
const onClose = () => {
visible.value = false;
uploadData.value = [];
tagData.value = [];
submitLoading.value = false;
};
const handleUpload = async (option) => {
// console.log('handleUpload', option);
};
const getFileType = (fileName) => {
const ext = fileName.slice(fileName.lastIndexOf('.')).toLowerCase();
if (imageExtensions.includes(ext)) {
return '图片';
} else if (videoExtensions.includes(ext)) {
return '视频';
} else if (documentExtensions.includes(ext)) {
return '文档';
} else {
return '其他';
}
};
const onConfirm = () => {
console.log('onConfirm');
};
const handleDelete = (file) => {
uploadData.value = uploadData.value.filter((item) => item.uid !== file.uid);
};
const renderUploadList = (file, actions) => {
if (!uploadData.value.length) return null;
console.log('renderUploadList', uploadData.value);
return (
<>
<p class="cts !color-#939499 mb-10px">
已上传<span class="!color-#211F24">{`${checkSuccessNum.value}/${uploadData.value.length}`}</span>
</p>
<Table ref="tableRef" dataSource={uploadData.value} pagination={false} class="manuscript-table w-100% flex-1">
<Column
title="文件名称"
dataIndex="name"
key="name"
width={347}
ellipsis={true}
customRender={({ text, record }) => {
return (
<div class="flex items-center">
<ImgLazyLoad width={64} height={64} src={record.cover} class="!rounded-6px mr-16px flex-shrink-0" />
<TextArea
v-model:value={record.name}
placeholder="请输入文件名称"
size="large"
class="w-full !h-72px"
showCount
maxlength={20}
/>
</div>
);
}}
/>
<Column
title="上传状态"
dataIndex="status"
key="status"
width={164}
customRender={({ text, record }) => {
if (record.status === 'done') {
return <span class="upload-text">上传成功</span>;
} else if (record.status === 'error') {
return <span class="upload-text">上传失败</span>;
} else {
return <span class="upload-text">上传中</span>;
}
}}
/>
<Column
title="标签"
dataIndex="tags"
key="tags"
width={243}
customRender={({ text, record }) => {
return (
<CommonSelect
v-model={record.tags}
class="w-full"
multiple
options={tagData}
placeholder="请选择标签"
/>
);
}}
/>
<Column
title="类型"
dataIndex="type"
key="type"
width={80}
customRender={({ text, record }) => {
const fileName = record.name || '';
return <span>{getFileType(fileName)}</span>;
}}
/>
<Column
title="大小"
dataIndex="size"
key="size"
width={100}
customRender={({ text, record }) => formatFileSize(record.size)}
/>
<Column
title="操作"
key="action"
width={118}
fixed="right"
customRender={({ text, record }) => {
return (
<div class="flex items-center">
<Button type="text" class="!h-22px !p-0 mr-16px">
取消生成
</Button>
<img
class="cursor-pointer"
src={icon1}
width="14"
height="14"
onClick={() => handleDelete(record)}
/>
</div>
);
}}
/>
</Table>
</>
);
};
expose({
open,
});
return () => (
<Drawer
width={1100}
title="上传到原料库"
rootClassName="xt-add-raw-material-modal"
v-model:open={visible.value}
onClose={onClose}
>
<section class="content flex-1 pt-8px px-24px pb-24px">
<div class=" rounded-16px bg-#F7F8FA p-16px flex flex-col items-center mb-24px">
<Upload
v-model:file-list={uploadData.value}
action="/"
multiple
customRequest={handleUpload}
class="w-full mb-16px"
accept={[...imageExtensions, ...videoExtensions, ...documentExtensions].join(',')}
showUploadList={false}
>
<div
class="upload-box rounded-8px cursor-pointer h-100px w-full border border-dashed border-#D7D7D9 flex flex-col items-center justify-center w-full">
<icon-plus size="14" class="mb-10px color-#55585F" />
<span class="cts">点击或拖拽文件到此处上传</span>
</div>
</Upload>
<p class="mb-4px cts !color-#939499 !text-12px !lh-20px">{`视频格式视频格式MP4、AVI、MOV大小<1000MB`}</p>
<p
class="mb-4px cts !color-#939499 !text-12px !lh-20px">{`图片格式PNG、JPG、JPEG、GIF、WEBP、BMP大小<20MB`}</p>
<p class="cts !color-#939499 !text-12px !lh-20px">{`文本格式TXT、DOC、DOCX、PDF大小<20MB`}</p>
</div>
{renderUploadList()}
</section>
<footer class="footer h-68px px-24px flex items-center justify-end">
<div class="flex items-center">
<Button size="large" onClick={onClose} class="mr-12px">
取消
</Button>
<Button
size="large"
type="primary"
onClick={onConfirm}
loading={submitLoading.value}
disabled={!checkSuccessNum.value}
>
确定
</Button>
</div>
</footer>
</Drawer>
);
},
});
</script>
<style lang="scss">
@import './style.scss';
</style>

View File

@ -0,0 +1,48 @@
.xt-add-raw-material-modal {
.ant-drawer-header {
height: 58px;
border-bottom: none;
}
.ant-upload {
width: 100%;
}
.ant-drawer-body {
padding: 0;
display: flex;
flex-direction: column;
.cts {
color: #211f24;
font-family: $font-family-regular;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 22px;
}
.content {
.upload-box {
transition: all 0.3s;
&:hover {
border-color: #6d4cfe;
}
}
.upload-text {
color: #000;
text-align: center;
font-family: $font-family-regular;
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 20px;
}
}
.footer {
}
}
}

View File

@ -7,6 +7,7 @@ import FilterBlock from './components/filter-block/index.vue';
import RawMaterialTable from './components/table/index.vue';
import DeleteRawMaterialModal from './components/table/delete-file-modal.vue';
import TagsManageModal from './components/tags-manage-modal';
import AddRawMaterialDrawer from './components/add-raw-material-drawer';
import { useTableSelectionWithPagination } from '@/hooks/useTableSelectionWithPagination';
import { getRawMaterialsPage } from '@/api/all/generationWorkshop';
@ -32,6 +33,7 @@ export default defineComponent({
const deleteRawMaterialModalRef = ref(null);
const tagsManageModalRef = ref(null);
const addRawMaterialDrawerRef = ref(null);
const query = ref(cloneDeep(INITIAL_QUERY));
const handleSearch = () => {
@ -91,7 +93,7 @@ export default defineComponent({
tagsManageModalRef.value?.open();
};
const handleAddMaterial = () => {
console.log('handleAddMaterial');
addRawMaterialDrawerRef.value?.open();
};
onMounted(() => {
@ -182,6 +184,7 @@ export default defineComponent({
</div>
<TagsManageModal ref={tagsManageModalRef} />
<AddRawMaterialDrawer ref={addRawMaterialDrawerRef} />
<DeleteRawMaterialModal ref={deleteRawMaterialModalRef} onBatchUpdate={onBatchSuccess} onUpdate={getData} />
</div>
);