feat: 内容稿件审核

This commit is contained in:
rd
2025-08-05 17:37:11 +08:00
parent da68733cb3
commit 07771f08bf
10 changed files with 455 additions and 33 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 B

View File

@ -3,22 +3,20 @@
<template #content>
<div :style="contentStyle" class="tip-content">{{ props.context }}</div>
</template>
<div class="overflow-hidden">
<div v-bind="$attrs" ref="Text" :class="`${isShow ? '' : `line-${props.line}`} `" class="overflow-text">
{{ props.context }}
</div>
<div
v-if="isShowBtn && !disabled"
class="color-#8C8C8C flex items-center cursor-pointer mt-2px"
@click="
() => {
isShow = !isShow;
}
"
>
{{ isShow ? '收起' : '展开' }}
<icon-up size="16" :class="{ active: isShow }" class="ml-2px color-#8C8C8C" />
</div>
<div v-bind="$attrs" ref="Text" :class="`${isShow ? '' : `line-${props.line}`} `" class="overflow-text">
{{ props.context }}
</div>
<div
v-if="isShowBtn && !disabled"
class="color-#8C8C8C flex items-center cursor-pointer mt-2px"
@click="
() => {
isShow = !isShow;
}
"
>
{{ isShow ? '收起' : '展开' }}
<icon-up size="16" :class="{ active: isShow }" class="ml-2px color-#8C8C8C" />
</div>
</a-tooltip>
</template>

View File

