feat: 内容稿件审核
This commit is contained in:
BIN
src/assets/img/creative-generation-workshop/icon-lf2.png
Normal file
BIN
src/assets/img/creative-generation-workshop/icon-lf2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 234 B |
@ -1,7 +1,9 @@
|
|||||||
<script lang="jsx">
|
<script lang="jsx">
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import { Swiper, SwiperSlide } from 'swiper/vue';
|
||||||
import { IconLoading } from '@arco-design/web-vue/es/icon';
|
import { IconLoading } from '@arco-design/web-vue/es/icon';
|
||||||
import {
|
import {
|
||||||
|
Image,
|
||||||
Form,
|
Form,
|
||||||
FormItem,
|
FormItem,
|
||||||
Input,
|
Input,
|
||||||
@ -15,11 +17,17 @@ import {
|
|||||||
} from '@arco-design/web-vue';
|
} from '@arco-design/web-vue';
|
||||||
import TextOverTips from '@/components/text-over-tips';
|
import TextOverTips from '@/components/text-over-tips';
|
||||||
|
|
||||||
|
import 'swiper/css';
|
||||||
|
import 'swiper/css/navigation';
|
||||||
|
import { Navigation } from 'swiper/modules';
|
||||||
import { FORM_RULES, enumTab, TAB_LIST, RESULT_LIST } from './constants';
|
import { FORM_RULES, enumTab, TAB_LIST, RESULT_LIST } from './constants';
|
||||||
|
import { getImagePreSignedUrl } from '@/api/all/common';
|
||||||
|
|
||||||
import icon1 from '@/assets/img/creative-generation-workshop/icon-magic.png';
|
import icon1 from '@/assets/img/creative-generation-workshop/icon-magic.png';
|
||||||
import icon2 from '@/assets/img/creative-generation-workshop/icon-line.png';
|
import icon2 from '@/assets/img/creative-generation-workshop/icon-line.png';
|
||||||
import icon3 from '@/assets/img/creative-generation-workshop/icon-success.png';
|
import icon3 from '@/assets/img/creative-generation-workshop/icon-success.png';
|
||||||
|
import icon4 from '@/assets/img/error-img.png';
|
||||||
|
import icon5 from '@/assets/img/creative-generation-workshop/icon-lf2.png';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
@ -31,16 +39,24 @@ export default {
|
|||||||
type: Object,
|
type: Object,
|
||||||
default: {},
|
default: {},
|
||||||
},
|
},
|
||||||
|
selectedImageInfo: {
|
||||||
|
type: Object,
|
||||||
|
default: {},
|
||||||
|
},
|
||||||
checkLoading: {
|
checkLoading: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'filesChange', 'againCheck'],
|
emits: ['update:modelValue', 'filesChange', 'againCheck', 'imageClick'],
|
||||||
setup(props, { emit, expose }) {
|
setup(props, { emit, expose }) {
|
||||||
const activeTab = ref(enumTab.TEXT);
|
const activeTab = ref(enumTab.TEXT);
|
||||||
const aiCheckLoading = ref(false);
|
const aiCheckLoading = ref(false);
|
||||||
const formRef = ref(null);
|
const formRef = ref(null);
|
||||||
|
const uploadRef = ref(null);
|
||||||
|
const modules = [Navigation];
|
||||||
|
|
||||||
|
const isTextTab = computed(() => activeTab.value === enumTab.TEXT);
|
||||||
|
|
||||||
const onAiCheck = () => {
|
const onAiCheck = () => {
|
||||||
if (aiCheckLoading.value) return;
|
if (aiCheckLoading.value) return;
|
||||||
@ -51,7 +67,18 @@ export default {
|
|||||||
}, 2000);
|
}, 2000);
|
||||||
};
|
};
|
||||||
const onAgainCheck = () => {
|
const onAgainCheck = () => {
|
||||||
emit('againCheck');
|
if (isTextTab.value) {
|
||||||
|
emit('againCheck');
|
||||||
|
} else {
|
||||||
|
if (!props.modelValue.files?.length) {
|
||||||
|
AMessage.warning('请先上传需审核图片');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit('againCheck');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const onReplaceImage = () => {
|
||||||
|
uploadRef.value?.upload?.();
|
||||||
};
|
};
|
||||||
const handleTabClick = (key) => {
|
const handleTabClick = (key) => {
|
||||||
activeTab.value = key;
|
activeTab.value = key;
|
||||||
@ -71,6 +98,62 @@ export default {
|
|||||||
formRef.value?.resetFields?.();
|
formRef.value?.resetFields?.();
|
||||||
formRef.value?.clearValidate?.();
|
formRef.value?.clearValidate?.();
|
||||||
};
|
};
|
||||||
|
const getFileExtension = (filename) => {
|
||||||
|
const match = filename.match(/\.([^.]+)$/);
|
||||||
|
return match ? match[1].toLowerCase() : '';
|
||||||
|
};
|
||||||
|
const handleSelectImage = (item) => {
|
||||||
|
emit('imageClick', item);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderUpload = (UploadBtn) => {
|
||||||
|
return (
|
||||||
|
<Upload
|
||||||
|
ref={uploadRef}
|
||||||
|
action="/"
|
||||||
|
draggable
|
||||||
|
class="w-fit"
|
||||||
|
custom-request={uploadImage}
|
||||||
|
accept=".jpg,.jpeg,.png,.gif,.webp"
|
||||||
|
show-file-list={false}
|
||||||
|
multiple
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
'upload-button': () => <UploadBtn />,
|
||||||
|
}}
|
||||||
|
</Upload>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const uploadImage = async (option) => {
|
||||||
|
const {
|
||||||
|
fileItem: { file },
|
||||||
|
} = option;
|
||||||
|
|
||||||
|
// 验证文件数量
|
||||||
|
if (formData.value.files?.length >= 18) {
|
||||||
|
AMessage.error('最多只能上传18张图片!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { name, size, type } = file;
|
||||||
|
const response = await getImagePreSignedUrl({ suffix: getFileExtension(name) });
|
||||||
|
const { file_name, upload_url, file_url } = response?.data;
|
||||||
|
|
||||||
|
const blob = new Blob([file], { type });
|
||||||
|
await axios.put(upload_url, blob, {
|
||||||
|
headers: { 'Content-Type': type },
|
||||||
|
});
|
||||||
|
|
||||||
|
const _files = [
|
||||||
|
...props.modelValue.files,
|
||||||
|
{
|
||||||
|
url: file_url,
|
||||||
|
name: file_name,
|
||||||
|
size,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
emits('filesChange', _files);
|
||||||
|
};
|
||||||
|
|
||||||
const renderFooterRow = () => {
|
const renderFooterRow = () => {
|
||||||
return (
|
return (
|
||||||
@ -78,7 +161,7 @@ export default {
|
|||||||
<Button class="mr-12px" size="medium" onClick={onAgainCheck}>
|
<Button class="mr-12px" size="medium" onClick={onAgainCheck}>
|
||||||
再次审核
|
再次审核
|
||||||
</Button>
|
</Button>
|
||||||
{activeTab.value === enumTab.TEXT ? (
|
{isTextTab.value ? (
|
||||||
<Button size="medium" type="outline" class="w-123px" onClick={onAiCheck}>
|
<Button size="medium" type="outline" class="w-123px" onClick={onAiCheck}>
|
||||||
{aiCheckLoading.value ? (
|
{aiCheckLoading.value ? (
|
||||||
<>
|
<>
|
||||||
@ -93,39 +176,122 @@ export default {
|
|||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<Button size="medium" type="outline">
|
<div class="w-88px">
|
||||||
图片替换
|
{renderUpload(
|
||||||
</Button>
|
<Button size="medium" type="outline">
|
||||||
|
图片替换
|
||||||
|
</Button>,
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
const renderLeftBoxContent = () => {
|
const renderTextForm = () => {
|
||||||
if (activeTab.value === enumTab.TEXT) {
|
return (
|
||||||
|
<Form ref={formRef} model={props.modelValue} rules={FORM_RULES} layout="vertical" auto-label-width>
|
||||||
|
<FormItem label="标题" field="title" required>
|
||||||
|
<Input
|
||||||
|
v-model={props.modelValue.title}
|
||||||
|
placeholder="请输入标题"
|
||||||
|
size="large"
|
||||||
|
maxLength={30}
|
||||||
|
show-word-limit
|
||||||
|
disabled={props.checkLoading}
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem label="作品描述" field="content" class="flex-1 content-form-item">
|
||||||
|
<Textarea
|
||||||
|
v-model={props.modelValue.content}
|
||||||
|
placeholder="请输入作品描述"
|
||||||
|
size="large"
|
||||||
|
show-word-limit
|
||||||
|
maxLength={1000}
|
||||||
|
show-word-limit
|
||||||
|
disabled={props.checkLoading}
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const renderImageForm = () => {
|
||||||
|
if (props.modelValue.files?.length > 0) {
|
||||||
return (
|
return (
|
||||||
<Form ref={formRef} model={props.modelValue} rules={FORM_RULES} layout="vertical" auto-label-width>
|
<div class="w-full h-full py-16px flex justify-center">
|
||||||
<FormItem label="标题" field="title" required>
|
<div class="w-380px flex flex-col justify-center">
|
||||||
<Input
|
<Image
|
||||||
v-model={props.modelValue.title}
|
src={props.selectedImageInfo.url}
|
||||||
placeholder="请输入标题"
|
width={370}
|
||||||
size="large"
|
height={370}
|
||||||
maxLength={30}
|
class="flex items-center justify-center mb-16px"
|
||||||
show-word-limit
|
fit="contain"
|
||||||
disabled={props.checkLoading}
|
v-slots={{
|
||||||
|
error: () => <img src={icon4} class="w-full h-full" />,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
|
||||||
<FormItem label="作品描述" field="content" class="flex-1 content-form-item">
|
<div class="swiper-wrap h-60px">
|
||||||
<Textarea
|
<Swiper
|
||||||
v-model={props.modelValue.content}
|
spaceBetween={16}
|
||||||
placeholder="请输入作品描述"
|
modules={modules}
|
||||||
size="large"
|
slidesPerView="auto"
|
||||||
show-word-limit
|
navigation={{
|
||||||
maxLength={1000}
|
nextEl: '.swiper-button-next',
|
||||||
show-word-limit
|
prevEl: '.swiper-button-prev',
|
||||||
disabled={props.checkLoading}
|
}}
|
||||||
/>
|
>
|
||||||
</FormItem>
|
{props.modelValue.files.map((item) => (
|
||||||
</Form>
|
<SwiperSlide
|
||||||
|
key={item.id}
|
||||||
|
onClick={() => handleSelectImage(item)}
|
||||||
|
class={`swiper-item !h-48px !w-48px bg-#F7F8FA cursor-pointer rounded-4px overflow-hidden !flex items-center ${
|
||||||
|
item.id === props.selectedImageInfo.id ? 'active' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div class="group relative">
|
||||||
|
<Image
|
||||||
|
width={48}
|
||||||
|
height={48}
|
||||||
|
src={item.url}
|
||||||
|
class="!rounded-4px"
|
||||||
|
fit="cover"
|
||||||
|
preview={false}
|
||||||
|
v-slots={{
|
||||||
|
error: () => <img src={icon4} class="w-full h-full" />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<icon-close-circle-fill
|
||||||
|
size={16}
|
||||||
|
class="absolute top--8px right--8px hidden cursor-pointer color-#939499 group-hover:block z-50"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</SwiperSlide>
|
||||||
|
))}
|
||||||
|
<div class="swiper-box swiper-button-prev">
|
||||||
|
<img src={icon5} class="w-8px h-17px" />
|
||||||
|
</div>
|
||||||
|
<div class="swiper-box swiper-button-next">
|
||||||
|
<img src={icon5} class="w-8px h-17px rotate-180" />
|
||||||
|
</div>
|
||||||
|
</Swiper>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<div class="w-full h-full flex flex-col items-center justify-center">
|
||||||
|
<div class="flex justify-center mb-16px">
|
||||||
|
{renderUpload(
|
||||||
|
<div class="upload-box">
|
||||||
|
<icon-plus size="14" class="mb-16px color-#3C4043" />
|
||||||
|
<span class="cts !color-#211F24">上传图片</span>
|
||||||
|
</div>,
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span class="cts">上传要审核的图片素材</span>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -151,9 +317,7 @@ export default {
|
|||||||
<div class="flex flex-col items-center h-138px justify-center">
|
<div class="flex flex-col items-center h-138px justify-center">
|
||||||
<img src={icon3} width={72} height={72} class="mb-12px" />
|
<img src={icon3} width={72} height={72} class="mb-12px" />
|
||||||
<span class="cts !color-#25C883">
|
<span class="cts !color-#25C883">
|
||||||
{
|
{isTextTab.value ? '恭喜,您的文案中没有检测出违禁词' : '恭喜,您的图片中没有检测出违禁内容'}
|
||||||
activeTab.value === enumTab.TEXT ? '恭喜,您的文案中没有检测出违禁词' : '恭喜,您的图片中没有检测出违禁内容'
|
|
||||||
}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -256,7 +420,7 @@ export default {
|
|||||||
<TabPane key={item.value} title={item.label}></TabPane>
|
<TabPane key={item.value} title={item.label}></TabPane>
|
||||||
))}
|
))}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
<div class="flex-1 px-16px">{renderLeftBoxContent()}</div>
|
<div class="flex-1 px-16px">{isTextTab.value ? renderTextForm() : renderImageForm()}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-end">{renderFooterRow()}</div>
|
<div class="flex items-center justify-end">{renderFooterRow()}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -21,7 +21,7 @@
|
|||||||
.arco-tabs-nav {
|
.arco-tabs-nav {
|
||||||
.arco-tabs-tab {
|
.arco-tabs-tab {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
// padding: 0 8px;
|
// padding: 0 8px;
|
||||||
margin: 0 16px;
|
margin: 0 16px;
|
||||||
}
|
}
|
||||||
&::before {
|
&::before {
|
||||||
@ -58,6 +58,62 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.swiper-wrap {
|
||||||
|
.swiper-item {
|
||||||
|
transition: all;
|
||||||
|
&.active {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border: 2px solid var(--Brand-6, #6d4cfe);
|
||||||
|
background: url(<path-to-image>) lightgray 50% / cover no-repeat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.swiper-box {
|
||||||
|
position: absolute;
|
||||||
|
margin-top: 0 !important;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(0, 0, 0, 0.4);
|
||||||
|
transition: all;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
&:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.6);
|
||||||
|
}
|
||||||
|
&.swiper-button-prev {
|
||||||
|
left: 16px;
|
||||||
|
}
|
||||||
|
&.swiper-button-next {
|
||||||
|
right: 16px;
|
||||||
|
}
|
||||||
|
&::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
&.swiper-button-disabled {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.right-box {
|
.right-box {
|
||||||
.s1 {
|
.s1 {
|
||||||
|
|||||||
@ -21,7 +21,6 @@ export default {
|
|||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
const workIds = ref([]);
|
const workIds = ref([]);
|
||||||
const selectCardInfo = ref({});
|
|
||||||
const isSaved = ref(false);
|
const isSaved = ref(false);
|
||||||
const dataSource = ref([]);
|
const dataSource = ref([]);
|
||||||
const remoteDataSource = ref([]);
|
const remoteDataSource = ref([]);
|
||||||
@ -32,6 +31,9 @@ export default {
|
|||||||
const checkLoading = ref(false);
|
const checkLoading = ref(false);
|
||||||
const checkResult = ref({});
|
const checkResult = ref({});
|
||||||
|
|
||||||
|
const selectCardInfo = ref({});
|
||||||
|
const selectedImageInfo = ref(null);
|
||||||
|
|
||||||
const onBack = () => {
|
const onBack = () => {
|
||||||
router.push({ name: 'ManuscriptCheckList' });
|
router.push({ name: 'ManuscriptCheckList' });
|
||||||
};
|
};
|
||||||
@ -43,6 +45,7 @@ export default {
|
|||||||
checkLoading.value = false;
|
checkLoading.value = false;
|
||||||
checkResult.value = {};
|
checkResult.value = {};
|
||||||
selectCardInfo.value = cloneDeep(item);
|
selectCardInfo.value = cloneDeep(item);
|
||||||
|
selectedImageInfo.value = cloneDeep(item.files?.[0] ?? {});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onCardClick = async (item) => {
|
const onCardClick = async (item) => {
|
||||||
@ -54,11 +57,16 @@ export default {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onImageClick = (item) => {
|
||||||
|
selectedImageInfo.value = cloneDeep(item);
|
||||||
|
};
|
||||||
|
|
||||||
const getWorkAudits = async () => {
|
const getWorkAudits = async () => {
|
||||||
const { code, data } = await getWorkAuditsBatchDetail({ ids: workIds.value });
|
const { code, data } = await getWorkAuditsBatchDetail({ ids: workIds.value });
|
||||||
if (code === 200) {
|
if (code === 200) {
|
||||||
dataSource.value = data ?? [];
|
dataSource.value = data ?? [];
|
||||||
selectCardInfo.value = cloneDeep(data?.[0] ?? {});
|
selectCardInfo.value = cloneDeep(data?.[0] ?? {});
|
||||||
|
selectedImageInfo.value = data?.[0].files?.[0] ?? {};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -96,12 +104,7 @@ export default {
|
|||||||
dataSource.value = dataSource.value.filter((v) => v.id != _id);
|
dataSource.value = dataSource.value.filter((v) => v.id != _id);
|
||||||
|
|
||||||
slsWithCatch('manuscriptCheckIds', workIds.value.join(','));
|
slsWithCatch('manuscriptCheckIds', workIds.value.join(','));
|
||||||
if (dataSource.value.length) {
|
onChangeCard(dataSource.value.length ? dataSource.value[0] : {});
|
||||||
const _target = dataSource.value[0] ?? {};
|
|
||||||
selectCardInfo.value = cloneDeep(_target);
|
|
||||||
} else {
|
|
||||||
selectCardInfo.value = {};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const onSubmit = async () => {
|
const onSubmit = async () => {
|
||||||
@ -142,8 +145,7 @@ export default {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const manuscriptCheckIds = glsWithCatch('manuscriptCheckIds');
|
workIds.value = glsWithCatch('manuscriptCheckIds')?.split(',') ?? [];
|
||||||
workIds.value = manuscriptCheckIds.split(',');
|
|
||||||
getWorkAudits();
|
getWorkAudits();
|
||||||
});
|
});
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
@ -171,6 +173,8 @@ export default {
|
|||||||
onFilesChange={onFilesChange}
|
onFilesChange={onFilesChange}
|
||||||
onAgainCheck={onAgainCheck}
|
onAgainCheck={onAgainCheck}
|
||||||
checkLoading={checkLoading.value}
|
checkLoading={checkLoading.value}
|
||||||
|
selectedImageInfo={selectedImageInfo.value}
|
||||||
|
onImageClick={onImageClick}
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user