feat: 内容稿件列表、整理文件目录结构

This commit is contained in:
rd
2025-07-29 12:00:20 +08:00
parent 6513fcf2ed
commit 4e10e1db04
29 changed files with 730 additions and 343 deletions

View File

@ -108,7 +108,6 @@ provide('toggleDrawerMenu', () => {
</template> </template>
<style scoped lang="scss"> <style scoped lang="scss">
$nav-size-height: 72px;
$layout-max-width: 1100px; $layout-max-width: 1100px;
.layout { .layout {
@ -121,7 +120,7 @@ $layout-max-width: 1100px;
left: 0; left: 0;
z-index: 100; z-index: 100;
width: 100%; width: 100%;
height: $nav-size-height; height: $navbar-height;
} }
.layout-sider { .layout-sider {
position: fixed; position: fixed;

View File

@ -8,6 +8,9 @@ const route = useRoute();
const routerKey = computed(() => { const routerKey = computed(() => {
return route.path + Math.random(); return route.path + Math.random();
}); });
const showFooter = computed(() => {
return !(route.meta && route.meta.hideFooter);
});
/*** - end */ /*** - end */
</script> </script>
@ -19,7 +22,7 @@ const routerKey = computed(() => {
<component :is="Component" :key="route.fullPath" /> <component :is="Component" :key="route.fullPath" />
</keep-alive> </keep-alive>
</transition> </transition>
<view class="footer"> <view class="footer" v-if="showFooter">
<view>闽公网安备 352018502850842 闽ICP备20250520582号 © 2025小题科技All Rights Reserved.</view> <view>闽公网安备 352018502850842 闽ICP备20250520582号 © 2025小题科技All Rights Reserved.</view>
<view>* 数据通过公开渠道获取灵机进行统计分析</view> <view>* 数据通过公开渠道获取灵机进行统计分析</view>
</view> </view>

View File

@ -12,7 +12,7 @@ const COMPONENTS: AppRouteRecordRaw[] = [
locale: '内容稿件', locale: '内容稿件',
icon: IconContentManuscript, icon: IconContentManuscript,
requiresAuth: false, requiresAuth: false,
requireLogin: true, requireLogin: false,
roles: ['*'], roles: ['*'],
id: MENU_GROUP_IDS.CREATIVE_GENERATION_WORKSHOP_ID, id: MENU_GROUP_IDS.CREATIVE_GENERATION_WORKSHOP_ID,
}, },
@ -23,21 +23,48 @@ const COMPONENTS: AppRouteRecordRaw[] = [
meta: { meta: {
locale: '内容稿件列表', locale: '内容稿件列表',
requiresAuth: false, requiresAuth: false,
requireLogin: true, requireLogin: false,
roles: ['*'], roles: ['*'],
}, },
component: () => import('@/views/creative-generation-workshop/manuscript/manuscript-list/index.vue'), component: () => import('@/views/creative-generation-workshop/manuscript/list/index.vue'),
}, },
{ {
path: 'check', path: 'edit/:id',
name: 'ManuscriptEdit',
meta: {
locale: '账号详情',
requiresAuth: false,
requireLogin: false,
hideFooter: true,
roles: ['*'],
hideInMenu: true,
activeMenu: 'ManuscriptList',
},
component: () => import('@/views/creative-generation-workshop/manuscript/edit/index.vue'),
},
{
path: 'detail/:id',
name: 'ManuscriptDetail',
meta: {
locale: '账号详情',
requiresAuth: false,
requireLogin: false,
roles: ['*'],
hideInMenu: true,
activeMenu: 'ManuscriptList',
},
component: () => import('@/views/creative-generation-workshop/manuscript/detail/index.vue'),
},
{
path: 'check-list',
name: 'ManuscriptCheck', name: 'ManuscriptCheck',
meta: { meta: {
locale: '内容稿件审核', locale: '内容稿件审核',
requiresAuth: false, requiresAuth: false,
requireLogin: true, requireLogin: false,
roles: ['*'], roles: ['*'],
}, },
component: () => import('@/views/creative-generation-workshop/manuscript/manuscript-check/index.vue'), component: () => import('@/views/creative-generation-workshop/manuscript/check/index.vue'),
}, },
], ],
}, },