@ -1,5 +1,3 @@
export const TABLE_COLUMNS1 = [
{
title: '序号',
@ -182,7 +180,7 @@ export const TABLE_COLUMNS3 = [
sortDirections: ['ascend', 'descend'],
},
},
{
{
title: '修改人员',
dataIndex: 'last_modifier',
width: 180,
@ -216,7 +214,7 @@ export const AUDIT_STATUS_LIST = [
value: AuditStatus.Passed,
tableColumns: TABLE_COLUMNS3,
},
]
];
export const INITIAL_QUERY = {
audit_status: AuditStatus.Pending,
@ -232,16 +230,16 @@ import icon1 from '@/assets/img/media-account/icon-dy.png';
import icon2 from '@/assets/img/media-account/icon-xhs.png';
export const PLATFORMS = [
{
label: '抖音',
value: 2,
icon: icon1,
},
{
label: '小红书',
value: 1,
icon: icon2,
},
{
label: '抖音',
value: 2,
icon: icon1,
},
];
export const CUSTOMER_OPINION = [
@ -249,12 +247,12 @@ export const CUSTOMER_OPINION = [
label: '待确认',
value: 0,
bg: '#F2F3F5',
color: 'color-#3C4043'
color: 'color-#3C4043',
},
{
label: '已确认',
value: 1,
bg: '#F0EDFF',
color: '!color-#6D4CFE'
color: '!color-#6D4CFE',
},
];

View File

@ -0,0 +1,57 @@
<template>
<a-modal
v-model:visible="visible"
:title="action === 'exit' ? '退出审核' : '切换内容稿件'"
width="480px"
@close="onClose"
>
<div class="flex items-center">
<img :src="icon1" width="20" height="20" class="mr-12px" />
<span>{{
action === 'exit'
? '内容已修改尚未保存,若退出编辑,本次修改将不保存。'
: '当前内容已修改尚未保存,若切换内容稿件,本次修改将不保存。'
}}</span>
</div>
<template #footer>
<a-button size="medium" @click="onClose">继续编辑</a-button>
<a-button type="primary" class="ml-8px" size="medium" @click="onConfirm">
{{ action === 'exit' ? '确认退出' : '确认切换' }}
</a-button>
</template>
</a-modal>
</template>
<script setup>
import { ref } from 'vue';
import icon1 from '@/assets/img/media-account/icon-warn-1.png';
import { useRouter } from 'vue-router';
const emit = defineEmits(['selectCard']);
const router = useRouter();
const visible = ref(false);
const action = ref('');
const cardInfo = ref(null);
const onClose = () => {
action.value = '';
cardInfo.value = null;
visible.value = false;
};
const onConfirm = () => {
if (action.value === 'exit') {
router.push({ name: 'ManuscriptCheckList' });
} else {
emit('selectCard', cardInfo.value);
}
onClose();
};
const open = (type = 'exit', card = null) => {
action.value = type;
cardInfo.value = card;
visible.value = true;
};
defineExpose({ open });
</script>

View File

@ -0,0 +1,61 @@
<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 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 workId = ref('');
const onClose = () => {
workId.value = '';
visible.value = false;
};
const onBack = () => {
onClose();
router.push({ name: 'ManuscriptList' });
};
const onConfirm = () => {
visible.value = false;
// router.push({ name: 'ManuscriptCheck', params: { id: workId.value } });
};
const open = (id) => {
workId.value = id;
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>

View File

@ -0,0 +1,20 @@
<script lang="jsx">
export default {
setup(props, { emit, expose }) {
return () => {
return (
<div class="content-wrap">
<div class="content-left">
<span class="cts bold !color-#1D2129">批量审核内容稿件</span>
</div>
</div>
);
};
},
};
</script>
<style lang="scss" scoped>
@import './style.scss';
</style>

View File

@ -0,0 +1,115 @@
<script lang="jsx">
import { Image } from '@arco-design/web-vue';
import { Swiper, SwiperSlide } from 'swiper/vue';
import TextOverTips from '@/components/text-over-tips';
import 'swiper/css';
import 'swiper/css/navigation';
import { Navigation } from 'swiper/modules';
import { PLATFORMS } from '@/views/creative-generation-workshop/manuscript/check-list/constants';
import icon1 from '@/assets/img/error-img.png';
import icon2 from '@/assets/img/creative-generation-workshop/icon-lf.png';
export default {
props: {
dataSource: {
type: Array,
default: () => [],
},
modelValue: {
type: Object,
default: {},
},
},
emits: ['update:modelValue', 'cardClick'],
setup(props, { emit, expose }) {
const selectedPlatform = ref(2);
const modules = [Navigation];
const handleCardClick = (item) => {
// emit('update:modelValue', item);
emit('cardClick', item);
};
return () => {
return (
<header class="header-wrap">
{props.dataSource.length > 1 && (
<div class="swiper-wrap pt-16px h-80px px-24px">
<Swiper
spaceBetween={16}
modules={modules}
navigation={{
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
}}
>
{props.dataSource.map((item) => (
<SwiperSlide
key={item.id}
onClick={() => handleCardClick(item)}
class={`swiper-item !h-64px !w-280px bg-#F7F8FA border-1px cursor-pointer border-solid border-transparent rounded-8px p-8px overflow-hidden !flex items-center ${
item.id === props.modelValue.id ? 'active' : ''
}`}
>
<Image
width={48}
height={48}
src={item.cover}
class="!rounded-4px mr-8px"
fit="cover"
v-slots={{
error: () => <img src={icon1} class="w-full h-full" />,
}}
/>
<div class="flex-1 overflow-hidden flex flex-col items-start">
<TextOverTips context={item.title} class={`cts !color-#211F24 title mb-4px`} />
<p class="cts">{`合规程度:${90}%`}</p>
</div>
</SwiperSlide>
))}
<div class="swiper-box swiper-button-prev">
<div class="swiper-button">
<img src={icon2} class="w-16px h-16px" />
</div>
</div>
<div class="swiper-box swiper-button-next">
<div class="swiper-button">
<img src={icon2} class="w-16px h-16px rotate-180" />
</div>
</div>
</Swiper>
</div>
)}
<div class="platform-row py-16px flex items-center px-24px">
<span class="mr-16px cts !color-#211F24">审核平台选择</span>
<div class="flex items-center">
{PLATFORMS.map((item) => (
<div
key={item.value}
onClick={() => {
selectedPlatform.value = item.value;
}}
class={`w-100px flex items-center mr-16px py-8px px-12px flex border-1px border-solid border-transparent transition-all
items-center rounded-8px cursor-pointer bg-#F2F3F5 hover:bg-#E6E6E8 ${
selectedPlatform.value === item.value ? '!bg-#F0EDFF !border-#6D4CFE' : ''
}`}
>
<img src={item.icon} alt="" width={20} height={20} class="mr-4px" />
<span class={`cts !color-#211F24 ${selectedPlatform.value === item.value ? 'bold' : ''}`}>
{item.label}
</span>
</div>
))}
</div>
</div>
</header>
);
};
},
};
</script>
<style lang="scss" scoped>
@import './style.scss';
</style>

View File

@ -0,0 +1,74 @@
.header-wrap {
.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;
}
}
.swiper-wrap {
.swiper-item {
transition: all;
&:hover {
background-color: #e6e6e8;
}
&.active {
background-color: #f0edff;
border-color: #6d4cfe;
:deep(.overflow-text) {
font-family: $font-family-medium !important;
}
}
}
.swiper-box {
width: 100px;
height: 64px;
position: absolute;
&.swiper-button-prev {
background: linear-gradient(270deg, rgba(255, 255, 255, 0) 0%, #fff 43.06%);
margin-top: 0 !important;
top: 0;
left: 0;
justify-content: flex-start;
padding-left: 8px;
}
&.swiper-button-next {
background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, #fff 43.06%);
margin-top: 0 !important;
top: 0;
right: 0;
justify-content: flex-end;
padding-right: 8px;
}
&::after {
display: none;
}
.swiper-button {
width: 40px;
height: 40px;
background-color: #fff;
display: flex;
justify-content: center;
align-items: center;
border-radius: 8px;
border: 1px solid var(--Border-1, #d7d7d9);
&:hover {
background-color: #f7f8fa;
}
&.click {
background-color: #f2f3f5;
}
}
&.swiper-button-disabled {
display: none;
}
}
}
.platform-row {
border-bottom: 1px solid var(--Border-2, #e6e6e8);
}
}

View File

@ -1,42 +1,141 @@
<script lang="jsx">
import { Button, Message as AMessage } from '@arco-design/web-vue';
import CancelCheckModal from './cancel-check-modal.vue';
import CheckSuccessModal from './check-success-modal.vue';
import HeaderCard from './components/header-card';
import ContentCard from './components/content-card';
import {
patchWorkAuditsAudit,
patchWorkAuditsBatchAudit,
putWorkAuditsUpdate,
putWorkAuditsAuditPass,
getWorkAuditsDetail,
} from '@/api/all/generationWorkshop.ts';
const generateMockData = (count = 20) =>
Array.from({ length: count }, (_, i) => ({
id: `${i + 1}`,
title: `挖到宝了!这个平价好物让我素颜出门都自信✨挖到宝了!这个平价好物让我素颜出门都自信✨${i + 1}`,
content: '挖到宝了!这个平价好物让我素颜出门都自信✨挖到宝了!这个平价好物让我素颜出门都自信✨',
platform: 1,
files: [],
}));
export default {
setup(props, { emit, expose }) {
const router = useRouter();
const route = useRoute();
const workId = ref(route.params.id);
const selectCardInfo = ref({});
const isSaved = ref(false);
const dataSource = ref([]);
const remoteDataSource = ref([]);
const cancelCheckModalRef = ref(null);
const checkSuccessModalRef = ref(null);
const checkLoading = ref(false);
const onBack = () => {
router.push({ name: 'ManuscriptCheckList' });
};
const onChangeCard = (item) => {
isSaved.value = false;
checkLoading.value = false;
selectCardInfo.value = cloneDeep(item);
};
const onCardClick = async (item) => {
const isModified = await isSelectCardModified();
if (isModified) {
cancelCheckModalRef.value?.open('toggle', item);
} else {
onChangeCard(item);
}
};
const getData = () => {
dataSource.value = generateMockData();
selectCardInfo.value = cloneDeep(dataSource.value[0] ?? {});
};
const isSelectCardModified = () => {
return new Promise((resolve) => {
const _a = dataSource.value.find((item) => item.id === selectCardInfo.value);
const _b = remoteDataSource.value.find((item) => item.id === selectCardInfo.value);
resolve(!isEqual(_a, _b) && !isSaved.value);
});
};
const onExit = async () => {
const isModified = await isSelectCardModified();
if (isModified) {
cancelCheckModalRef.value?.open();
} else {
onBack();
}
};
const onSave = async () => {
const { code, data } = await putWorkAuditsUpdate(selectCardInfo.value);
if (code === 200) {
isSaved.value = true;
AMessage.success('当前内容稿件已保存');
}
};
const onSubmit = async () => {
try {
checkLoading.value = true;
const { code, data } = await putWorkAuditsAuditPass(selectCardInfo.value);
if (code === 200) {
checkSuccessModalRef.value?.open(selectCardInfo.value.id);
}
} finally {
checkLoading.value = false;
}
};
const renderFooterRow = () => {
return (
<>
<Button size="medium" type="outline" class="mr-12px">
<Button size="medium" type="outline" class="mr-12px" onClick={onExit}>
退出
</Button>
<Button size="medium" type="outline" class="mr-12px">
<Button size="medium" type="outline" class="mr-12px" onClick={onSave}>
保存
</Button>
<Button type="primary" size="medium">
通过审核
<Button type="primary" size="medium" onClick={onSubmit} loading={checkLoading.value}>
{checkLoading.value ? '通过审核中...' : '通过审核'}
</Button>
</>
);
};
onMounted(() => {
getData();
});
return () => (
<>
<div class="manuscript-check-wrap">
<div class="flex items-center mb-8px">
<span class="cts color-#4E5969 cursor-pointer" onClick={onBack}>
<span class="cts color-#4E5969 cursor-pointer" onClick={onExit}>
内容稿件审核
</span>
<icon-oblique-line size="12" class="color-#C9CDD4 mx-4px" />
<span class="cts bold !color-#1D2129">批量审核内容稿件</span>
</div>
<div class="flex-1 flex flex-col overflow-hidden bg-#fff rounded-8px border-1px border-#D7D7D9 border-solid">
<HeaderCard dataSource={dataSource.value} v-model={selectCardInfo.value} onCardClick={onCardClick} />
<section class="flex-1">
<ContentCard />
</section>
</div>
</div>
<div class="flex justify-end items-center px-16px py-20px w-full bg-#fff footer-row">{renderFooterRow()}</div>
<footer class="flex justify-end items-center px-16px py-20px w-full bg-#fff footer-row">
{renderFooterRow()}
</footer>
<CancelCheckModal ref={cancelCheckModalRef} onSelectCard={onChangeCard} />
<CheckSuccessModal ref={checkSuccessModalRef} />
</>
);
},