feat: 内容稿件上传、编辑

This commit is contained in:
rd
2025-07-29 18:12:25 +08:00
parent 4e10e1db04
commit d8a4fc73e2
15 changed files with 739 additions and 272 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 604 B

View File

@ -3,8 +3,8 @@
<template #content>
<div :style="contentStyle" class="tip-content">{{ props.context }}</div>
</template>
<div v-bind="$attrs" class="overflow-hidden">
<div ref="Text" :class="`${isShow ? '' : `line-${props.line}`} `" class="overflow-text">
<div class="overflow-hidden">
<div v-bind="$attrs" ref="Text" :class="`${isShow ? '' : `line-${props.line}`} `" class="overflow-text">
{{ props.context }}
</div>
<div
@ -25,11 +25,8 @@
<script setup>
import { ref, reactive, toRefs, onBeforeMount, onMounted, watchEffect, computed, watch, nextTick } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import elementResizeDetectorMaker from 'element-resize-detector';
const route = useRoute();
const router = useRouter();
const props = defineProps({
context: {
type: String,

View File

@ -28,6 +28,20 @@ const COMPONENTS: AppRouteRecordRaw[] = [
},
component: () => import('@/views/creative-generation-workshop/manuscript/list/index.vue'),
},
{
path: 'upload/:id',
name: 'ManuscriptUpload',
meta: {
locale: '稿件上传',
requiresAuth: false,
requireLogin: false,
hideFooter: true,
roles: ['*'],
hideInMenu: true,
activeMenu: 'ManuscriptList',
},
component: () => import('@/views/creative-generation-workshop/manuscript/upload/index.vue'),
},
{
path: 'edit/:id',
name: 'ManuscriptEdit',
@ -46,7 +60,7 @@ const COMPONENTS: AppRouteRecordRaw[] = [
path: 'detail/:id',
name: 'ManuscriptDetail',
meta: {
locale: '账号详情',
locale: '稿件详情',
requiresAuth: false,
requireLogin: false,
roles: ['*'],

View File

@ -0,0 +1,255 @@
<script lang="jsx">
import { Form, FormItem, Input, Textarea, Upload, Message as AMessage } from '@arco-design/web-vue';
import CommonSelect from '@/components/common-select';
import { VueDraggable } from 'vue-draggable-plus';
import { getProjectList } from '@/api/all/propertyMarketing';
import icon1 from '@/assets/img/creative-generation-workshop/icon-close.png';
// 初始表单数据
// const INITIAL_FORM = {
// title: '',
// desc: '',
// project_ids: [],
// files: [],
// };
// 表单验证规则
const FORM_RULES = {
title: [{ required: true, message: '请输入标题' }],
};
export default {
name: 'ManuscriptForm',
props: {
modelValue: {
type: Object,
default: () => {},
},
rules: {
type: Object,
default: () => FORM_RULES,
},
},
emits: ['reValidate', 'change'],
setup(props, { emit, expose }) {
const formRef = ref(null);
const uploadRef = ref(null);
const projects = ref([]);
// 文件上传处理
const handleUpload = (option) => {
const {
fileItem: { file },
} = option;
// 验证文件类型
const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp'];
if (!allowedTypes.includes(file.type)) {
AMessage.error('只能上传图片文件!');
return;
}
// 验证文件大小 (5MB)
const maxSize = 5 * 1024 * 1024;
if (file.size > maxSize) {
AMessage.error('图片大小不能超过5MB');
return;
}
// 验证文件数量
if (props.modelValue.files?.length >= 18) {
AMessage.error('最多只能上传18张图片');
return;
}
const newFile = {
id: Date.now(),
name: file.name,
url: URL.createObjectURL(file),
size: file.size,
type: file.type,
};
props.modelValue.files.push(newFile);
emit('change');
};
// 删除文件
const handleDeleteFile = (index) => {
props.modelValue.files.splice(index, 1);
emit('change');
AMessage.success('删除成功!');
};
// 获取项目列表
const getProjects = async () => {
try {
const { code, data } = await getProjectList();
if (code === 200) {
projects.value = data;
}
} catch (error) {
console.error('获取项目列表失败:', error);
}
};
// 表单验证
const validate = () => {
return new Promise((resolve, reject) => {
formRef.value?.validate((errors) => {
if (errors) {
reject(props.modelValue);
} else {
resolve();
}
});
});
};
// 重置表单
const resetForm = () => {
formRef.value?.resetFields?.();
formRef.value?.clearValidate?.();
};
// 暴露方法
expose({
validate,
resetForm,
});
// 组件挂载时获取项目列表
onMounted(() => {
getProjects();
});
return () => (
<Form ref={formRef} model={props.modelValue} rules={props.rules} layout="vertical" auto-label-width>
<FormItem label="标题" field="title" required>
<Input
v-model={props.modelValue.title}
onChange={() => {
emit('change');
emit('reValidate');
}}
placeholder="请输入标题"
size="large"
class="!w-500px"
/>
</FormItem>
<FormItem label="作品描述" field="desc">
<Textarea
v-model={props.modelValue.desc}
onChange={() => emit('change')}
placeholder="请输入作品描述"
size="large"
class="h-200px !w-784px"
show-word-limit
max-length={1000}
auto-size={{ minRows: 7, maxRows: 9 }}
/>
</FormItem>
<FormItem
field="files"
v-slots={{
label: () => (
<div class="flex items-center">
<span class="cts !color-#211F24 mr-4px">图片</span>
<span class="cts mr-8px">{`(${props.modelValue.files?.length ?? 0}/18)`}</span>
<span class="cts">第一张为首图支持拖拽排序</span>
</div>
),
}}
>
<div>
<div class="">
{/* 已上传的图片列表 */}
<VueDraggable v-model={props.modelValue.files} class="grid grid-cols-7 gap-8px">
{props.modelValue.files?.map((file, index) => (
<div key={file.id} class="group relative cursor-move">
<img
src={file.url}
alt={file.name}
class="w-100px h-100px object-cover rounded-8px border-1px border-#E6E6E8"
/>
<img
src={icon1}
width={16}
height={16}
class="absolute top-2px right-2px cursor-pointer hidden group-hover:block"
onClick={() => handleDeleteFile(index)}
/>
</div>
))}
</VueDraggable>
</div>
{props.modelValue.files?.length < 18 && (
<Upload
class="not-draggable"
ref={uploadRef}
action="/"
draggable
custom-request={handleUpload}
accept=".jpg,.jpeg,.png,.gif,.webp"
show-file-list={false}
multiple
limit={18 - props.modelValue.files?.length}
>
{{
'upload-button': () => (
<div class="upload-box">
<icon-plus size="14" class="mb-16px color-#3C4043" />
<span class="cts !color-#211F24">上传图片</span>
</div>
),
}}
</Upload>
)}
</div>
</FormItem>
<FormItem label="所属项目" field="project_ids">
<CommonSelect
v-model={props.modelValue.project_ids}
onChange={() => emit('change')}
options={projects.value}
placeholder="请选择所属项目"
size="large"
class="!w-280px"
/>
</FormItem>
</Form>
);
},
};
</script>
<style lang="scss" scoped>
.upload-box {
display: flex;
width: 100px;
height: 100px;
cursor: pointer;
transition: all 0.3s ease;
flex-direction: column;
justify-content: center;
align-items: center;
border-radius: 8px;
border: 1px dashed var(--Border-1, #d7d7d9);
background: var(--BG-200, #f2f3f5);
&:hover {
background: var(--Primary-1, #e6e6e8);
}
}
.group {
cursor: move;
border-radius: 8px;
&:hover {
background: linear-gradient(0deg, rgba(0, 0, 0, 0.2) 0%, rgba(0, 0, 0, 0.2) 100%),
url(<path-to-image>) lightgray 0px -40.771px / 100% 149.766% no-repeat;
}
}
</style>

View File

@ -0,0 +1,34 @@
<template>
<a-modal v-model:visible="visible" title="退出编辑" width="480px" @close="onClose">
<div class="flex items-center">
<img :src="icon1" width="20" height="20" class="mr-12px" />
<span>内容已修改尚未保存若退出编辑本次修改将不保存</span>
</div>
<template #footer>
<a-button size="medium" @click="onClose">继续编辑</a-button>
<a-button type="primary" class="ml-8px" size="medium" @click="onConfirm">确认退出</a-button>
</template>
</a-modal>
</template>
<script setup>
import { ref } from 'vue';
import icon1 from '@/assets/img/media-account/icon-warn-1.png';
const router = useRouter();
const visible = ref(false);
const onClose = () => {
visible.value = false;
};
const onConfirm = () => {
onClose();
router.push({ name: 'ManuscriptList' });
};
const open = () => {
visible.value = true;
};
defineExpose({ open });
</script>

View File

@ -1,248 +1,99 @@
<script lang="jsx">
import {
Modal,
Form,
FormItem,
Input,
RadioGroup,
Radio,
Upload,
Button,
Message as AMessage,
Textarea,
} from '@arco-design/web-vue';
import TextOverTips from '@/components/text-over-tips';
import CommonSelect from '@/components/common-select';
import { EnumManuscriptType } from '@/views/creative-generation-workshop/manuscript/list/constants';
import { getProjectList } from '@/api/all/propertyMarketing';
import icon1 from '@/assets/img/creative-generation-workshop/icon-photo.png';
import icon2 from '@/assets/img/creative-generation-workshop/icon-video.png';
const generateMockData = (count = 20) =>
Array.from({ length: count }, (_, i) => ({
id: `item-${i + 1}`,
content: '挖到宝了!这个平价好物让我素颜出门都自信✨挖到宝了!这个平价好物让我素颜出门都自信✨',
type: 1,
}));
const INITIAL_FORM = {
title: '',
desc: '',
project_ids: [],
files: [],
};
import { Button, Message as AMessage } from '@arco-design/web-vue';
import EditForm from '../components/edit-form';
import CancelEditModal from './cancel-edit-modal.vue';
export default {
components: {
EditForm,
},
setup(props, { emit, expose }) {
const router = useRouter();
const formRef = ref(null);
const uploadRef = ref(null);
const form = ref(cloneDeep(INITIAL_FORM));
const projects = ref([]);
const cancelEditModal = ref(null);
const dataSource = ref({});
const editDataSource = ref({});
const uploadLoading = ref(false);
const isSaved = ref(false);
const rules = {
title: [{ required: true, message: '请输入标题' }],
};
const dataSource = generateMockData();
const onCancel = () => {
console.log('onCancel');
};
const onSubmit = () => {
console.log('onSubmit');
};
const handleUpload = (option) => {
console.log('handleUpload', option);
const {
fileItem: { file },
} = option;
// 验证文件类型
const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp'];
if (!allowedTypes.includes(file.type)) {
AMessage.error('只能上传图片文件!');
return;
}
// 验证文件大小 (5MB)
const maxSize = 5 * 1024 * 1024;
if (file.size > maxSize) {
AMessage.error('图片大小不能超过5MB');
return;
}
// 验证文件数量
if (form.value.files.length >= 18) {
AMessage.error('最多只能上传18张图片');
return;
}
// 模拟上传过程
setTimeout(() => {
const newFile = {
id: Date.now(),
name: file.name,
url: URL.createObjectURL(file),
size: file.size,
type: file.type,
};
form.value.files.push(newFile);
console.log(form.value.files);
AMessage.success('上传成功!');
}, 1000);
};
const handleDeleteFile = (index) => {
form.value.files.splice(index, 1);
AMessage.success('删除成功!');
};
const renderLeft = () => {
return (
<Form ref={formRef} model={form.value} rules={rules} layout="vertical" auto-label-width>
<FormItem label="标题" field="title" required>
<Input v-model={form.value.title} placeholder="请输入标题" size="large" class="w-500px" />
</FormItem>
<FormItem label="作品描述" field="desc">
<Textarea
v-model={form.value.desc}
placeholder="请输入作品描述"
size="large"
class="h-200px"
show-word-limit
max-length={1000}
auto-size={{ minRows: 7, maxRows: 9 }}
/>
</FormItem>
<FormItem
field="files"
v-slots={{
label: () => (
<div class="flex items-center">
<span class="cts !color-#211F24 mr-4px">图片</span>
<span class="cts mr-8px">{`(${form.value.files.length}/18)`}</span>
<span class="cts">第一张为首图支持拖拽排序</span>
</div>
),
}}
>
<div class="flex flex-col gap-16px">
{/* 已上传的图片列表 */}
{form.value.files.length > 0 && (
<div class="grid grid-cols-4 gap-12px">
{form.value.files.map((file, index) => (
<div key={file.id} class="relative group">
<img
src={file.url}
alt={file.name}
class="w-full h-120px object-cover rounded-8px border-1px border-#E6E6E8"
/>
<div class="absolute top-8px right-8px opacity-0 group-hover:opacity-100 transition-opacity">
<icon-delete
size="16"
class="color-#fff cursor-pointer bg-#000 bg-opacity-50 rounded-full p-4px"
onClick={() => handleDeleteFile(index)}
/>
</div>
{index === 0 && (
<div class="absolute top-8px left-8px">
<span class="text-10px color-#fff bg-#6D4CFE px-4px py-2px rounded-4px">首图</span>
</div>
)}
</div>
))}
</div>
)}
{/* 上传按钮 */}
{form.value.files.length < 18 && (
<Upload
ref={uploadRef}
action="/"
draggable
custom-request={handleUpload}
accept=".jpg,.jpeg,.png,.gif,.webp"
show-file-list={false}
multiple
limit={18 - form.value.files.length}
>
{{
'upload-button': () => (
<div class="upload-box">
<icon-plus size="14" class="mb-16px color-#3C4043" />
<span class="cts !color-#211F24">上传图片</span>
</div>
),
}}
</Upload>
)}
</div>
</FormItem>
<FormItem label="所属项目" field="project_ids">
<CommonSelect
v-model={form.value.project_ids}
options={projects.value}
placeholder="请选择所属项目"
size="large"
class="w-280px"
/>
</FormItem>
</Form>
);
};
const getProjects = async () => {
const { code, data } = await getProjectList();
if (code === 200) {
projects.value = data;
const isModified = !isEqual(dataSource.value, editDataSource.value);
if (isModified && isSaved.value) {
cancelEditModal.value?.open();
} else {
onBack();
}
};
const onSubmit = async () => {
formRef.value?.validate().then(() => {
AMessage.success('保存成功');
isSaved.value = true;
console.log('success');
});
};
const onSubmitAndCheck = () => {
console.log('onSubmitAndCheck');
isSaved.value = false;
};
const getData = () => {
const _data = {
id: 1,
title: '',
desc: '',
content: '挖到宝了!这个平价好物让我素颜出门都自信✨挖到宝了!这个平价好物让我素颜出门都自信✨',
type: 1,
project_ids: [],
files: [],
};
dataSource.value = cloneDeep(_data);
editDataSource.value = cloneDeep(_data);
};
const handleFormChange = () => {
console.log('handleFormChange');
};
const onBack = () => {
router.push({ name: 'ManuscriptList' });
};
onMounted(() => {
getProjects();
getData();
});
return () => (
<>
<div class="manuscript-edit-wrap bg-#fff rounded-8px border-1px border-#D7D7D9 border-solid flex">
<div class="left flex-1 overflow-y-auto p-24px">{renderLeft()}</div>
<div class="right pt-24px pb-12px flex flex-col">
<div class="title flex items-center mb-24px px-24px">
<div class="w-3px h-16px bg-#6D4CFE rounded-2px mr-8px"></div>
<p class="cts bold !color-#211F24 !text-16px !lh-24px mr-8px">上传内容稿件列表</p>
<p class="cts">共30个</p>
</div>
<div class="flex-1 px-24px overflow-y-auto">
{dataSource.map((item) => (
<div key={item.id} class="mb-12px px-8px py-12px flex flex-col rounded-8px bg-#F7F8FA">
<TextOverTips context={item.content} line={1} class="cts bold color-#211F24 mb-8px" />
<div class="flex items-center">
<img
src={item.type === EnumManuscriptType.Image ? icon1 : icon2}
width="16"
height="16"
class="mr-4px"
/>
<span class={`cts ${item.type === EnumManuscriptType.Image ? '!color-#25C883' : '!color-#6D4CFE'}`}>
{item.type === EnumManuscriptType.Image ? '图文' : '视频'}
</span>
</div>
</div>
))}
</div>
<div class="manuscript-edit-wrap">
<div class="flex items-center mb-8px">
<span class="cts color-#4E5969 cursor-pointer" onClick={onBack}>
内容稿件列表
</span>
<icon-oblique-line size="12" class="color-#C9CDD4 mx-4px" />
<span class="cts bold !color-#1D2129">编辑内容稿件</span>
</div>
<div class="flex-1 overflow-y-auto p-24px bg-#fff rounded-8px border-1px border-#D7D7D9 border-solid">
<EditForm ref={formRef} v-model={dataSource.value} onChange={handleFormChange} />
</div>
</div>
<div class="flex justify-end items-center px-16px py-20px w-full bg-#fff footer-row">
<Button size="medium" type="outline" onClick={onCancel} class="mr-12px">
取消上传
退出
</Button>
<Button type="primary" size="medium" onClick={onSubmit}>
批量上传
<Button size="medium" type="outline" onClick={onSubmit} class="mr-12px">
保存
</Button>
<Button type="primary" size="medium" onClick={onSubmitAndCheck} loading={uploadLoading.value}>
保存并审核
</Button>
</div>
<CancelEditModal ref={cancelEditModal} />
</>
);
},
};
</script>
<style lang="scss" scoped>
@import './style.scss';
</style>

View File

@ -1,6 +1,8 @@
$footer-height: 68px;
.manuscript-edit-wrap {
height: calc(100% - 72px);
display: flex;
flex-direction: column;
.cts {
color: #939499;
font-family: $font-family-regular;
@ -12,44 +14,6 @@ $footer-height: 68px;
font-family: $font-family-medium;
}
}
.left {
.upload-box {
display: flex;
width: 100px;
height: 100px;
padding: 24px 16px;
flex-direction: column;
justify-content: center;
align-items: center;
border-radius: 8px;
border: 1px dashed var(--Border-1, #d7d7d9);
background: var(--BG-200, #f2f3f5);
}
}
.right {
border-left: 1px solid var(--Border-2, #e6e6e8);
width: 320px;
flex-shrink: 0;
}
.upload-box {
display: flex;
height: 120px;
padding: 0 16px;
flex-direction: column;
justify-content: center;
align-items: center;
border-radius: 8px;
border: 1px dashed var(--Border-1, #d7d7d9);
background: var(--BG-200, #f2f3f5);
cursor: pointer;
transition: all 0.3s ease;
&:hover {
border-color: var(--Primary-6, #6D4CFE);
background: var(--Primary-1, #f0f0ff);
}
}
}
.footer-row {
position: fixed;

View File

@ -103,6 +103,7 @@ import icon2 from '@/assets/img/creative-generation-workshop/icon-photo.png';
import icon3 from '@/assets/img/creative-generation-workshop/icon-video.png';
const emits = defineEmits(['edit', 'sorterChange', 'delete']);
const router = useRouter();
const props = defineProps({
dataSource: {
@ -120,10 +121,11 @@ const onDelete = (item) => {
emits('delete', item);
};
const onEdit = (item) => {
emits('edit', item);
router.push(`/manuscript/edit/${item.index}`);
};
const onDetail = (item) => {
console.log('onDetail');
router.push(`/manuscript/detail/${item.index}`);
};
const getStatusInfo = (status) => {

View File

@ -144,8 +144,8 @@ export default {
};
// 跳转编辑
const goEdit = () => {
router.push(`/manuscript/edit/1`);
const goUpload = () => {
router.push(`/manuscript/upload/1`);
onClose();
};
@ -302,7 +302,7 @@ export default {
<Button size="medium" onClick={onClose}>
取消
</Button>
<Button type="primary" size="medium" onClick={goEdit}>
<Button type="primary" size="medium" onClick={goUpload}>
确认
</Button>
</>

View File

@ -24,7 +24,6 @@
:dataSource="dataSource"
@sorterChange="handleSorterChange"
@delete="handleDelete"
@edit="handleEdit"
/>
<div v-if="pageInfo.total > 0" class="pagination-box">
<a-pagination
@ -141,9 +140,6 @@ const handleDelete = (item) => {
const { id, name } = item;
deleteManuscriptModalRef.value?.open({ id, name: `${name}` });
};
const handleEdit = (item) => {
// addManuscriptModalRef.value?.open(item.id);
};
onMounted(() => {
getData();

View File

@ -0,0 +1,38 @@
<template>
<a-modal v-model:visible="visible" title="确认提示" width="480px" @close="onClose">
<div class="flex items-center">
<img :src="icon1" width="20" height="20" class="mr-12px" />
<span>确认取消上传这 {{ num }} 个文件吗此操作不可恢复</span>
</div>
<template #footer>
<a-button size="medium" @click="onClose">继续编辑</a-button>
<a-button type="primary" class="ml-8px" size="medium" @click="onConfirm">确认取消</a-button>
</template>
</a-modal>
</template>
<script setup>
import { ref } from 'vue';
import icon1 from '@/assets/img/media-account/icon-warn-1.png';
const router = useRouter();
const visible = ref(false);
const num = ref('');
const onClose = () => {
num.value = '';
visible.value = false;
};
const onConfirm = () => {
onClose();
router.push({ name: 'ManuscriptList' });
};
const open = (manusNum) => {
num.value = manusNum;
visible.value = true;
};
defineExpose({ open });
</script>

View File

@ -0,0 +1,225 @@
<script lang="jsx">
import { Button, Message as AMessage } from '@arco-design/web-vue';
import TextOverTips from '@/components/text-over-tips';
import EditForm from '../components/edit-form';
import CancelUploadModal from './cancel-upload-modal.vue';
import UploadSuccessModal from './upload-success-modal.vue';
import { EnumManuscriptType } from '@/views/creative-generation-workshop/manuscript/list/constants';
import icon1 from '@/assets/img/creative-generation-workshop/icon-photo.png';
import icon2 from '@/assets/img/creative-generation-workshop/icon-video.png';
const generateMockData = (count = 20) =>
Array.from({ length: count }, (_, i) => ({
id: `${i + 1}`,
title: '',
desc: '',
content: '挖到宝了!这个平价好物让我素颜出门都自信✨挖到宝了!这个平价好物让我素颜出门都自信✨',
type: 1,
project_ids: [],
files: [],
}));
export default {
components: {
EditForm,
},
setup(props, { emit, expose }) {
const formRef = ref(null);
const cancelUploadModal = ref(null);
const uploadSuccessModal = ref(null);
const dataSource = ref([]);
const selectCardInfo = ref({});
const errorDataCards = ref([]);
const uploadLoading = ref(false);
const onCancel = () => {
cancelUploadModal.value?.open(dataSource.value.length);
};
const validateDataSource = () => {
return new Promise((resolve) => {
dataSource.value.forEach((item) => {
if (!item.title?.trim()) {
errorDataCards.value.push(item);
}
});
if (!errorDataCards.value.length) {
resolve();
}
if (errorDataCards.value.length > 0) {
AMessage.error(`${errorDataCards.value.length} 个必填信息未填写,请检查`);
setTimeout(() => {
const el = document.getElementById(`card-${errorDataCards.value[0]?.id}`);
if (el) el.scrollIntoView({ behavior: 'smooth', block: 'center' });
}, 100);
return;
}
});
};
const onSubmit = async () => {
formRef.value
?.validate()
.then(() => {
validateDataSource().then((res) => {
console.log('success');
uploadSuccessModal.value?.open(dataSource.value);
});
})
.catch((err) => {
errorDataCards.value.push(err);
});
};
const onSubmitAndCheck = () => {
console.log('onSubmitAndCheck');
};
const handleReValidate = () => {
formRef.value?.validate().then(() => {
deleteErrorCard(selectCardInfo.value);
});
};
const onselect = (item) => {
selectCardInfo.value = item;
};
const deleteErrorCard = (item) => {
const errorIndex = errorDataCards.value.findIndex((v) => v.id === item.id);
errorIndex > -1 && errorDataCards.value.splice(errorIndex, 1);
};
const onDelete = (e, item, index) => {
e.stopPropagation();
dataSource.value.splice(index, 1);
deleteErrorCard(item);
if (item.id === selectCardInfo.value.id) {
if (dataSource.value.length) {
const _target = dataSource.value[0] ?? {};
selectCardInfo.value = _target;
} else {
selectCardInfo.value = {};
}
}
};
const renderFooterRow = () => {
if (dataSource.value.length > 1) {
return (
<>
<Button size="medium" type="outline" onClick={onCancel} class="mr-12px">
取消上传
</Button>
<Button type="primary" size="medium" onClick={onSubmit} loading={uploadLoading.value}>
{uploadLoading.value ? '批量上传' : '批量上传'}
</Button>
</>
);
} else {
return (
<>
<Button size="medium" type="outline" onClick={onCancel} class="mr-12px">
取消上传
</Button>
<Button size="medium" type="outline" onClick={onSubmit} class="mr-12px">
上传
</Button>
<Button type="primary" size="medium" onClick={onSubmitAndCheck} loading={uploadLoading.value}>
上传并审核
</Button>
</>
);
}
};
const getData = () => {
dataSource.value = generateMockData();
selectCardInfo.value = dataSource.value[0] ?? {};
};
const getCardClass = (item) => {
if (selectCardInfo.value.id === item.id) {
return '!border-#6D4CFE !bg-#F0EDFF';
}
if (errorDataCards.value.find((v) => v.id === item.id)) {
return '!border-#F64B31';
}
return '';
};
onMounted(() => {
getData();
});
return () => (
<>
<div class="manuscript-upload-wrap bg-#fff rounded-8px border-1px border-#D7D7D9 border-solid flex">
<div class="left flex-1 overflow-y-auto p-24px">
<EditForm ref={formRef} v-model={selectCardInfo.value} onReValidate={handleReValidate} />
</div>
{dataSource.value.length > 1 && (
<div class="right pt-24px pb-12px flex flex-col">
<div class="title flex items-center px-24px">
<div class="w-3px h-16px bg-#6D4CFE rounded-2px mr-8px"></div>
<p class="cts bold !color-#211F24 !text-16px !lh-24px mr-8px">上传内容稿件列表</p>
<p class="cts">{`${dataSource.value.length}`}</p>
</div>
<div class="flex-1 px-24px overflow-y-auto pt-24px">
{dataSource.value.map((item, index) => (
<div
key={item.id}
id={`card-${item.id}`}
class={`group relative mb-12px px-8px py-12px flex flex-col rounded-8px bg-#F7F8FA border-1px border-solid border-transparent transition-all duration-300 cursor-pointer hover:bg-#E6E6E8 ${getCardClass(
item,
)}`}
onClick={() => onselect(item)}
>
<icon-close-circle-fill
size={16}
class="absolute top--8px right--8px color-#737478 hover:color-#211F24 hidden group-hover:block"
onClick={(e) => onDelete(e, item, index)}
/>
<TextOverTips
context={item.content}
line={1}
class={`cts !color-#211F24 mb-8px ${selectCardInfo.value.id === item.id ? 'bold' : ''}`}
/>
<div class="flex items-center justify-between">
<div class="flex items-center ">
<img
src={item.type === EnumManuscriptType.Image ? icon1 : icon2}
width="16"
height="16"
class="mr-4px"
/>
<span
class={`cts ${item.type === EnumManuscriptType.Image ? '!color-#25C883' : '!color-#6D4CFE'}`}
>
{item.type === EnumManuscriptType.Image ? '图文' : '视频'}
</span>
</div>
{errorDataCards.value.find((v) => v.id === item.id) && (
<p class="cts !color-#F64B31">必填项未填</p>
)}
</div>
</div>
))}
</div>
</div>
)}
</div>
<div class="flex justify-end items-center px-16px py-20px w-full bg-#fff footer-row">{renderFooterRow()}</div>
<CancelUploadModal ref={cancelUploadModal} />
<UploadSuccessModal ref={uploadSuccessModal} />
</>
);
},
};
</script>
<style lang="scss" scoped>
@import './style.scss';
</style>

View File

@ -0,0 +1,28 @@
$footer-height: 68px;
.manuscript-upload-wrap {
height: calc(100% - 72px);
.cts {
color: #939499;
font-family: $font-family-regular;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 22px;
&.bold {
font-family: $font-family-medium;
}
}
.right {
border-left: 1px solid var(--Border-2, #e6e6e8);
width: 320px;
flex-shrink: 0;
}
}
.footer-row {
position: fixed;
bottom: 0;
left: $sidebar-width;
width: calc(100% - $sidebar-width);
border-top: 1px solid #e6e6e8;
height: $footer-height;
}

View File

@ -0,0 +1,63 @@
<template>
<a-modal v-model:visible="visible" title="提示" width="480px" @close="onClose" modal-class="upload-success11-modal">
<div class="flex items-center flex-col justify-center">
<img :src="icon1" width="80" height="80" class="mb-16px" />
<span class="text-18px lh-26px font-400 color-#211F24 md">上传成功</span>
<p class="text-14px lh-22px font-400 color-#737478 ld">为确保内容合规建议您立即进行审核</p>
<p class="text-14px lh-22px font-400 color-#737478 ld">检测是否存在违规内容</p>
</div>
<template #footer>
<a-button size="medium" @click="onBack">回到列表</a-button>
<a-button type="primary" class="ml-8px" size="medium" @click="onConfirm">批量审核</a-button>
</template>
</a-modal>
</template>
<script setup>
import { ref } from 'vue';
import icon1 from '@/assets/img/media-account/icon-feedback-success.png';
const router = useRouter();
const visible = ref(false);
const data = ref([]);
const onClose = () => {
data.value = [];
visible.value = false;
};
const onBack = () => {
onClose();
router.push({ name: 'ManuscriptList' });
};
const onConfirm = () => {
onClose();
console.log('跳转至「内容稿件审核」菜单tab进入批量审核稿件页面');
router.push({ name: 'ManuscriptCheck' });
};
const open = (dataSource) => {
data.value = dataSource;
visible.value = true;
};
defineExpose({ open });
</script>
<style lang="scss">
.upload-success11-modal {
.arco-modal-header {
border-bottom: none;
}
.md {
font-family: $font-family-medium;
}
.ld {
font-family: $font-family-regular;
}
.arco-modal-footer {
border-top: none;
justify-content: center;
}
}
</style>