View File

@ -14,6 +14,7 @@ declare module 'vue-router' {
noAffix?: boolean; // if set true, the tag will not affix in the tab-bar noAffix?: boolean; // if set true, the tag will not affix in the tab-bar
ignoreCache?: boolean; // if set true, the page will not be cached ignoreCache?: boolean; // if set true, the page will not be cached
hideSidebar?: boolean; hideSidebar?: boolean;
hideFooter?: boolean;
requireLogin?: boolean; // 是否需要登陆才能访问 requireLogin?: boolean; // 是否需要登陆才能访问
} }
} }

View File

@ -55,9 +55,9 @@ export const MENU_LIST = [
{ {
name: '内容稿件', name: '内容稿件',
routeName: 'ManuscriptList', routeName: 'ManuscriptList',
includeRouteNames: ['ManuscriptList', 'ManuscriptCheck'], includeRouteNames: ['ManuscriptList', 'ManuscriptCheck', 'ManuscriptEdit', 'ManuscriptDetail'],
} },
] ],
}, },
{ {
id: MENU_GROUP_IDS.PROPERTY_ID, id: MENU_GROUP_IDS.PROPERTY_ID,
@ -101,9 +101,7 @@ export const MENU_LIST = [
{ {
name: '项目管理', name: '项目管理',
routeName: 'ProjectList', routeName: 'ProjectList',
includeRouteNames: [ includeRouteNames: ['ProjectList'],
'ProjectList',
],
}, },
], ],
}, },

View File

@ -1,4 +1,7 @@
$navbar-height: 72px; // 头部高度
$sidebar-width: 220px; // 侧边栏菜单宽度
// 汉字字体 // 汉字字体
$font-family-regular: 'PingFangSC-Regular', 'Microsoft Yahei', Arial, sans-serif; $font-family-regular: 'PingFangSC-Regular', 'Microsoft Yahei', Arial, sans-serif;
$font-family-medium: 'PingFangSC-Medium', 'Microsoft Yahei', Arial, sans-serif; $font-family-medium: 'PingFangSC-Medium', 'Microsoft Yahei', Arial, sans-serif;

View File

@ -0,0 +1,5 @@
<template>
<div class="manuscript-detail-wrap">
<div class="bg-#fff rounded-8px border-1px border-#D7D7D9 border-solid mb-24px">详情</div>
</div>
</template>

View File

@ -0,0 +1,3 @@
.manuscript-edit-wrap {
}

View File

@ -0,0 +1,248 @@
<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: [],
};
export default {
setup(props, { emit, expose }) {
const formRef = ref(null);
const uploadRef = ref(null);
const form = ref(cloneDeep(INITIAL_FORM));
const projects = ref([]);
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;
}
};
onMounted(() => {
getProjects();
});
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>
</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>
</div>
</>
);
},
};
</script>
<style lang="scss" scoped>
@import './style.scss';
</style>

View File

@ -0,0 +1,61 @@
$footer-height: 68px;
.manuscript-edit-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;
}
}
.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;
bottom: 0;
left: $sidebar-width;
width: calc(100% - $sidebar-width);
border-top: 1px solid #e6e6e8;
height: $footer-height;
}

View File

@ -62,7 +62,7 @@ export const TABLE_COLUMNS = [
{ {
title: '操作', title: '操作',
dataIndex: 'operation', dataIndex: 'operation',
width: 160, width: 180,
fixed: 'right' fixed: 'right'
}, },
]; ];

View File

