feat(creator-center): 新增两步发布作品并支持删除作品
This commit is contained in:
1 parent
4055b7a2a9
commit
3c730c7d33
6 files changed
+1522
-112
No files matched your search
@@ -109,6 +109,35 @@ export interface CreatorDramaEditData {
|
|||||||
language?: string
|
language?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 一键发布的分集入参:assetId 来自已完成的分片上传 */
|
||||||
|
export interface PublishEpisodeItem {
|
||||||
|
assetId: number
|
||||||
|
title?: string
|
||||||
|
durationSec?: number
|
||||||
|
cover?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PublishDramaPayload {
|
||||||
|
title: string
|
||||||
|
subtitle?: string
|
||||||
|
/** 短剧分类 id(下拉选择) */
|
||||||
|
categoryId?: number
|
||||||
|
/** 题材标签(自由文本,可选) */
|
||||||
|
genre?: string
|
||||||
|
description?: string
|
||||||
|
cover?: string
|
||||||
|
language?: string
|
||||||
|
/** 默认 true:创建后立即提交审核 */
|
||||||
|
submit?: boolean
|
||||||
|
episodes: PublishEpisodeItem[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PublishDramaResult {
|
||||||
|
dramaId: number
|
||||||
|
submissionId?: number | null
|
||||||
|
status: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface PriceEpisodeConfig {
|
export interface PriceEpisodeConfig {
|
||||||
episodeId: number
|
episodeId: number
|
||||||
number: number
|
number: number
|
||||||
@@ -176,6 +205,11 @@ export const createDramaApi = (data: CreatorDramaEditData) => {
|
|||||||
return request.post<number>({ url: '/admin/creator-center/dramas', data })
|
return request.post<number>({ url: '/admin/creator-center/dramas', data })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 一键发布:一个事务里建作品 + 建分集 + 绑定视频(可选提交审核) */
|
||||||
|
export const publishDramaApi = (data: PublishDramaPayload) => {
|
||||||
|
return request.post<PublishDramaResult>({ url: '/admin/creator-center/publish', data })
|
||||||
|
}
|
||||||
|
|
||||||
export const updateDramaApi = (id: number, data: CreatorDramaEditData) => {
|
export const updateDramaApi = (id: number, data: CreatorDramaEditData) => {
|
||||||
return request.put<void>({ url: `/admin/creator-center/dramas/${id}`, data })
|
return request.put<void>({ url: `/admin/creator-center/dramas/${id}`, data })
|
||||||
}
|
}
|
||||||
@@ -205,6 +239,11 @@ export const deleteEpisodeApi = (dramaId: number, episodeId: number) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 删除作品(草稿 / 已下线 / 审核中可删,已上线需先下线) */
|
||||||
|
export const deleteDramaApi = (dramaId: number) => {
|
||||||
|
return request.delete<void>({ url: `/admin/creator-center/dramas/${dramaId}` })
|
||||||
|
}
|
||||||
|
|
||||||
export const sortEpisodesApi = (dramaId: number, ids: number[]) => {
|
export const sortEpisodesApi = (dramaId: number, ids: number[]) => {
|
||||||
return request.put<void>({
|
return request.put<void>({
|
||||||
url: `/admin/creator-center/dramas/${dramaId}/episodes/sort`,
|
url: `/admin/creator-center/dramas/${dramaId}/episodes/sort`,
|
||||||
|
|||||||
@@ -298,6 +298,11 @@ html.dark .creator-guide-title {
|
|||||||
margin-bottom: 14px;
|
margin-bottom: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.creator-switch-hint {
|
||||||
|
margin-left: 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
html.dark .creator-profile-name,
|
html.dark .creator-profile-name,
|
||||||
html.dark .creator-profile-meta b,
|
html.dark .creator-profile-meta b,
|
||||||
html.dark .creator-drama-title,
|
html.dark .creator-drama-title,
|
||||||
@@ -316,3 +321,144 @@ html.dark .creator-episode-cover {
|
|||||||
border-color: var(--el-border-color-light);
|
border-color: var(--el-border-color-light);
|
||||||
background: var(--el-fill-color-light);
|
background: var(--el-fill-color-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------- 移动端(创作者多手机上传) ---------- */
|
||||||
|
.creator-mobile-only {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 作品表格:列宽合计超过容器时由外层横向滚动,
|
||||||
|
不依赖 el-table 内部的 scrollbar(其 header/body 会被各自裁切)。 */
|
||||||
|
.creator-desktop-only {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.creator-desktop-only .el-table.drama-el-table {
|
||||||
|
min-width: 1150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 作品编辑页:分集 / 提交记录表格由外层容器横向滚动(宽度见各表格 inline min-width) */
|
||||||
|
.creator-table-scroll {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 操作列按钮放不下时换行,避免「删除」被表格裁掉 */
|
||||||
|
.creator-table-scroll .table-actions {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
white-space: normal;
|
||||||
|
row-gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.creator-video-preview {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
max-height: 70vh;
|
||||||
|
background: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 768px) {
|
||||||
|
.creator-desktop-only {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.creator-mobile-only {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.creator-profile-meta {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.console-toolbar {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.console-toolbar .console-keyword,
|
||||||
|
.console-toolbar .console-select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.creator-drama-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.creator-drama-card {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 12px;
|
||||||
|
border: 1px solid #e6e8eb;
|
||||||
|
border-radius: var(--app-radius-md);
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.creator-drama-card-cover {
|
||||||
|
display: grid;
|
||||||
|
flex-shrink: 0;
|
||||||
|
place-content: center;
|
||||||
|
width: 60px;
|
||||||
|
height: 80px;
|
||||||
|
overflow: hidden;
|
||||||
|
color: #9a97a8;
|
||||||
|
font-size: 16px;
|
||||||
|
background: #eef0f5;
|
||||||
|
border-radius: var(--app-radius-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.creator-drama-card-cover img,
|
||||||
|
.creator-drama-card-cover .creator-drama-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.creator-drama-card-body {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.creator-drama-card-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.creator-drama-card-head .creator-drama-title {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.creator-drama-card-meta {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
color: #727983;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.creator-drama-card-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark .creator-drama-card {
|
||||||
|
border-color: var(--el-border-color-light);
|
||||||
|
background: var(--el-bg-color-overlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark .creator-drama-card-cover {
|
||||||
|
background: var(--el-fill-color-light);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
// Author: fruit.com.cn
|
||||||
|
/** 从本地视频文件截取封面(3:4 居中裁切),抖音式自动封面用 */
|
||||||
|
|
||||||
|
export interface CapturedCover {
|
||||||
|
file: File
|
||||||
|
previewUrl: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadVideo(file: File): Promise<HTMLVideoElement> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const video = document.createElement('video')
|
||||||
|
video.preload = 'metadata'
|
||||||
|
video.muted = true
|
||||||
|
video.playsInline = true
|
||||||
|
const src = URL.createObjectURL(file)
|
||||||
|
const fail = (message: string) => {
|
||||||
|
URL.revokeObjectURL(src)
|
||||||
|
reject(new Error(message))
|
||||||
|
}
|
||||||
|
video.onloadedmetadata = () => {
|
||||||
|
URL.revokeObjectURL(src)
|
||||||
|
resolve(video)
|
||||||
|
}
|
||||||
|
video.onerror = () => fail('当前浏览器无法解析该视频格式')
|
||||||
|
video.src = src
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 截取第 atSeconds 秒画面,按 3:4 居中裁切输出 720×960 JPEG */
|
||||||
|
export async function captureVideoCover(file: File, atSeconds = 1): Promise<CapturedCover> {
|
||||||
|
const video = await loadVideo(file)
|
||||||
|
const duration = Number.isFinite(video.duration) && video.duration > 0 ? video.duration : 0
|
||||||
|
// 太短的视频从开头截,正常视频取第 1 秒,避免黑场
|
||||||
|
const target = duration ? Math.min(atSeconds, Math.max(0.1, duration * 0.2)) : 0
|
||||||
|
await new Promise<void>((resolve, reject) => {
|
||||||
|
const timer = window.setTimeout(() => reject(new Error('截取封面超时')), 8000)
|
||||||
|
video.onseeked = () => {
|
||||||
|
window.clearTimeout(timer)
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
video.currentTime = target
|
||||||
|
})
|
||||||
|
|
||||||
|
const width = video.videoWidth
|
||||||
|
const height = video.videoHeight
|
||||||
|
if (!width || !height) throw new Error('无法读取视频尺寸')
|
||||||
|
|
||||||
|
const ratio = 3 / 4
|
||||||
|
let sw = width
|
||||||
|
let sh = height
|
||||||
|
if (width / height > ratio) {
|
||||||
|
sw = height * ratio
|
||||||
|
} else {
|
||||||
|
sh = width / ratio
|
||||||
|
}
|
||||||
|
const sx = (width - sw) / 2
|
||||||
|
const sy = (height - sh) / 2
|
||||||
|
|
||||||
|
const canvas = document.createElement('canvas')
|
||||||
|
canvas.width = 720
|
||||||
|
canvas.height = 960
|
||||||
|
const context = canvas.getContext('2d')
|
||||||
|
if (!context) throw new Error('当前浏览器不支持 canvas')
|
||||||
|
context.drawImage(video, sx, sy, sw, sh, 0, 0, canvas.width, canvas.height)
|
||||||
|
|
||||||
|
const blob = await new Promise<Blob>((resolve, reject) => {
|
||||||
|
canvas.toBlob(
|
||||||
|
(result) => (result ? resolve(result) : reject(new Error('封面生成失败'))),
|
||||||
|
'image/jpeg',
|
||||||
|
0.85
|
||||||
|
)
|
||||||
|
})
|
||||||
|
const baseName = file.name.replace(/\.[^.]+$/, '') || 'cover'
|
||||||
|
return {
|
||||||
|
file: new File([blob], `${baseName}-cover.jpg`, { type: 'image/jpeg' }),
|
||||||
|
previewUrl: URL.createObjectURL(blob)
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -30,16 +30,12 @@ const {
|
|||||||
profileLoading,
|
profileLoading,
|
||||||
dramas,
|
dramas,
|
||||||
notices,
|
notices,
|
||||||
createVisible,
|
|
||||||
createSaving,
|
|
||||||
createForm,
|
|
||||||
queryDramas,
|
queryDramas,
|
||||||
loadDramas,
|
loadDramas,
|
||||||
loadNotices,
|
loadNotices,
|
||||||
submit,
|
submit,
|
||||||
withdraw,
|
withdraw,
|
||||||
openCreate,
|
remove
|
||||||
confirmCreate
|
|
||||||
} = useCreatorWorkspace()
|
} = useCreatorWorkspace()
|
||||||
|
|
||||||
const activeTab = ref('dramas')
|
const activeTab = ref('dramas')
|
||||||
@@ -59,6 +55,11 @@ function onTabChange(name) {
|
|||||||
function openEdit(row) {
|
function openEdit(row) {
|
||||||
router.push(`/creator-center/dramas/${row.id}`)
|
router.push(`/creator-center/dramas/${row.id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 抖音式发布:直接进发布页选视频,不再先建空草稿 */
|
||||||
|
function openPublish() {
|
||||||
|
router.push('/creator-center/publish')
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -68,8 +69,8 @@ function openEdit(row) {
|
|||||||
title="我的作品"
|
title="我的作品"
|
||||||
description="查看作品与审核进度,提交上线 / 下线申请。"
|
description="查看作品与审核进度,提交上线 / 下线申请。"
|
||||||
>
|
>
|
||||||
<el-button v-if="canEdit && !impersonating" type="primary" @click="openCreate"
|
<el-button v-if="canEdit && !impersonating" type="primary" @click="openPublish"
|
||||||
>新建作品</el-button
|
>发布作品</el-button
|
||||||
>
|
>
|
||||||
</PageHeading>
|
</PageHeading>
|
||||||
|
|
||||||
@@ -114,7 +115,7 @@ function openEdit(row) {
|
|||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="panel">
|
<section class="panel creator-workspace-wrap">
|
||||||
<el-tabs v-model="activeTab" @tab-change="onTabChange">
|
<el-tabs v-model="activeTab" @tab-change="onTabChange">
|
||||||
<el-tab-pane label="我的作品" name="dramas">
|
<el-tab-pane label="我的作品" name="dramas">
|
||||||
<div class="console-toolbar">
|
<div class="console-toolbar">
|
||||||
@@ -146,47 +147,151 @@ function openEdit(row) {
|
|||||||
<span class="console-count">共 {{ dramas.total }} 部作品</span>
|
<span class="console-count">共 {{ dramas.total }} 部作品</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-table
|
<div class="creator-desktop-only">
|
||||||
v-loading="dramas.loading"
|
<el-table
|
||||||
:data="dramas.rows"
|
v-loading="dramas.loading"
|
||||||
row-key="id"
|
:data="dramas.rows"
|
||||||
border
|
row-key="id"
|
||||||
class="drama-el-table"
|
border
|
||||||
empty-text="还没有作品,点右上角「新建作品」开始"
|
class="drama-el-table"
|
||||||
>
|
empty-text="还没有作品,点右上角「发布作品」开始"
|
||||||
<el-table-column label="作品" min-width="220">
|
>
|
||||||
<template #default="{ row }">
|
<el-table-column label="作品" min-width="220">
|
||||||
<div class="creator-drama-cell">
|
<template #default="{ row }">
|
||||||
<div class="creator-drama-cover">
|
<div class="creator-drama-cell">
|
||||||
<AppImage
|
<div class="creator-drama-cover">
|
||||||
v-if="row.cover"
|
<AppImage
|
||||||
:src="row.cover"
|
v-if="row.cover"
|
||||||
:preview-list="workspaceCoverPreviewList"
|
:src="row.cover"
|
||||||
:initial-index="workspaceCoverPreviewIndex(row)"
|
:preview-list="workspaceCoverPreviewList"
|
||||||
fit="cover"
|
:initial-index="workspaceCoverPreviewIndex(row)"
|
||||||
class="creator-drama-image"
|
fit="cover"
|
||||||
:alt="row.title"
|
class="creator-drama-image"
|
||||||
/>
|
:alt="row.title"
|
||||||
<span v-else>{{ (row.title || '?').slice(0, 1) }}</span>
|
/>
|
||||||
|
<span v-else>{{ (row.title || '?').slice(0, 1) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="creator-drama-meta">
|
||||||
|
<span class="creator-drama-title">{{ row.title }}</span>
|
||||||
|
<span class="creator-drama-code">{{ row.code }}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="creator-drama-meta">
|
</template>
|
||||||
<span class="creator-drama-title">{{ row.title }}</span>
|
</el-table-column>
|
||||||
<span class="creator-drama-code">{{ row.code }}</span>
|
<el-table-column label="状态" width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag :type="dramaStatusMeta(row.status).tag" effect="light">{{
|
||||||
|
dramaStatusMeta(row.status).label
|
||||||
|
}}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="episodes" label="分集" width="80" />
|
||||||
|
<el-table-column prop="views" label="播放" width="100" />
|
||||||
|
<el-table-column label="审核进度" min-width="200">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div v-if="row.submissionStatus" class="creator-progress">
|
||||||
|
<el-tag
|
||||||
|
size="small"
|
||||||
|
:type="submissionStatusMeta(row.submissionStatus).tag"
|
||||||
|
effect="plain"
|
||||||
|
>
|
||||||
|
{{ submissionTypeText(row.submissionType) }}·{{
|
||||||
|
submissionStatusMeta(row.submissionStatus).label
|
||||||
|
}}
|
||||||
|
</el-tag>
|
||||||
|
<span v-if="row.reviewNote" class="console-muted" :title="row.reviewNote">{{
|
||||||
|
row.reviewNote
|
||||||
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<span v-else class="console-muted">尚未提交</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="updatedAt"
|
||||||
|
label="更新时间"
|
||||||
|
min-width="165"
|
||||||
|
:formatter="dateCell"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
<el-table-column label="操作" width="280">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div class="table-actions">
|
||||||
|
<el-button
|
||||||
|
v-if="canEdit && !impersonating"
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="openEdit(row)"
|
||||||
|
>编辑</el-button
|
||||||
|
>
|
||||||
|
<el-dropdown
|
||||||
|
v-if="canEdit && !impersonating && submitOptions(row).length"
|
||||||
|
trigger="click"
|
||||||
|
@command="(type) => submit(row, type)"
|
||||||
|
>
|
||||||
|
<el-button size="small">提交审核</el-button>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item
|
||||||
|
v-for="type in submitOptions(row)"
|
||||||
|
:key="type"
|
||||||
|
:command="type"
|
||||||
|
>
|
||||||
|
{{ submissionTypeText(type) }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
<el-button
|
||||||
|
v-if="
|
||||||
|
canEdit &&
|
||||||
|
!impersonating &&
|
||||||
|
row.submissionId &&
|
||||||
|
['SUBMITTED', 'REVIEWING'].includes(row.submissionStatus)
|
||||||
|
"
|
||||||
|
type="danger"
|
||||||
|
size="small"
|
||||||
|
@click="withdraw(row)"
|
||||||
|
>撤回</el-button
|
||||||
|
>
|
||||||
|
<el-button
|
||||||
|
v-if="canEdit && !impersonating"
|
||||||
|
text
|
||||||
|
type="danger"
|
||||||
|
size="small"
|
||||||
|
@click="remove(row)"
|
||||||
|
>删除</el-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-loading="dramas.loading" class="creator-mobile-only creator-drama-list">
|
||||||
|
<el-empty v-if="!dramas.rows.length" description="还没有作品,点下方「发布作品」开始" />
|
||||||
|
<article v-for="row in dramas.rows" :key="row.id" class="creator-drama-card">
|
||||||
|
<div class="creator-drama-card-cover" @click="openEdit(row)">
|
||||||
|
<AppImage
|
||||||
|
v-if="row.cover"
|
||||||
|
:src="row.cover"
|
||||||
|
fit="cover"
|
||||||
|
class="creator-drama-image"
|
||||||
|
:alt="row.title"
|
||||||
|
/>
|
||||||
|
<span v-else>{{ (row.title || '?').slice(0, 1) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="creator-drama-card-body">
|
||||||
|
<div class="creator-drama-card-head">
|
||||||
|
<span class="creator-drama-title">{{ row.title }}</span>
|
||||||
|
<el-tag size="small" :type="dramaStatusMeta(row.status).tag" effect="light">
|
||||||
|
{{ dramaStatusMeta(row.status).label }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
<span class="creator-drama-code">{{ row.code }}</span>
|
||||||
|
<div class="creator-drama-card-meta">
|
||||||
|
<span>{{ row.episodes }} 集</span>
|
||||||
|
<span>{{ row.views }} 播放</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="状态" width="100">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-tag :type="dramaStatusMeta(row.status).tag" effect="light">{{
|
|
||||||
dramaStatusMeta(row.status).label
|
|
||||||
}}</el-tag>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="episodes" label="分集" width="80" />
|
|
||||||
<el-table-column prop="views" label="播放" width="100" />
|
|
||||||
<el-table-column label="审核进度" min-width="200">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<div v-if="row.submissionStatus" class="creator-progress">
|
<div v-if="row.submissionStatus" class="creator-progress">
|
||||||
<el-tag
|
<el-tag
|
||||||
size="small"
|
size="small"
|
||||||
@@ -197,32 +302,12 @@ function openEdit(row) {
|
|||||||
submissionStatusMeta(row.submissionStatus).label
|
submissionStatusMeta(row.submissionStatus).label
|
||||||
}}
|
}}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
<span v-if="row.reviewNote" class="console-muted" :title="row.reviewNote">{{
|
|
||||||
row.reviewNote
|
|
||||||
}}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<span v-else class="console-muted">尚未提交</span>
|
<span v-else class="console-muted">尚未提交</span>
|
||||||
</template>
|
<div v-if="canEdit && !impersonating" class="creator-drama-card-actions">
|
||||||
</el-table-column>
|
<el-button size="small" type="primary" @click="openEdit(row)">编辑</el-button>
|
||||||
<el-table-column
|
|
||||||
prop="updatedAt"
|
|
||||||
label="更新时间"
|
|
||||||
min-width="165"
|
|
||||||
:formatter="dateCell"
|
|
||||||
show-overflow-tooltip
|
|
||||||
/>
|
|
||||||
<el-table-column label="操作" width="210" fixed="right">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<div class="table-actions">
|
|
||||||
<el-button
|
|
||||||
v-if="canEdit && !impersonating"
|
|
||||||
type="primary"
|
|
||||||
size="small"
|
|
||||||
@click="openEdit(row)"
|
|
||||||
>编辑</el-button
|
|
||||||
>
|
|
||||||
<el-dropdown
|
<el-dropdown
|
||||||
v-if="canEdit && !impersonating && submitOptions(row).length"
|
v-if="submitOptions(row).length"
|
||||||
trigger="click"
|
trigger="click"
|
||||||
@command="(type) => submit(row, type)"
|
@command="(type) => submit(row, type)"
|
||||||
>
|
>
|
||||||
@@ -241,20 +326,18 @@ function openEdit(row) {
|
|||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
<el-button
|
<el-button
|
||||||
v-if="
|
v-if="
|
||||||
canEdit &&
|
row.submissionId && ['SUBMITTED', 'REVIEWING'].includes(row.submissionStatus)
|
||||||
!impersonating &&
|
|
||||||
row.submissionId &&
|
|
||||||
['SUBMITTED', 'REVIEWING'].includes(row.submissionStatus)
|
|
||||||
"
|
"
|
||||||
type="danger"
|
type="danger"
|
||||||
size="small"
|
size="small"
|
||||||
@click="withdraw(row)"
|
@click="withdraw(row)"
|
||||||
>撤回</el-button
|
>撤回</el-button
|
||||||
>
|
>
|
||||||
|
<el-button text type="danger" size="small" @click="remove(row)">删除</el-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</div>
|
||||||
</el-table-column>
|
</article>
|
||||||
</el-table>
|
</div>
|
||||||
|
|
||||||
<div class="pagination">
|
<div class="pagination">
|
||||||
<el-pagination
|
<el-pagination
|
||||||
@@ -290,41 +373,5 @@ function openEdit(row) {
|
|||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<el-dialog v-model="createVisible" title="新建作品" width="480px">
|
|
||||||
<el-form label-position="top">
|
|
||||||
<el-form-item label="作品名称" required>
|
|
||||||
<el-input
|
|
||||||
v-model="createForm.title"
|
|
||||||
maxlength="64"
|
|
||||||
placeholder="例如:闪婚后,傅先生每天都在撒糖"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="题材">
|
|
||||||
<el-input v-model="createForm.genre" maxlength="32" placeholder="例如:都市情感" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="简介">
|
|
||||||
<el-input
|
|
||||||
v-model="createForm.description"
|
|
||||||
type="textarea"
|
|
||||||
:rows="3"
|
|
||||||
maxlength="200"
|
|
||||||
show-word-limit
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-alert
|
|
||||||
type="info"
|
|
||||||
:closable="false"
|
|
||||||
show-icon
|
|
||||||
title="先建草稿,再去作品编辑页完善信息、分集与视频,然后提交审核"
|
|
||||||
/>
|
|
||||||
</el-form>
|
|
||||||
<template #footer>
|
|
||||||
<el-button @click="createVisible = false">取消</el-button>
|
|
||||||
<el-button type="primary" :loading="createSaving" @click="confirmCreate"
|
|
||||||
>创建草稿</el-button
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
</CreatorCenterPage>
|
</CreatorCenterPage>
|
||||||
</template>
|
</template>
|
||||||
@@ -4,6 +4,7 @@ import { onMounted, reactive, ref } from 'vue'
|
|||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import {
|
import {
|
||||||
createDramaApi,
|
createDramaApi,
|
||||||
|
deleteDramaApi,
|
||||||
getCreatorProfileApi,
|
getCreatorProfileApi,
|
||||||
listCreatorNoticesApi,
|
listCreatorNoticesApi,
|
||||||
pageMyDramasApi,
|
pageMyDramasApi,
|
||||||
@@ -168,6 +169,32 @@ export function useCreatorWorkspace() {
|
|||||||
await loadDramas()
|
await loadDramas()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 删作品。已上线的后端会拦,前端按状态给提示。 */
|
||||||
|
async function remove(row) {
|
||||||
|
const label = row.title || '该作品'
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm(
|
||||||
|
`《${label}》将被永久删除(分集、封面、审核记录一并清理),且不可恢复。确认删除?`,
|
||||||
|
'删除作品',
|
||||||
|
{
|
||||||
|
confirmButtonText: '确认删除',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
confirmButtonClass: 'el-button--danger'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await deleteDramaApi(row.id)
|
||||||
|
ElMessage.success('作品已删除')
|
||||||
|
await loadDramas()
|
||||||
|
} catch (e) {
|
||||||
|
// 后端可能抛"已上线,请先下线"之类的业务错误;ElMessage 全局拦截器会弹 message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const createVisible = ref(false)
|
const createVisible = ref(false)
|
||||||
const createSaving = ref(false)
|
const createSaving = ref(false)
|
||||||
const createForm = reactive({ title: '', genre: '', description: '' })
|
const createForm = reactive({ title: '', genre: '', description: '' })
|
||||||
@@ -216,6 +243,7 @@ export function useCreatorWorkspace() {
|
|||||||
loadNotices,
|
loadNotices,
|
||||||
submit,
|
submit,
|
||||||
withdraw,
|
withdraw,
|
||||||
|
remove,
|
||||||
openCreate,
|
openCreate,
|
||||||
confirmCreate
|
confirmCreate
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user