feat(admin): 剧集列表展示分集互动数据

This commit is contained in:
Zhou Weidong committed 2026-10-21 15:59:08 +08:00
1 parent c1e03f94be
commit 0e814840b1
3 files changed
+58 -12

No files matched your search

+5
View File
@@ -77,6 +77,10 @@ export interface EpisodeItem {
status: string status: string
statusText: string statusText: string
views?: number views?: number
likeCount?: number
commentCount?: number
favoriteCount?: number
heat?: number
sort?: number sort?: number
} }
@@ -89,6 +93,7 @@ export interface EpisodeSaveData {
videoUrl?: string videoUrl?: string
isFree?: boolean isFree?: boolean
unlockCoin?: number unlockCoin?: number
likeCount?: number
status?: string status?: string
sort?: number sort?: number
} }
@@ -43,6 +43,7 @@ const coverError = ref('')
const coverFileName = ref('') const coverFileName = ref('')
const editingId = ref(null) const editingId = ref(null)
const editingCommentCount = ref(0) const editingCommentCount = ref(0)
const editingLikeCount = ref(0)
const formRef = ref() const formRef = ref()
const form = reactive({ const form = reactive({
title: '', title: '',
@@ -52,7 +53,6 @@ const form = reactive({
freeEpisodes: 3, freeEpisodes: 3,
unlockCoin: 0, unlockCoin: 0,
favoriteCount: 0, favoriteCount: 0,
likeCount: 0,
heat: 0, heat: 0,
isHot: false, isHot: false,
cover: '' cover: ''
@@ -162,7 +162,6 @@ function openCreate() {
freeEpisodes: 3, freeEpisodes: 3,
unlockCoin: 0, unlockCoin: 0,
favoriteCount: 0, favoriteCount: 0,
likeCount: 0,
heat: 0, heat: 0,
isHot: false, isHot: false,
cover: '' cover: ''
@@ -170,6 +169,7 @@ function openCreate() {
coverFileName.value = '' coverFileName.value = ''
coverError.value = '' coverError.value = ''
editingCommentCount.value = 0 editingCommentCount.value = 0
editingLikeCount.value = 0
dialogVisible.value = true dialogVisible.value = true
resetValidation() resetValidation()
} }
@@ -184,7 +184,6 @@ function openEdit(row) {
freeEpisodes: row.freeEpisodes ?? 3, freeEpisodes: row.freeEpisodes ?? 3,
unlockCoin: row.unlockCoin ?? 0, unlockCoin: row.unlockCoin ?? 0,
favoriteCount: row.favoriteCount ?? 0, favoriteCount: row.favoriteCount ?? 0,
likeCount: row.likeCount ?? 0,
heat: row.views ?? 0, heat: row.views ?? 0,
isHot: !!row.isHot, isHot: !!row.isHot,
cover: row.cover || '' cover: row.cover || ''
@@ -192,6 +191,7 @@ function openEdit(row) {
coverFileName.value = row.cover ? coverBasename(row.cover) : '' coverFileName.value = row.cover ? coverBasename(row.cover) : ''
coverError.value = '' coverError.value = ''
editingCommentCount.value = row.commentCount ?? 0 editingCommentCount.value = row.commentCount ?? 0
editingLikeCount.value = row.likeCount ?? 0
dialogVisible.value = true dialogVisible.value = true
resetValidation() resetValidation()
} }
@@ -606,13 +606,8 @@ onMounted(async () => {
/> />
</el-form-item> </el-form-item>
<el-form-item label="点赞数"> <el-form-item label="点赞数">
<el-input-number <div class="readonly-stat">{{ formatCount(editingLikeCount) }}</div>
v-model="form.likeCount" <p class="readonly-stat-hint">全剧分集汇总,只读</p>
class="editor-number"
controls-position="right"
:min="0"
:precision="0"
/>
</el-form-item> </el-form-item>
<el-form-item label="热度"> <el-form-item label="热度">
<el-input-number <el-input-number
@@ -625,7 +620,7 @@ onMounted(async () => {
</el-form-item> </el-form-item>
<el-form-item label="评论数"> <el-form-item label="评论数">
<div class="readonly-stat">{{ formatCount(editingCommentCount) }}</div> <div class="readonly-stat">{{ formatCount(editingCommentCount) }}</div>
<p class="readonly-stat-hint">实时统计,不可编辑</p> <p class="readonly-stat-hint">全剧汇总,实时统计,只读</p>
</el-form-item> </el-form-item>
</div> </div>
</section> </section>
@@ -35,6 +35,7 @@ const videoFileName = ref('')
const previewVisible = ref(false) const previewVisible = ref(false)
const previewEpisode = ref(null) const previewEpisode = ref(null)
const editingId = ref(null) const editingId = ref(null)
const editingCommentCount = ref(0)
const formRef = ref() const formRef = ref()
const form = reactive({ const form = reactive({
number: 1, number: 1,
@@ -43,6 +44,7 @@ const form = reactive({
videoUrl: '', videoUrl: '',
isFree: true, isFree: true,
unlockCoin: 0, unlockCoin: 0,
likeCount: 0,
status: 'draft' status: 'draft'
}) })
@@ -127,10 +129,12 @@ function openCreate() {
videoUrl: '', videoUrl: '',
isFree: true, isFree: true,
unlockCoin: selectedDrama.value?.unlockCoin ?? 0, unlockCoin: selectedDrama.value?.unlockCoin ?? 0,
likeCount: 0,
status: 'draft' status: 'draft'
}) })
videoFileName.value = '' videoFileName.value = ''
videoError.value = '' videoError.value = ''
editingCommentCount.value = 0
dialogVisible.value = true dialogVisible.value = true
resetValidation() resetValidation()
} }
@@ -144,10 +148,12 @@ function openEdit(row) {
videoUrl: row.videoUrl || '', videoUrl: row.videoUrl || '',
isFree: !!row.isFree, isFree: !!row.isFree,
unlockCoin: row.unlockCoin || 0, unlockCoin: row.unlockCoin || 0,
likeCount: row.likeCount ?? 0,
status: row.status status: row.status
}) })
videoFileName.value = row.videoUrl ? episodeBasename(row.videoUrl) : '' videoFileName.value = row.videoUrl ? episodeBasename(row.videoUrl) : ''
videoError.value = '' videoError.value = ''
editingCommentCount.value = row.commentCount ?? 0
dialogVisible.value = true dialogVisible.value = true
resetValidation() resetValidation()
} }
@@ -167,6 +173,7 @@ async function saveEpisode() {
videoUrl: form.videoUrl.trim(), videoUrl: form.videoUrl.trim(),
isFree: form.isFree, isFree: form.isFree,
unlockCoin: form.isFree ? 0 : form.unlockCoin || 0, unlockCoin: form.isFree ? 0 : form.unlockCoin || 0,
likeCount: form.likeCount,
status: form.status status: form.status
} }
saving.value = true saving.value = true
@@ -325,7 +332,9 @@ onMounted(async () => {
<span class="episode-context-label">当前短剧</span> <span class="episode-context-label">当前短剧</span>
<b class="episode-context-title">{{ selectedDrama?.title || '暂无短剧' }}</b> <b class="episode-context-title">{{ selectedDrama?.title || '暂无短剧' }}</b>
<small class="episode-context-count" <small class="episode-context-count"
>共 {{ selectedDrama?.episodes ?? episodes.length }} 集</small >共 {{ selectedDrama?.episodes ?? episodes.length }} 集 · 收藏
{{ formatCount(selectedDrama?.favoriteCount ?? 0) }} · 热度
{{ formatCount(selectedDrama?.views ?? 0) }}</small
> >
</div> </div>
<div class="episode-switcher"> <div class="episode-switcher">
@@ -398,6 +407,24 @@ onMounted(async () => {
<el-table-column label="播放量" width="120" <el-table-column label="播放量" width="120"
><template #default="{ row }">{{ formatCount(row.views) }}</template></el-table-column ><template #default="{ row }">{{ formatCount(row.views) }}</template></el-table-column
> >
<el-table-column label="点赞(本集)" width="120"
><template #default="{ row }">{{
formatCount(row.likeCount ?? 0)
}}</template></el-table-column
>
<el-table-column label="评论(本集)" width="120"
><template #default="{ row }">{{
formatCount(row.commentCount ?? 0)
}}</template></el-table-column
>
<el-table-column label="所属剧收藏" width="120"
><template #default="{ row }">{{
formatCount(row.favoriteCount ?? 0)
}}</template></el-table-column
>
<el-table-column label="所属剧热度" width="120"
><template #default="{ row }">{{ formatCount(row.heat ?? 0) }}</template></el-table-column
>
<el-table-column label="状态" width="110" <el-table-column label="状态" width="110"
><template #default="{ row }" ><template #default="{ row }"
><el-tag :class="['status-badge', statusClass(row.status)]" effect="light">{{ ><el-tag :class="['status-badge', statusClass(row.status)]" effect="light">{{
@@ -568,6 +595,25 @@ onMounted(async () => {
</el-form-item> </el-form-item>
</section> </section>
<section class="episode-editor-section">
<h3 class="episode-editor-section-title">互动数据</h3>
<div class="episode-editor-grid episode-editor-grid--two">
<el-form-item label="本集点赞数">
<el-input-number
v-model="form.likeCount"
class="episode-editor-number"
controls-position="right"
:min="0"
:precision="0"
/>
</el-form-item>
<el-form-item label="本集评论数">
<div class="readonly-stat">{{ formatCount(editingCommentCount) }}</div>
<p class="readonly-stat-hint">按集实时统计,不可编辑</p>
</el-form-item>
</div>
</section>
<section class="episode-editor-section"> <section class="episode-editor-section">
<h3 class="episode-editor-section-title">付费与状态</h3> <h3 class="episode-editor-section-title">付费与状态</h3>
<div class="episode-editor-grid episode-editor-grid--three"> <div class="episode-editor-grid episode-editor-grid--three">