@ -56,10 +56,17 @@
</template> </template>
<template v-else-if="column.dataIndex === 'budget'" #cell="{ record }"> <template v-else-if="column.dataIndex === 'budget'" #cell="{ record }">
<div class="flex items-center"> <div class="flex items-center">
<img :src="record.budget === 1 ? icon2 : icon3" width="16" height="16" class="mr-4px" /> <img
<span class="cts" :class="record.budget === 1 ? '!color-#25C883' : '!color-#6D4CFE'">{{ :src="record.budget === EnumManuscriptType.Image ? icon2 : icon3"
record.budget === 1 ? '图文' : '视频' width="16"
}}</span> height="16"
class="mr-4px"
/>
<span
class="cts"
:class="record.budget === EnumManuscriptType.Image ? '!color-#25C883' : '!color-#6D4CFE'"
>{{ record.budget === EnumManuscriptType.Image ? '图文' : '视频' }}</span
>
</div> </div>
</template> </template>
<template v-else-if="['create_at', 'last_create_at'].includes(column.dataIndex)" #cell="{ record }"> <template v-else-if="['create_at', 'last_create_at'].includes(column.dataIndex)" #cell="{ record }">
@ -87,7 +94,7 @@
import { ref } from 'vue'; import { ref } from 'vue';
import { formatTableField, exactFormatTime } from '@/utils/tools'; import { formatTableField, exactFormatTime } from '@/utils/tools';
import { TABLE_COLUMNS } from './constants'; import { TABLE_COLUMNS } from './constants';
import { CHECK_STATUS } from '@/views/creative-generation-workshop/manuscript/manuscript-list/constants'; import { CHECK_STATUS, EnumManuscriptType } from '@/views/creative-generation-workshop/manuscript/list/constants';
import TextOverTips from '@/components/text-over-tips'; import TextOverTips from '@/components/text-over-tips';

View File

