feat: 稿件详情处理

This commit is contained in:
rd
2025-08-05 10:48:08 +08:00
parent 447b0d1be3
commit f6e4bdea09
5 changed files with 79 additions and 39 deletions

View File

@ -85,7 +85,7 @@
<span class="cts num">{{ exactFormatTime(record[column.dataIndex]) }}</span> <span class="cts num">{{ exactFormatTime(record[column.dataIndex]) }}</span>
</template> </template>
<template v-else-if="column.dataIndex === 'cover'" #cell="{ record }"> <template v-else-if="column.dataIndex === 'cover'" #cell="{ record }">
<a-image :width="64" :height="64" :src="record.cover" class="!rounded-6px"> <a-image :width="64" :height="64" :src="record.cover" class="!rounded-6px" fit="cover">
<template #error> <template #error>
<img :src="icon4" class="w-full h-full" /> <img :src="icon4" class="w-full h-full" />
</template> </template>

View File

@ -1,7 +1,11 @@
<script lang="jsx"> <script lang="jsx">
import { Button, Message as AMessage } from '@arco-design/web-vue'; import { Button, Message as AMessage, Spin } from '@arco-design/web-vue';
import { useRouter, useRoute } from 'vue-router'; import { useRouter, useRoute } from 'vue-router';
import { AuditStatus } from '@/views/creative-generation-workshop/manuscript/check-list/constants'; import { AuditStatus } from '@/views/creative-generation-workshop/manuscript/check-list/constants';
import { getWorksDetail } from '@/api/all/generationWorkshop';
import { EnumManuscriptType } from '@/views/creative-generation-workshop/manuscript/list/constants.ts';
import { convertVideoUrlToCoverUrl, exactFormatTime } from '@/utils/tools.ts';
const DEFAULT_SOURCE_INFO = { const DEFAULT_SOURCE_INFO = {
title: '内容稿件列表', title: '内容稿件列表',
@ -16,36 +20,66 @@ export default {
const workId = ref(route.params.id); const workId = ref(route.params.id);
const { source, audit_status } = route.query; const { source, audit_status } = route.query;
const sourceInfo = computed(() => SOURCE_MAP.get(source) ?? DEFAULT_SOURCE_INFO);
console.log({ source });
// 视频播放相关状态 // 视频播放相关状态
const isPlaying = ref(false); const isPlaying = ref(false);
const videoRef = ref(null); const videoRef = ref(null);
const videoUrl = ref(''); const videoUrl = ref('');
const coverImageUrl = ref(''); const coverImageUrl = ref('');
const isVideoLoaded = ref(false); const isVideoLoaded = ref(false);
const contentType = ref('video'); const dataSource = ref({});
const images = ref([]);
const loading = ref(false);
const isVideo = computed(() => contentType.value === 'video'); const isVideo = computed(() => dataSource.value.type === EnumManuscriptType.Video);
const sourceInfo = computed(() => SOURCE_MAP.get(source) ?? DEFAULT_SOURCE_INFO);
const onBack = () => { const onBack = () => {
router.push({ name: sourceInfo.value.routeName }); router.push({ name: sourceInfo.value.routeName });
}; };
const initData = () => {
const [fileOne, ...fileOthers] = dataSource.value.files ?? [];
if (isVideo.value) {
videoUrl.value = fileOne;
coverImageUrl.value = convertVideoUrlToCoverUrl(fileOne);
} else {
coverImageUrl.value = fileOne;
images.value = fileOthers;
}
};
const getData = async () => {
try {
loading.value = true;
const { code, data } = await getWorksDetail(workId.value);
if (code === 200) {
dataSource.value = data;
initData();
}
} finally {
loading.value = false;
}
};
const renderMainImg = () => { const renderMainImg = () => {
if (!coverImageUrl.value) return null;
if (isVideo.value) { if (isVideo.value) {
return ( return (
<div class="main-img-box mb-16px relative overflow-hidden cursor-pointer" onClick={togglePlay}> <div class="main-video-box mb-16px relative overflow-hidden cursor-pointer" onClick={togglePlay}>
<video ref={videoRef} class="w-100% h-100% object-cover" onEnded={onVideoEnded}></video> <video ref={videoRef} class="w-100% h-100% object-contain" onEnded={onVideoEnded}></video>
<img src={coverImageUrl.value} class="w-100% h-100% object-cover absolute z-0 top-0 left-0" /> {!isPlaying.value && (
<div v-show={!isPlaying.value} class="play-icon"></div> <>
<img src={coverImageUrl.value} class="w-100% h-100% object-contain absolute z-0 top-0 left-0" />
<div v-show={!isPlaying.value} class="play-icon"></div>
</>
)}
</div> </div>
); );
} else { } else {
return ( return (
<div class="main-img-box mb-16px relative overflow-hidden cursor-pointer"> <div class="main-img-box mb-16px relative overflow-hidden cursor-pointer">
<img src={coverImageUrl.value} class="w-100% h-100% object-cover absolute z-0 top-0 left-0" /> <img src={coverImageUrl.value} class="w-100% h-100% object-contain absolute z-0 top-0 left-0" />
</div> </div>
); );
} }
@ -54,10 +88,10 @@ export default {
const togglePlay = () => { const togglePlay = () => {
if (!videoRef.value) return; if (!videoRef.value) return;
console.log('togglePlay', videoRef.value);
if (isPlaying.value) { if (isPlaying.value) {
videoRef.value.pause(); videoRef.value.pause();
} else { } else {
// 首次播放时才加载视频
if (!isVideoLoaded.value) { if (!isVideoLoaded.value) {
videoRef.value.src = videoUrl.value; videoRef.value.src = videoUrl.value;
isVideoLoaded.value = true; isVideoLoaded.value = true;
@ -101,10 +135,13 @@ export default {
); );
}; };
// 4. 修改模板部分根据contentType条件渲染 onMounted(() => {
workId && getData();
});
return () => ( return () => (
<> <Spin loading={loading.value} class="manuscript-detail-wrap" size={50}>
<div class="manuscript-detail-wrap"> <div class="h-full w-full flex flex-col">
<div class="flex items-center mb-8px"> <div class="flex items-center mb-8px">
<span class="cts color-#4E5969 cursor-pointer" onClick={onBack}> <span class="cts color-#4E5969 cursor-pointer" onClick={onBack}>
{sourceInfo.value.title} {sourceInfo.value.title}
@ -114,38 +151,32 @@ export default {
</div> </div>
<div class="flex-1 overflow-y-auto bg-#fff rounded-8px border-1px border-#D7D7D9 border-solid py-32px"> <div class="flex-1 overflow-y-auto bg-#fff rounded-8px border-1px border-#D7D7D9 border-solid py-32px">
<div class="w-684px mx-auto flex flex-col items-center"> <div class="w-684px mx-auto flex flex-col items-center">
<p class="mb-8px cts bold !text-28px !lh-40px !color-#211F24"> <div class="flex justify-start flex-col w-full">
挖到宝了这个平价好物让我素颜出门都自信挖到宝了这个平价 <p class="mb-8px cts bold !text-28px !lh-40px !color-#211F24">{dataSource.value.title}</p>
</p> <p class="cts !text-12px !color-#737478 mb-32px w-full text-left">
<p class="cts !text-12px !color-#737478 mb-32px w-full text-left">6月26日修改</p> {exactFormatTime(dataSource.value.update_time)}修改
</p>
</div>
{renderMainImg()} {renderMainImg()}
<p class="cts !color-#211F24 "> <p class="cts !color-#211F24 ">{dataSource.value.content}</p>
谁懂啊作为混干皮每天素颜出门总被说 气色好差直到被闺蜜按头安利这个 30 块的素颜霜
质地像冰淇淋一样推开就化完全不卡粉带一点自然提亮黄黑皮涂完像天生好皮肤连遮瑕都省了
最绝的是它还带轻微防晒值早上洗完脸涂一层就能冲出门懒人狂喜我已经空罐 3
瓶了现在同事都以为我每天早起化妆其实我多睡了 20 分钟 hhh
PS油痘肌姐妹建议局部薄涂后续补妆更服帖
</p>
{/* 仅图片类型显示图片列表 */} {/* 仅图片类型显示图片列表 */}
{!isVideo.value && ( {!isVideo.value && (
<div class="desc-img-wrap mt-40px"> <div class="desc-img-wrap mt-40px">
{new Array(5).fill(0).map((item, index) => ( {images.value.map((item, index) => (
<div class="desc-img-box" key={index}> <div class="desc-img-box" key={index}>
<img src="" class="w-100% h-100%" /> <img src={item} class="w-100% h-100% object-contain" />
</div> </div>
))} ))}
<div class="desc-img-box">
<img src="" class="w-100% h-100%" />
</div>
</div> </div>
)} )}
</div> </div>
</div> </div>
</div> </div>
<div class="flex justify-end items-center px-16px py-20px w-full bg-#fff footer-row">{renderFooterRow()}</div> <div class="flex justify-end items-center px-16px py-20px w-full bg-#fff footer-row">{renderFooterRow()}</div>
</> </Spin>
); );
}, },
}; };

View File

@ -1,8 +1,7 @@
$footer-height: 68px; $footer-height: 68px;
.manuscript-detail-wrap { .manuscript-detail-wrap {
width: 100%;
height: calc(100% - 72px); height: calc(100% - 72px);
display: flex;
flex-direction: column;
.cts { .cts {
color: #939499; color: #939499;
font-family: $font-family-regular; font-family: $font-family-regular;
@ -14,11 +13,15 @@ $footer-height: 68px;
font-family: $font-family-medium; font-family: $font-family-medium;
} }
} }
.main-video-box {
width: 320px;
height: auto;
background: #fff;
}
.main-img-box { .main-img-box {
width: 320px; width: 320px;
height: 472px; height: auto;
background: #fff; background: #fff;
object-fit: contain;
aspect-ratio: 3/4; aspect-ratio: 3/4;
} }
.desc-img-wrap { .desc-img-wrap {

View File

@ -67,12 +67,18 @@ export default {
const getData = async () => { const getData = async () => {
const { code, data } = await getWorksDetail(workId.value); const { code, data } = await getWorksDetail(workId.value);
if (code === 200) { if (code === 200) {
const { type, files } = data;
const _data = { ...data, videoInfo: cloneDeep(INITIAL_VIDEO_INFO) }; const _data = { ...data, videoInfo: cloneDeep(INITIAL_VIDEO_INFO) };
if (type === EnumManuscriptType.Video && files.length) {
_data.videoInfo.uploadStatus = 'end';
}
dataSource.value = cloneDeep(_data); dataSource.value = cloneDeep(_data);
remoteDataSource.value = cloneDeep(_data); remoteDataSource.value = cloneDeep(_data);
} }
}; };
const onChange = (val) => { const onChange = (val) => {
console.log('onChange', val);
dataSource.value = val; dataSource.value = val;
}; };
const onUpdateVideoInfo = (newVideoInfo) => { const onUpdateVideoInfo = (newVideoInfo) => {

View File

@ -71,7 +71,7 @@
{{ exactFormatTime(record[column.dataIndex]) }} {{ exactFormatTime(record[column.dataIndex]) }}
</template> </template>
<template v-else-if="column.dataIndex === 'cover'" #cell="{ record }"> <template v-else-if="column.dataIndex === 'cover'" #cell="{ record }">
<a-image :width="64" :height="64" :src="record.cover" class="!rounded-6px"> <a-image :width="64" :height="64" :src="record.cover" class="!rounded-6px" fit="cover">
<template #error> <template #error>
<img :src="icon4" class="w-full h-full" /> <img :src="icon4" class="w-full h-full" />
</template> </template>