feat: 内容稿件上传、编辑
This commit is contained in:
BIN
src/assets/img/creative-generation-workshop/icon-close.png
Normal file
BIN
src/assets/img/creative-generation-workshop/icon-close.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 604 B |
@ -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,
|
||||
|
||||
@ -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: ['*'],
|
||||
|
||||
@ -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>
|
||||
@ -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>
|
||||
@ -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>
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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) => {
|
||||
|
||||
@ -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>
|
||||
</>
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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>
|
||||
@ -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>
|
||||
@ -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;
|
||||
}
|
||||
@ -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>
|
||||
Reference in New Issue
Block a user