@ -0,0 +1,352 @@
<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 icon1 from '@/assets/img/media-account/icon-feedback-fail.png';
// 状态枚举
const TASK_STATUS = {
DEFAULT: 1,
LOADING: 2,
FAILED: 3,
SUCCESS: 4,
};
const UPLOAD_TYPE = {
LINK: 'link',
LOCAL: 'local',
HANDWRITE: 'handwrite',
};
// 初始表单数据
const INITIAL_FORM = {
linkUrl: '',
link: '',
};
// 模拟数据生成
const generateMockData = (count = 20) =>
Array.from({ length: count }, (_, i) => ({
id: `item-${i + 1}`,
content: '挖到宝了!这个平价好物让我素颜出门都自信✨挖到宝了!这个平价好物让我素颜出门都自信✨',
}));
export default {
setup(props, { emit, expose }) {
const update = inject('update');
const router = useRouter();
// 响应式状态
const visible = ref(false);
const formRef = ref(null);
const uploadType = ref(UPLOAD_TYPE.LINK);
const taskStatus = ref(TASK_STATUS.DEFAULT);
const form = ref(cloneDeep(INITIAL_FORM));
const uploadList = ref([]);
const totalCount = ref(0);
// 剪贴板功能
const { copy } = useClipboard({ source: form.value.link });
const isLink = computed(() => uploadType.value === UPLOAD_TYPE.LINK);
const isLocal = computed(() => uploadType.value === UPLOAD_TYPE.LOCAL);
const isHandwrite = computed(() => uploadType.value === UPLOAD_TYPE.HANDWRITE);
const isDefault = computed(() => taskStatus.value === TASK_STATUS.DEFAULT);
// 模态框标题
const getTitle = () => {
const titleMap = {
[TASK_STATUS.DEFAULT]: '上传内容稿件',
[TASK_STATUS.LOADING]: isLink.value ? '链接上传' : isLocal.value ? '本地批量上传' : '上传内容稿件',
[TASK_STATUS.FAILED]: isLink.value ? '链接上传' : isLocal.value ? '本地批量上传' : '上传内容稿件',
[TASK_STATUS.SUCCESS]: '上传内容稿件列表',
};
return titleMap[taskStatus.value];
};
// 重置状态
const reset = () => {
formRef.value?.resetFields?.();
uploadType.value = UPLOAD_TYPE.LINK;
taskStatus.value = TASK_STATUS.DEFAULT;
form.value = cloneDeep(INITIAL_FORM);
uploadList.value = [];
totalCount.value = 0;
};
// 打开模态框
const open = () => {
visible.value = true;
};
// 关闭模态框
const onClose = () => {
reset();
visible.value = false;
};
// 防抖提交
const debouncedSubmit = debounce(async () => {
if (isHandwrite.value) {
handleHandwriteSubmit();
return;
}
taskStatus.value = TASK_STATUS.LOADING;
// 模拟上传过程
setTimeout(() => {
taskStatus.value = TASK_STATUS.SUCCESS;
totalCount.value = 30;
uploadList.value = generateMockData();
}, 2000);
}, 300);
// 提交处理
const onSubmit = () => {
debouncedSubmit();
};
// 手写提交处理
const handleHandwriteSubmit = () => {
copy(form.value.link);
AMessage.success('复制成功!');
onClose();
};
// 取消上传
const onCancelUpload = () => {
taskStatus.value = TASK_STATUS.DEFAULT;
AMessage.info('已取消上传');
};
// 文件上传处理
const handleUpload = async (file) => {
console.log('handleUpload', file);
taskStatus.value = TASK_STATUS.LOADING;
setTimeout(() => {
taskStatus.value = TASK_STATUS.SUCCESS;
totalCount.value = 30;
uploadList.value = generateMockData();
}, 2000);
};
// 跳转编辑
const goEdit = () => {
router.push(`/manuscript/edit/1`);
onClose();
};
// 删除项目
const onDelete = (item) => {
console.log('onDelete', item);
uploadList.value = uploadList.value.filter((i) => i.id !== item.id);
totalCount.value = uploadList.value.length;
AMessage.success('删除成功');
};
// 下载模板
const handleDownloadTemplate = async () => {
AMessage.info('下载模板功能开发中...');
};
// 渲染链接上传表单
const renderLinkForm = () => (
<FormItem label="链接地址">
<Textarea
v-model={form.value.linkUrl}
size="large"
placeholder="请输入..."
autoSize={{ minRows: 5, maxRows: 8 }}
/>
</FormItem>
);
// 渲染手写上传表单
const renderHandwriteForm = () => (
<FormItem label="上传链接">
<Input v-model={form.value.link} placeholder="请输入..." size="large" />
</FormItem>
);
// 渲染本地上传表单
const renderLocalForm = () => (
<FormItem label="内容稿件">
<div class="flex flex-col w-full">
<Upload
action="/"
draggable
customRequest={handleUpload}
accept=".xlsx,.xls,.docx,.doc"
show-file-list={false}
>
{{
'upload-button': () => (
<div class="upload-box">
<icon-plus size="14" class="mb-16px" />
<span class="text mb-4px">点击或拖拽文件到此处上传</span>
<span class="tip">支持文档文本+, 视频批量上传</span>
</div>
),
}}
</Upload>
<div class="flex items-center cursor-pointer mt-8px" onClick={handleDownloadTemplate}>
<icon-download size="14" class="mr-4px !color-#6D4CFE" />
<span class="cts color-#6D4CFE">下载示例文档</span>
</div>
</div>
</FormItem>
);
// 渲染加载状态
const renderLoadingState = () => (
<div class="flex flex-col items-center justify-center rounded-8px bg-#F7F8FA h-208px">
<icon-loading size="48" class="!color-#6D4CFE mb-16px" />
<p class="tip !text-#768893">上传过程耗时可能较长请耐心等待</p>
<p class="tip !text-#768893">刷新页面将会终止本次数据的上传请谨慎操作</p>
</div>
);
// 渲染失败状态
const renderFailedState = () => (
<div class="flex flex-col items-center justify-center rounded-8px bg-#F7F8FA h-208px">
<img src={icon1} class="w-80px h-80px mb-16px" />
<p class="text mb-4px">上传失败</p>
<p class="tip !text-#768893">可能是网络不稳定导致请检查网络后重试</p>
</div>
);
// 渲染成功状态
const renderSuccessState = () => (
<div class="flex flex-col py-12px max-h-540px rounded-8px bg-#F7F8FA">
<span class="tip mb-8px px-12px fs-14px !text-left"> {totalCount.value} 个内容稿件</span>
<div class="flex-1 overflow-y-auto overflow-x-hidden px-12px">
{uploadList.value.map((item) => (
<div key={item.id} class="rounded-8px bg-#fff px-8px py-8px flex justify-between items-center mt-8px">
<div class="flex-1 overflow-hidden flex items-center mr-12px">
<img width="32" height="32" class="rounded-3px mr-8px" />
<TextOverTips class="text" context={item.content} />
</div>
<icon-delete
size="16px"
class="color-#737478 cursor-pointer hover:!color-#211F24"
onClick={() => onDelete(item)}
/>
</div>
))}
</div>
</div>
);
// 渲染表单内容
const renderFormContent = () => {
const contentMap = {
[TASK_STATUS.DEFAULT]: () => {
const formMap = {
[UPLOAD_TYPE.LINK]: renderLinkForm,
[UPLOAD_TYPE.HANDWRITE]: renderHandwriteForm,
[UPLOAD_TYPE.LOCAL]: renderLocalForm,
};
return formMap[uploadType.value]?.();
},
[TASK_STATUS.LOADING]: renderLoadingState,
[TASK_STATUS.FAILED]: renderFailedState,
[TASK_STATUS.SUCCESS]: renderSuccessState,
};
return contentMap[taskStatus.value]?.();
};
// 渲染底部按钮
const renderFooterButtons = () => {
const buttonMap = {
[TASK_STATUS.LOADING]: () => (
<Button type="primary" size="medium" onClick={onCancelUpload}>
取消上传
</Button>
),
[TASK_STATUS.DEFAULT]: () => (
<>
<Button size="medium" onClick={onClose}>
取消
</Button>
<Button type="primary" size="medium" onClick={onSubmit}>
{isHandwrite.value ? '复制链接' : '确认'}
</Button>
</>
),
[TASK_STATUS.FAILED]: () => (
<>
<Button size="medium" onClick={onClose}>
取消
</Button>
<Button type="primary" size="medium" onClick={onClose}>
重新上传
</Button>
</>
),
[TASK_STATUS.SUCCESS]: () => (
<>
<Button size="medium" onClick={onClose}>
取消
</Button>
<Button type="primary" size="medium" onClick={goEdit}>
确认
</Button>
</>
),
};
return buttonMap[taskStatus.value]?.();
};
expose({ open });
return () => (
<Modal
v-model:visible={visible.value}
title={getTitle()}
modal-class="upload-manuscript-modal"
width="500px"
mask-closable={false}
unmount-on-close
onClose={onClose}
footer={!(isDefault.value && isLocal.value)}
v-slots={{
footer: () => renderFooterButtons(),
}}
>
<Form ref="formRef" model={form.value} layout="horizontal" auto-label-width>
{isDefault.value && (
<FormItem label="上传方式">
<RadioGroup v-model={uploadType.value}>
<Radio value={UPLOAD_TYPE.LINK}>链接上传</Radio>
<Radio value={UPLOAD_TYPE.LOCAL}>本地上传</Radio>
<Radio value={UPLOAD_TYPE.HANDWRITE}>手写上传</Radio>
</RadioGroup>
</FormItem>
)}
{renderFormContent()}
</Form>
</Modal>
);
},
};
</script>
<style lang="scss">
@import './style.scss';
</style>

