feat(web): 创作者中心「当前创作者」代看下拉 + 未绑定引导页

为什么:超管点进创作者中心原本是一片"未绑定创作者主体"的报错,既没说明原因也没有出路。
这批把两件事做出来:超管能在顶部选一个创作者只读代看;没有创作者身份的账号看到明确的引导页而不是白屏。

- 新增 useCreatorAccess(身份三态:granted / need-target / unbound)+ ImpersonationBar + CreatorAccessGuide,
  四个页面用 CreatorCenterPage 统一套壳;身份不满足时不打业务接口,避免一串无意义的报错提示
- 代看目标存 localStorage,由 axios.request 统一给 /admin/creator-center 的请求加 X-Creator-Id 头
  (不做查询参数;后端只对具备 impersonate 权限的账号认这个头)
- axios 支持 silent 请求:业务码非 0 时不弹全局提示、把 Result 交给调用方判断(引导页据此分流)
- 代看只读体验:代看态下「新建作品 / 编辑 / 提交审核 / 撤回」不渲染,编辑页整页禁用并提示原因
- 顶部下拉只在有 impersonate 权限时显示;无权限账号看不到、也不会请求 targets 接口
This commit is contained in:
Vicky committed 2026-10-15 15:25:53 +08:00
1 parent 0a2e3e0b81
commit 685c912313
19 files changed
+995 -658

No files matched your search

