diff --git a/src/App.vue b/src/App.vue index 21ab695..1ce3d3e 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,6 +1,6 @@ diff --git a/src/api/all/generationWorkshop.ts b/src/api/all/generationWorkshop.ts index d55ecd9..e614a6f 100644 --- a/src/api/all/generationWorkshop.ts +++ b/src/api/all/generationWorkshop.ts @@ -77,3 +77,46 @@ export const putWorkAuditsAuditPass = (params = {}) => { const { id: auditId, ...rest } = params as { id: string; [key: string]: any }; return Http.put(`/v1/work-audits/${auditId}/audit-pass`, rest); }; + +// 内容稿件-列表(客户) +export const getShareWorksList = (shareCode: string) => { + return Http.get( + '/v1/share/works/list', + {}, + { + headers: { 'share-code': shareCode }, + }, + ); +}; + +// 内容稿件-详情(客户) +export const getShareWorksDetail = (id: string, shareCode: string) => { + return Http.get( + `/v1/share/works/${id}`, + {}, + { + headers: { 'share-code': shareCode }, + }, + ); +}; + +// 内容稿件-确认(客户) +export const patchShareWorksConfirm = (id: string, params = {}, shareCode: string) => { + return Http.patch(`/v1/share/works/${id}/confirm`, params, { + headers: { 'share-code': shareCode }, + }); +}; + +// 内容稿件-评论(客户) +export const postShareWorksComments = (id: string, params = {}, shareCode: string) => { + return Http.post(`/v1/share/works/${id}/comments`, params, { + headers: { 'share-code': shareCode }, + }); +}; + +// 内容稿件-删除评论(客户) +export const deleteShareWorksComments = (id: string, commentId: string, shareCode: string) => { + return Http.delete(`/v1/share/works/${id}/comments/${commentId}`, { + headers: { 'share-code': shareCode }, + }); +}; diff --git a/src/assets/img/creative-generation-workshop/icon-confirm.png b/src/assets/img/creative-generation-workshop/icon-confirm.png new file mode 100644 index 0000000..9adbf19 Binary files /dev/null and b/src/assets/img/creative-generation-workshop/icon-confirm.png differ diff --git a/src/router/app-menus/index.ts b/src/router/app-menus/index.ts deleted file mode 100644 index 9a8a4f0..0000000 --- a/src/router/app-menus/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * @Author: RenXiaoDong - * @Date: 2025-06-19 01:45:53 - */ -import { appRoutes } from '../routes'; - -const mixinRoutes = [...appRoutes]; - -const appClientMenus = mixinRoutes.map((el) => { - const { name, path, meta, redirect, children } = el; - return { - name, - path, - meta, - redirect, - children, - }; -}); - -export default mixinRoutes; diff --git a/src/router/index.ts b/src/router/index.ts index aa86ee7..c041c60 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -4,7 +4,6 @@ */ import { createRouter, createWebHistory } from 'vue-router'; import { appRoutes } from './routes'; -import { NOT_FOUND_ROUTE } from './routes/base'; import NProgress from 'nprogress'; import 'nprogress/nprogress.css'; import { MENU_GROUP_IDS } from './constants'; @@ -35,27 +34,18 @@ export const router = createRouter({ id: MENU_GROUP_IDS.WORK_BENCH_ID, }, }, + + ...appRoutes, { - path: '/permission', - name: 'permission', - component: () => import('@/views/components/permission/choose-enterprise.vue'), + path: '/:pathMatch(.*)*', + name: 'notFound', + component: () => import('@/layouts/NotFound.vue'), meta: { requiresAuth: false, - requireLogin: true, + hideInMenu: true, + hideSidebar: true, }, }, - // { - // path: '/auth', - // name: 'auth', - // component: () => import('@/views/components/permission/auth.vue'), - // meta: { - // requiresAuth: false, - // requireLogin: true, - // }, - // }, - ...appRoutes, - // REDIRECT_MAIN, - NOT_FOUND_ROUTE, ], scrollBehavior() { return { top: 0 }; diff --git a/src/router/routes/base.ts b/src/router/routes/base.ts deleted file mode 100644 index f10ef89..0000000 --- a/src/router/routes/base.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { RouteRecordRaw } from 'vue-router'; -import { REDIRECT_ROUTE_NAME } from '@/router/constants'; - -// export const REDIRECT_MAIN: RouteRecordRaw = { -// path: '/redirect', -// name: 'redirect', -// meta: { -// requiresAuth: false, -// requireLogin: false, -// hideInMenu: true, -// }, -// children: [ -// { -// path: '/redirect/:path', -// name: REDIRECT_ROUTE_NAME, -// component: () => import('@/layouts/Basic.vue'), -// meta: { -// requiresAuth: false, -// requireLogin: false, -// hideInMenu: true, -// }, -// }, -// ], -// }; - -export const NOT_FOUND_ROUTE: RouteRecordRaw = { - path: '/:pathMatch(.*)*', - name: 'notFound', - component: () => import('@/layouts/NotFound.vue'), - meta: { - requiresAuth: false, - hideInMenu: true, - hideSidebar: true, - }, -}; diff --git a/src/router/routes/modules/creativeGenerationWorkshop.ts b/src/router/routes/modules/creativeGenerationWorkshop.ts index c426c3e..92393fd 100644 --- a/src/router/routes/modules/creativeGenerationWorkshop.ts +++ b/src/router/routes/modules/creativeGenerationWorkshop.ts @@ -111,6 +111,28 @@ const COMPONENTS: AppRouteRecordRaw[] = [ }, ], }, + { + path: '/explore/list/:shareCode', + name: 'ExploreList', + meta: { + locale: '分享链接列表', + requiresAuth: false, + requireLogin: false, + roles: ['*'], + }, + component: () => import('@/views/creative-generation-workshop/explore/list/index.vue'), + }, + { + path: '/explore/detail/:shareCode/:id', + name: 'ExploreDetail', + meta: { + locale: '分享链接详情', + requiresAuth: false, + requireLogin: false, + roles: ['*'], + }, + component: () => import('@/views/creative-generation-workshop/explore/detail/index.vue'), + }, ]; export default COMPONENTS; diff --git a/src/utils/tools.ts b/src/utils/tools.ts index e78aa17..687c535 100644 --- a/src/utils/tools.ts +++ b/src/utils/tools.ts @@ -131,101 +131,101 @@ export function getVideoInfo(file: File): Promise<{ duration: number; firstFrame video.style.position = 'fixed'; // 确保视频元素在DOM中 video.style.top = '-1000px'; // 但不可见 document.body.appendChild(video); // 添加到DOM - + let hasResolved = false; - + // 先获取元数据(时长) video.onloadedmetadata = function () { // 视频时长 const duration = video.duration; - + // 尝试将视频定位到非常小的时间点,确保有帧可捕获 if (duration > 0) { video.currentTime = Math.min(0.1, duration / 2); } }; - + // 当视频定位完成后尝试捕获首帧 video.onseeked = function () { if (hasResolved) return; - + const canvas = document.createElement('canvas'); canvas.width = video.videoWidth; canvas.height = video.videoHeight; - + const ctx = canvas.getContext('2d'); if (ctx) { ctx.drawImage(video, 0, 0, canvas.width, canvas.height); } - + // 清理 window.URL.revokeObjectURL(video.src); document.body.removeChild(video); - + // 返回结果 hasResolved = true; resolve({ duration: video.duration, - firstFrame: canvas.toDataURL('image/jpeg', 0.9) // 提高质量 + firstFrame: canvas.toDataURL('image/jpeg', 0.9), // 提高质量 }); }; - + // 作为备选方案,监听loadeddata事件 video.onloadeddata = function () { if (hasResolved) return; - + // 尝试捕获帧 const canvas = document.createElement('canvas'); canvas.width = video.videoWidth; canvas.height = video.videoHeight; - + const ctx = canvas.getContext('2d'); if (ctx) { ctx.drawImage(video, 0, 0, canvas.width, canvas.height); } - + // 检查是否捕获到有效帧(非全黑) const imageData = ctx?.getImageData(0, 0, canvas.width, canvas.height); if (imageData) { let isAllBlack = true; for (let i = 0; i < imageData.data.length; i += 4) { - if (imageData.data[i] > 10 || imageData.data[i+1] > 10 || imageData.data[i+2] > 10) { + if (imageData.data[i] > 10 || imageData.data[i + 1] > 10 || imageData.data[i + 2] > 10) { isAllBlack = false; break; } } - + if (!isAllBlack) { // 清理 window.URL.revokeObjectURL(video.src); document.body.removeChild(video); - + // 返回结果 hasResolved = true; resolve({ duration: video.duration, - firstFrame: canvas.toDataURL('image/jpeg', 0.9) + firstFrame: canvas.toDataURL('image/jpeg', 0.9), }); return; } } - + // 如果是全黑帧,尝试定位到0.1秒 if (video.duration > 0) { video.currentTime = 0.1; } }; - + // 设置视频源以触发加载 video.src = URL.createObjectURL(file); - + // 设置超时,防止长时间无响应 setTimeout(() => { if (!hasResolved) { document.body.removeChild(video); resolve({ duration: 0, - firstFrame: '' + firstFrame: '', }); } }, 5000); // 5秒超时 @@ -266,7 +266,7 @@ export const formatUploadSpeed = (bytesPerSecond: number): string => { } }; - export function convertVideoUrlToCoverUrl(videoUrl: string): string { +export function convertVideoUrlToCoverUrl(videoUrl: string): string { if (!videoUrl || typeof videoUrl !== 'string') { console.error('Invalid video URL'); return ''; @@ -281,3 +281,20 @@ export const formatUploadSpeed = (bytesPerSecond: number): string => { return urlWithCovers + '.jpg'; } + +/** + * 生成包含协议、域名和参数的完整URL + */ +export const generateFullUrl = (pathTemplate: string, params: Record = {}): string => { + const protocol = window.location.protocol; + const hostname = window.location.hostname; + const port = window.location.port ? `:${window.location.port}` : ''; + const baseUrl = `${protocol}//${hostname}${port}`; + + let path = pathTemplate; + Object.entries(params).forEach(([key, value]) => { + path = path.replace(`:${key}`, String(value)); + }); + + return `${baseUrl}${path}`; +}; diff --git a/src/views/creative-generation-workshop/explore/detail/index.vue b/src/views/creative-generation-workshop/explore/detail/index.vue new file mode 100644 index 0000000..09ab964 --- /dev/null +++ b/src/views/creative-generation-workshop/explore/detail/index.vue @@ -0,0 +1,213 @@ + + + diff --git a/src/views/creative-generation-workshop/explore/detail/style.scss b/src/views/creative-generation-workshop/explore/detail/style.scss new file mode 100644 index 0000000..dbc1b58 --- /dev/null +++ b/src/views/creative-generation-workshop/explore/detail/style.scss @@ -0,0 +1,151 @@ +.explore-page { + position: relative; + padding-top: $navbar-height; + min-width: 1200px; + height: 100vh; + background: #fff; + display: flex; + flex-direction: column; + .page-header { + position: fixed; + left: 0; + right: 0; + top: 0; + z-index: 1000; + min-width: 1200px; + .content { + height: $navbar-height; + border-bottom: 1px solid var(--Border-1, #d7d7d9); + } + } + .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; + } + } + + .page-wrap { + flex: 1; + display: flex; + justify-content: center; + .explore-detail-wrap { + min-height: 500px; + width: 684px; + .title { + color: var(--Text-1, #211f24); + font-family: $font-family-medium; + font-size: 28px; + font-style: normal; + font-weight: 400; + line-height: 40px; /* 142.857% */ + } + } + .fold-box { + width: 40px; + height: 40px; + border-radius: 30px; + border: 1px solid var(--Border-1, #d7d7d9); + background: #fff; + box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.15); + position: absolute; + right: 16px; + top: 32px; + display: flex; + justify-content: center; + align-items: center; + } + .main-video-box { + width: 320px; + height: auto; + background: #fff; + } + .main-img-box { + width: 347px; + height: auto; + background: #fff; + aspect-ratio: 3/4; + } + .desc-img-wrap { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 24px; + .desc-img-box { + width: 212px; + height: 283px; + background: #fff; + object-fit: contain; + aspect-ratio: 3/4; + } + } + .play-icon { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 222; + width: 64px; + height: 64px; + background-image: url('@/assets/img/creative-generation-workshop/icon-play.png'); + background-size: contain; + background-repeat: no-repeat; + background-position: center; + transition: background-image 0.3s ease; + } + + .play-icon:hover { + background-image: url('@/assets/img/creative-generation-workshop/icon-play-hover.png'); + } + .ai-suggest-box { + width: 440px; + height: fit-content; + max-height: 100%; + border-radius: 16px; + background: linear-gradient(126deg, #eef2fd 8.36%, #f5ebfe 49.44%, #fdebf3 90.52%); + box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.1); + .ai-text { + font-family: $font-family-medium; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 24px; + background: linear-gradient(85deg, #7d419d 4.56%, #31353d 94.75%); + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + } + .result-box { + background: rgba(255, 255, 255, 0.8); + backdrop-filter: blur(4px); + .result-item { + .s1 { + color: var(--Brand-6, #6d4cfe); + font-family: $font-family-manrope-regular; + font-size: 24px; + font-style: normal; + font-weight: 700; + line-height: 32px; /* 133.333% */ + } + &:first-child { + position: relative; + &::after { + content: ''; + position: absolute; + top: 50%; + transform: translateY(-50%); + right: 0; + width: 1px; + height: 32px; + background: var(--Border-1, #d7d7d9); + } + } + } + } + } + } +} diff --git a/src/views/creative-generation-workshop/explore/list/index.vue b/src/views/creative-generation-workshop/explore/list/index.vue new file mode 100644 index 0000000..3be908e --- /dev/null +++ b/src/views/creative-generation-workshop/explore/list/index.vue @@ -0,0 +1,102 @@ + + + diff --git a/src/views/creative-generation-workshop/explore/list/style.scss b/src/views/creative-generation-workshop/explore/list/style.scss new file mode 100644 index 0000000..f9e8045 --- /dev/null +++ b/src/views/creative-generation-workshop/explore/list/style.scss @@ -0,0 +1,59 @@ +.explore-page { + position: relative; + padding-top: $navbar-height; + min-width: 1200px; + background: #fff; + .page-header { + position: fixed; + left: 0; + right: 0; + top: 0; + z-index: 1000; + min-width: 1200px; + .content { + height: $navbar-height; + border-bottom: 1px solid var(--Border-1, #d7d7d9); + } + } + .page-wrapper { + min-height: 500px; + .explore-container { + width: 1200px; + .explore-list-wrap { + .cts { + font-family: $font-family-regular; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 22px; + } + :deep(.overflow-text) { + color: var(--Text-1, #211f24); + font-family: $font-family-regular; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 22px; + &.bold { + font-family: $font-family-medium; + } + } + .card-container { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 24px; + .card-item { + border: 1px solid var(--Border-1, #d7d7d9); + cursor: pointer; + transition: all 0.3s; + &:hover { + box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.15); + border: 1.01px solid var(--Border-1, #d7d7d9); + border-radius: 8.08px; + } + } + } + } + } + } +} diff --git a/src/views/creative-generation-workshop/manuscript/check/components/check-list-drawer/index.vue b/src/views/creative-generation-workshop/manuscript/check/components/check-list-drawer/index.vue index 42a9cc0..fa0040b 100644 --- a/src/views/creative-generation-workshop/manuscript/check/components/check-list-drawer/index.vue +++ b/src/views/creative-generation-workshop/manuscript/check/components/check-list-drawer/index.vue @@ -39,7 +39,7 @@ export default { 批量审核列表 {`共${dataSource.value.length}个`} - +
{dataSource.value.map((item) => ( diff --git a/src/views/creative-generation-workshop/manuscript/check/components/content-card/index.vue b/src/views/creative-generation-workshop/manuscript/check/components/content-card/index.vue index 65111b7..f5d880b 100644 --- a/src/views/creative-generation-workshop/manuscript/check/components/content-card/index.vue +++ b/src/views/creative-generation-workshop/manuscript/check/components/content-card/index.vue @@ -22,6 +22,7 @@ import 'swiper/css/navigation'; import { Navigation } from 'swiper/modules'; import { FORM_RULES, enumTab, TAB_LIST, RESULT_LIST } from './constants'; import { getImagePreSignedUrl } from '@/api/all/common'; +import { EnumManuscriptType } from '@/views/creative-generation-workshop/manuscript/list/constants'; import icon1 from '@/assets/img/creative-generation-workshop/icon-magic.png'; import icon2 from '@/assets/img/creative-generation-workshop/icon-line.png'; @@ -53,6 +54,12 @@ export default { const modules = [Navigation]; const checkLoading = ref(false); + const tabList = computed(() => { + if (props.modelValue.type === EnumManuscriptType.Image) { + return TAB_LIST; + } + return TAB_LIST.filter((item) => item.value !== enumTab.IMAGE); + }); const isTextTab = computed(() => activeTab.value === enumTab.TEXT); const onAiReplace = () => { @@ -336,7 +343,7 @@ export default { ))}
-
+
AI 审核建议
@@ -434,7 +441,7 @@ export default {
- {TAB_LIST.map((item) => ( + {tabList.value.map((item) => ( ))} diff --git a/src/views/creative-generation-workshop/manuscript/check/components/header-card/index.vue b/src/views/creative-generation-workshop/manuscript/check/components/header-card/index.vue index a00b885..ea569c7 100644 --- a/src/views/creative-generation-workshop/manuscript/check/components/header-card/index.vue +++ b/src/views/creative-generation-workshop/manuscript/check/components/header-card/index.vue @@ -17,18 +17,13 @@ export default { type: Array, default: () => [], }, - modelValue: { - type: Object, - default: {}, - }, selectCardInfo: { type: Object, default: {}, }, }, - emits: ['update:modelValue', 'cardClick'], + emits: ['cardClick', 'platformChange'], setup(props, { emit, expose }) { - const selectedPlatform = ref(1); const modules = [Navigation]; const handleCardClick = (item) => { // emit('update:modelValue', item); @@ -94,15 +89,15 @@ export default {
{ - selectedPlatform.value = item.value; + emit('platformChange', 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' : '' + props.selectCardInfo.platform === item.value ? '!bg-#F0EDFF !border-#6D4CFE' : '' }`} > - + {item.label}
diff --git a/src/views/creative-generation-workshop/manuscript/check/index.vue b/src/views/creative-generation-workshop/manuscript/check/index.vue index 66cac56..166d2d9 100644 --- a/src/views/creative-generation-workshop/manuscript/check/index.vue +++ b/src/views/creative-generation-workshop/manuscript/check/index.vue @@ -64,10 +64,15 @@ export default { const getWorkAudits = async () => { const { code, data } = await getWorkAuditsBatchDetail({ ids: workIds.value }); if (code === 200) { - dataSource.value = data ?? []; - remoteDataSource.value = cloneDeep(data ?? []); - selectCardInfo.value = cloneDeep(data?.[0] ?? {}); - selectedImageInfo.value = data?.[0].files?.[0] ?? {}; + const _data = (data ?? []).map((item) => ({ + ...item, + platform: item.platform === 0 ? 1 : item.platform, + })); + + dataSource.value = _data; + remoteDataSource.value = cloneDeep(_data); + selectCardInfo.value = cloneDeep(_data?.[0] ?? {}); + selectedImageInfo.value = cloneDeep(_data?.[0].files?.[0] ?? {}); } }; @@ -77,6 +82,9 @@ export default { resolve(!isEqual(selectCardInfo.value, _item) && !isSaved.value); }); }; + const onPlatformChange = (platform) => { + selectCardInfo.value.platform = platform; + }; const onExit = async () => { const isModified = await isSelectCardModified(); @@ -165,13 +173,18 @@ export default { class="check-list-icon" onClick={() => checkListDrawerRef.value.open(dataSource.value, selectCardInfo.value)} > - + 审核列表
)}
- +