View File

@ -58,7 +58,7 @@ import { getProjects } from '@/api/all/propertyMarketing';
import { import {
INITIAL_QUERY, INITIAL_QUERY,
EnumCheckStatus, EnumCheckStatus,
} from '@/views/creative-generation-workshop/manuscript/manuscript-list/constants.ts'; } from '@/views/creative-generation-workshop/manuscript/list/constants.ts';
const { dataSource, pageInfo, onPageChange, onPageSizeChange, resetPageInfo } = useTableSelectionWithPagination({ const { dataSource, pageInfo, onPageChange, onPageSizeChange, resetPageInfo } = useTableSelectionWithPagination({
onPageChange: () => { onPageChange: () => {

View File

@ -1,320 +0,0 @@
<script lang="jsx">
import {
Modal,
Form,
FormItem,
Input,
RadioGroup,
Radio,
Upload,
Button,
Tooltip,
Message as AMessage,
Textarea,
} from '@arco-design/web-vue';
import { useClipboard } from '@vueuse/core';
import TextOverTips from '@/components/text-over-tips';
import icon1 from '@/assets/img/media-account/icon-feedback-fail.png';
const INITIAL_FORM = {
linkUrl: '',
link: '',
};
const FORM_TASK_STATUS = {
default: 1, // 默认状态,
importLoading: 2, // 上传中
importFailed: 3, // 上传失败
importSuccess: 4, // 上传成功
};
export default {
setup(props, { emit, expose }) {
const update = inject('update');
const visible = ref(false);
const formRef = ref(null);
const uploadRef = ref(null);
const uploadType = ref('link');
const taskStatus = ref(FORM_TASK_STATUS.default);
const form = ref(cloneDeep(INITIAL_FORM));
const { copy, copied, isSupported } = useClipboard({ source: form.value.link });
const isLink = computed(() => uploadType.value === 'link'); // 链接上传
const isLocal = computed(() => uploadType.value === 'local'); // 本地上传
const isHandwrite = computed(() => uploadType.value === 'handwrite'); // 手写上传
const isDefaultTask = computed(() => taskStatus.value === FORM_TASK_STATUS.default);
const isImportLoadingTask = computed(() => taskStatus.value === FORM_TASK_STATUS.importLoading);
const isImportFailedTask = computed(() => taskStatus.value === FORM_TASK_STATUS.importFailed);
const isImportSuccessTask = computed(() => taskStatus.value === FORM_TASK_STATUS.importSuccess);
const open = () => {
visible.value = true;
};
const reset = () => {
formRef.value?.resetFields?.();
uploadType.value = 'link';
taskStatus.value = FORM_TASK_STATUS.default;
form.value = cloneDeep(INITIAL_FORM);
};
const onClose = () => {
reset();
visible.value = false;
};
const onSubmit = async () => {
if (isHandwrite.value) {
if (!isSupported) {
AMessage.error('您的浏览器不支持复制,请手动复制!');
}
copy(form.value.link);
if (!copied) {
AMessage.error('复制失败,请手动复制!');
}
AMessage.success('复制成功!');
onClose();
return;
}
taskStatus.value = FORM_TASK_STATUS.importLoading;
setTimeout(() => {
taskStatus.value = FORM_TASK_STATUS.importSuccess;
}, 2000);
};
const onCancelUpload = () => {
console.log('onCancelUpload');
taskStatus.value = FORM_TASK_STATUS.default;
// onClose()
};
const handleUpload = async (file) => {
console.log('handleUpload11111', file);
taskStatus.value = FORM_TASK_STATUS.importLoading;
setTimeout(() => {
taskStatus.value = FORM_TASK_STATUS.importSuccess;
}, 2000);
};
const goEdit = () => {
console.log('goEdit 去稿件编辑页面');
};
const onDelete = (item) => {
console.log('onDelete', item);
};
const handleDownloadTemplate = async () => {
// const { code, data } = await getPlacementAccountsTemplateUrl();
// if (code === 200) {
// window.open(data.download_url, '_blank');
// }
};
const getLink = () => {
return (
<FormItem label="链接地址">
<Textarea
v-model={form.value.linkUrl}
size="large"
placeholder="请输入..."
autoSize={{ minRows: 5, maxRows: 8 }}
/>
</FormItem>
);
};
const getHandwrite = () => {
return (
<FormItem label="上传链接">
<Input v-model={form.value.link} placeholder="请输入..." size="large" />
</FormItem>
);
};
const getLocal = () => {
return (
<FormItem label="内容稿件">
<div class="flex flex-col w-full">
<Upload
ref="uploadRef"
action="/"
draggable
customRequest={handleUpload}
accept=".xlsx,.xls,.docx,.doc"
show-file-list={false}
>
{{
'upload-button': () => (
<div class="upload-box">
<icon-plus size="14" class="mb-16px" />
<span class="text mb-4px">点击或拖拽文件到此处上传</span>
<span class="tip">支持文档文本+, 视频批量上传</span>
</div>
),
}}
</Upload>
<div class="flex items-center cursor-pointer mt-8px" onClick={handleDownloadTemplate}>
<icon-download size="14" class="mr-4px !color-#6D4CFE" />
<span class="cts color-#6D4CFE">下载示例文档</span>
</div>
</div>
</FormItem>
);
};
const renderFormContent = () => {
if (isDefaultTask.value) {
if (isLink.value) {
return getLink();
}
if (isHandwrite.value) {
return getHandwrite();
}
if (isLocal.value) {
return getLocal();
}
}
if (isImportLoadingTask.value) {
return (
<div class="flex flex-col items-center justify-center rounded-8px bg-#F7F8FA h-208px">
<icon-loading size="48" class="!color-#6D4CFE mb-16px" />
<p class="tip !text-#768893">上传过程耗时可能较长请耐心等待</p>
<p class="tip !text-#768893">刷新页面将会终止本次数据的上传请谨慎操作</p>
</div>
);
}
if (isImportFailedTask.value) {
return (
<div class="flex flex-col items-center justify-center rounded-8px bg-#F7F8FA h-208px">
<img src={icon1} class="w-80px h-80px mb-16px" />
<p class="text mb-4px">上传失败</p>
<p class="tip !text-#768893">可能是网络不稳定导致请检查网络后重试</p>
</div>
);
}
if (isImportSuccessTask.value) {
return (
<div class="flex flex-col py-12px max-h-540px rounded-8px bg-#F7F8FA">
<span class="tip mb-8px px-12px fs-14px !text-left"> 30 个内容稿件</span>
<div class="flex-1 overflow-y-auto overflow-x-hidden px-12px">
{new Array(20).fill(0).map((v) => (
<div class="rounded-8px bg-#fff px-8px py-8px flex justify-between items-center mt-8px">
<div class="flex-1 overflow-hidden flex items-center mr-12px">
<img width="32" height="32" class="rounded-3px mr-8px" />
<TextOverTips
class="text"
context="挖到宝了!这个平价好物让我素颜出门都自信✨挖到宝了!这个平价好物让我素颜出门都自信✨"
/>
</div>
<icon-delete
size="16px"
class="color-#737478 cursor-pointer hover:!color-#211F24"
onClick={onDelete}
/>
</div>
))}
</div>
</div>
);
}
};
const renderFooterBtn = () => {
if (isImportLoadingTask.value) {
return (
<Button type="primary" size="medium" onClick={onCancelUpload}>
取消上传
</Button>
);
}
const getConfirmBtn = () => {
if (isDefaultTask.value) {
return (
<Button type="primary" size="medium" onClick={onSubmit}>
{isHandwrite.value ? '复制链接' : '确认'}
</Button>
);
}
if (isImportFailedTask.value) {
return (
<Button type="primary" size="medium" onClick={onClose}>
重新上传
</Button>
);
}
if (isImportSuccessTask.value) {
return (
<Button type="primary" size="medium" onClick={goEdit}>
确认
</Button>
);
}
};
return (
<>
<Button size="medium" onClick={onClose}>
取消
</Button>
{getConfirmBtn()}
</>
);
};
const getTitle = () => {
if (isDefaultTask.value) {
return '上传内容稿件';
} else if (isImportSuccessTask.value) {
return '上传内容稿件列表';
} else {
if (isLink.value) {
return '链接上传';
}
if (isLocal.value) {
return '本地批量上传';
}
}
};
expose({ open });
return () => (
<Modal
v-model:visible={visible.value}
title={getTitle()}
modal-class="upload-manuscript-modal"
width="500px"
mask-closable={false}
unmount-on-close
onClose={onClose}
footer={!(isDefaultTask.value && isLocal.value)}
v-slots={{
footer: () => renderFooterBtn(),
}}
>
<Form ref="formRef" model={form.value} layout="horizontal" auto-label-width>
{isDefaultTask.value && (
<FormItem label="上传方式">
<RadioGroup v-model={uploadType.value}>
<Radio value="link">链接上传</Radio>
<Radio value="local">本地上传</Radio>
<Radio value="handwrite">手写上传</Radio>
</RadioGroup>
</FormItem>
)}
{renderFormContent()}
</Form>
</Modal>
);
},
};
</script>
<style lang="scss">
@import './style.scss';
</style>