+254 -239
View File
@@ -3,6 +3,8 @@ import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { Search } from '@element-plus/icons-vue'
import PageHeading from '@/views/components/PageHeading.vue'
import CreatorCenterPage from './components/CreatorCenterPage.vue'
import { useCreatorAccess } from './composables/useCreatorAccess'
import { useRbac } from '@/stores/rbac'
import { dateCell } from '@/utils/format'
import {
@@ -17,6 +19,8 @@ import {
const router = useRouter()
const { can } = useRbac()
const canEdit = can('creator-center:drama:edit')
// 代看是只读:写操作按钮直接不渲染(后端也会拒,界面不该给出做不到的入口)
const { impersonating } = useCreatorAccess()
const {
profile,
@@ -47,249 +51,260 @@ function openEdit(row) {
</script>
<template>
<PageHeading
eyebrow="创作者中心"
title="我的作品"
description="查看作品与审核进度,提交上线 / 下线申请。"
>
<el-button v-if="canEdit" type="primary" @click="openCreate">新建作品</el-button>
</PageHeading>
<CreatorCenterPage>
<PageHeading
eyebrow="创作者中心"
title="我的作品"
description="查看作品与审核进度,提交上线 / 下线申请。"
>
<el-button v-if="canEdit && !impersonating" type="primary" @click="openCreate"
>新建作品</el-button
>
</PageHeading>
<section v-loading="profileLoading" class="panel creator-profile">
<div class="creator-profile-main">
<span class="creator-profile-name">{{ profile?.name || '—' }}</span>
<el-tag
size="small"
effect="light"
:type="profile?.status === 'approved' ? 'success' : 'warning'"
>
{{ profile?.statusText || '—' }}
</el-tag>
<span class="console-muted">{{ profile?.code || '' }}</span>
</div>
<div class="creator-profile-meta">
<span
>作品数 <b>{{ profile?.dramaTotal ?? 0 }}</b></span
>
<span
>联系人 <b>{{ profile?.contactName || '—' }}</b></span
>
<span
>联系邮箱 <b>{{ profile?.contactEmail || '—' }}</b></span
>
</div>
<el-alert
v-if="profile && profile.status === 'rejected'"
class="creator-profile-alert"
type="error"
:closable="false"
show-icon
:title="`入驻审核未通过:${profile.auditNote || '未填写原因'}`"
/>
<el-alert
v-else-if="profile && profile.status !== 'approved'"
class="creator-profile-alert"
type="warning"
:closable="false"
show-icon
title="入驻审核通过后才能创建作品与提交审核"
/>
</section>
<section class="panel">
<el-tabs v-model="activeTab" @tab-change="onTabChange">
<el-tab-pane label="我的作品" name="dramas">
<div class="console-toolbar">
<el-input
v-model="dramas.keyword"
class="console-keyword"
:prefix-icon="Search"
clearable
placeholder="搜索作品名称"
@keyup.enter="queryDramas"
@clear="queryDramas"
/>
<el-select
v-model="dramas.status"
class="console-select"
clearable
placeholder="全部状态"
@change="queryDramas"
>
<el-option
v-for="item in DRAMA_STATUS"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-button type="primary" @click="queryDramas">查询</el-button>
<span class="console-count">共 {{ dramas.total }} 部作品</span>
</div>
<el-table
v-loading="dramas.loading"
:data="dramas.rows"
row-key="id"
border
class="drama-el-table"
empty-text="还没有作品,点右上角「新建作品」开始"
<section v-loading="profileLoading" class="panel creator-profile">
<div class="creator-profile-main">
<span class="creator-profile-name">{{ profile?.name || '—' }}</span>
<el-tag
size="small"
effect="light"
:type="profile?.status === 'approved' ? 'success' : 'warning'"
>
<el-table-column label="作品" min-width="220">
<template #default="{ row }">
<div class="creator-drama-cell">
<div class="creator-drama-cover">
<img v-if="row.cover" :src="row.cover" alt="" />
<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>
</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">
<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>
<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="210" fixed="right">
<template #default="{ row }">
<div class="table-actions">
<el-button v-if="canEdit" type="primary" size="small" @click="openEdit(row)"
>编辑</el-button
>
<el-dropdown
v-if="canEdit && 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 &&
row.submissionId &&
['SUBMITTED', 'REVIEWING'].includes(row.submissionStatus)
"
size="small"
class="danger-action"
@click="withdraw(row)"
>撤回</el-button
>
</div>
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination
v-model:current-page="dramas.page"
v-model:page-size="dramas.size"
:page-sizes="[10, 20, 50]"
:total="dramas.total"
layout="total, prev, pager, next, sizes"
background
@current-change="loadDramas"
@size-change="queryDramas"
/>
</div>
</el-tab-pane>
<el-tab-pane label="通知" name="notices">
<div v-loading="notices.loading" class="creator-notice-list">
<el-empty v-if="!notices.rows.length" description="还没有审核结果通知" />
<div
v-for="item in notices.rows"
:key="`${item.bizType}-${item.id}`"
class="creator-notice"
>
<div class="creator-notice-head">
<span class="creator-notice-title">{{ item.title }}</span>
<span class="console-muted">{{
item.time ? String(item.time).replace('T', ' ').slice(0, 19) : ''
}}</span>
</div>
<p class="creator-notice-body">{{ item.content || '—' }}</p>
</div>
</div>
</el-tab-pane>
</el-tabs>
</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>
{{ profile?.statusText || '—' }}
</el-tag>
<span class="console-muted">{{ profile?.code || '' }}</span>
</div>
<div class="creator-profile-meta">
<span
>作品数 <b>{{ profile?.dramaTotal ?? 0 }}</b></span
>
<span
>联系人 <b>{{ profile?.contactName || '—' }}</b></span
>
<span
>联系邮箱 <b>{{ profile?.contactEmail || '—' }}</b></span
>
</div>
<el-alert
type="info"
v-if="profile && profile.status === 'rejected'"
class="creator-profile-alert"
type="error"
:closable="false"
show-icon
title="先建草稿,再去作品编辑页完善信息、分集与视频,然后提交审核"
:title="`入驻审核未通过:${profile.auditNote || '未填写原因'}`"
/>
</el-form>
<template #footer>
<el-button @click="createVisible = false">取消</el-button>
<el-button type="primary" :loading="createSaving" @click="confirmCreate">创建草稿</el-button>
</template>
</el-dialog>
<el-alert
v-else-if="profile && profile.status !== 'approved'"
class="creator-profile-alert"
type="warning"
:closable="false"
show-icon
title="入驻审核通过后才能创建作品与提交审核"
/>
</section>
<section class="panel">
<el-tabs v-model="activeTab" @tab-change="onTabChange">
<el-tab-pane label="我的作品" name="dramas">
<div class="console-toolbar">
<el-input
v-model="dramas.keyword"
class="console-keyword"
:prefix-icon="Search"
clearable
placeholder="搜索作品名称"
@keyup.enter="queryDramas"
@clear="queryDramas"
/>
<el-select
v-model="dramas.status"
class="console-select"
clearable
placeholder="全部状态"
@change="queryDramas"
>
<el-option
v-for="item in DRAMA_STATUS"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-button type="primary" @click="queryDramas">查询</el-button>
<span class="console-count">共 {{ dramas.total }} 部作品</span>
</div>
<el-table
v-loading="dramas.loading"
:data="dramas.rows"
row-key="id"
border
class="drama-el-table"
empty-text="还没有作品,点右上角「新建作品」开始"
>
<el-table-column label="作品" min-width="220">
<template #default="{ row }">
<div class="creator-drama-cell">
<div class="creator-drama-cover">
<img v-if="row.cover" :src="row.cover" alt="" />
<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>
</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">
<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>
<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="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
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)
"
size="small"
class="danger-action"
@click="withdraw(row)"
>撤回</el-button
>
</div>
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination
v-model:current-page="dramas.page"
v-model:page-size="dramas.size"
:page-sizes="[10, 20, 50]"
:total="dramas.total"
layout="total, prev, pager, next, sizes"
background
@current-change="loadDramas"
@size-change="queryDramas"
/>
</div>
</el-tab-pane>
<el-tab-pane label="通知" name="notices">
<div v-loading="notices.loading" class="creator-notice-list">
<el-empty v-if="!notices.rows.length" description="还没有审核结果通知" />
<div
v-for="item in notices.rows"
:key="`${item.bizType}-${item.id}`"
class="creator-notice"
>
<div class="creator-notice-head">
<span class="creator-notice-title">{{ item.title }}</span>
<span class="console-muted">{{
item.time ? String(item.time).replace('T', ' ').slice(0, 19) : ''
}}</span>
</div>
<p class="creator-notice-body">{{ item.content || '—' }}</p>
</div>
</div>
</el-tab-pane>
</el-tabs>
</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>
</template>