feat: 内容稿件上传、编辑
This commit is contained in:
@ -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>
|
||||
Reference in New Issue
Block a user