更新国际化支持:为媒体详情视图添加多语言文本,增强用户体验

This commit is contained in:
jxxghp
2025-04-28 16:37:49 +08:00
parent 149403e5c0
commit f3af10e93e
4 changed files with 195 additions and 54 deletions
+43 -2
View File
@@ -251,8 +251,49 @@ export default {
adaptiveHeight: 'Adaptive Component Height', adaptiveHeight: 'Adaptive Component Height',
}, },
media: { media: {
movie: 'Movie', status: {
tv: 'TV Show', inLibrary: 'In Library',
missing: 'Missing',
partiallyMissing: 'Partially Missing',
subscribed: 'Subscribed',
},
minutes: 'minutes',
overview: 'Overview',
seasons: 'Seasons',
seasonNumber: 'Season {number}',
episodeCount: '{count} Episodes',
actions: {
searchResource: 'Search Resource',
subscribe: 'Subscribe',
playOnline: 'Play Online',
},
search: {
byTitle: 'Title',
byImdb: 'IMDB Link',
},
info: {
originalTitle: 'Original Title',
status: 'Status',
releaseDate: 'Release Date',
originalLanguage: 'Original Language',
productionCountries: 'Production Countries',
productionCompanies: 'Production Companies',
doubanId: 'Douban ID',
},
subscribe: {
normal: 'Subscribe',
bestVersion: 'Best Version Subscribe',
addFailed: 'Failed to add subscription: {reason}!',
canceled: 'Subscription canceled!',
cancelFailed: 'Failed to cancel subscription: {reason}!',
},
castAndCrew: 'Cast & Crew',
recommendations: 'Recommendations',
similar: 'Similar',
error: {
title: 'Error!',
noMediaInfo: 'No media information recognized.',
},
}, },
subscribe: { subscribe: {
normalSub: 'Subscribe', normalSub: 'Subscribe',
+43 -2
View File
@@ -251,8 +251,49 @@ export default {
adaptiveHeight: '自适应组件高度', adaptiveHeight: '自适应组件高度',
}, },
media: { media: {
movie: '电影', status: {
tv: '电视剧', inLibrary: '已入库',
missing: '缺失',
partiallyMissing: '部分缺失',
subscribed: '已订阅',
},
minutes: '分钟',
overview: '简介',
seasons: '季',
seasonNumber: '第 {number} 季',
episodeCount: '{count}集',
actions: {
searchResource: '搜索资源',
subscribe: '订阅',
playOnline: '在线播放',
},
search: {
byTitle: '标题',
byImdb: 'IMDB链接',
},
info: {
originalTitle: '原始标题',
status: '状态',
releaseDate: '上映日期',
originalLanguage: '原始语言',
productionCountries: '出品国家',
productionCompanies: '制作公司',
doubanId: '豆瓣ID',
},
subscribe: {
normal: '订阅',
bestVersion: '洗版订阅',
addFailed: '添加订阅失败:{reason}',
canceled: '已取消订阅!',
cancelFailed: '取消订阅失败:{reason}',
},
castAndCrew: '演员阵容',
recommendations: '推荐',
similar: '类似',
error: {
title: '出错啦!',
noMediaInfo: '未识别到媒体信息。',
},
}, },
subscribe: { subscribe: {
normalSub: '订阅', normalSub: '订阅',
+43 -2
View File
@@ -251,8 +251,49 @@ export default {
adaptiveHeight: '自適應組件高度', adaptiveHeight: '自適應組件高度',
}, },
media: { media: {
movie: '電影', status: {
tv: '電視劇', inLibrary: '已入庫',
missing: '缺失',
partiallyMissing: '部分缺失',
subscribed: '已訂閱',
},
minutes: '分鐘',
overview: '簡介',
seasons: '季',
seasonNumber: '第 {number} 季',
episodeCount: '{count}集',
actions: {
searchResource: '搜索資源',
subscribe: '訂閱',
playOnline: '線上播放',
},
search: {
byTitle: '標題',
byImdb: 'IMDB鏈接',
},
info: {
originalTitle: '原始標題',
status: '狀態',
releaseDate: '上映日期',
originalLanguage: '原始語言',
productionCountries: '出品國家',
productionCompanies: '製作公司',
doubanId: '豆瓣ID',
},
subscribe: {
normal: '訂閱',
bestVersion: '洗版訂閱',
addFailed: '添加訂閱失敗:{reason}',
canceled: '已取消訂閱!',
cancelFailed: '取消訂閱失敗:{reason}',
},
castAndCrew: '演員陣容',
recommendations: '推薦',
similar: '類似',
error: {
title: '出錯啦!',
noMediaInfo: '未識別到媒體信息。',
},
}, },
subscribe: { subscribe: {
normalSub: '訂閱', normalSub: '訂閱',
+66 -48
View File
@@ -13,6 +13,10 @@ import { useUserStore } from '@/stores'
import SubscribeEditDialog from '@/components/dialog/SubscribeEditDialog.vue' import SubscribeEditDialog from '@/components/dialog/SubscribeEditDialog.vue'
import SearchSiteDialog from '@/components/dialog/SearchSiteDialog.vue' import SearchSiteDialog from '@/components/dialog/SearchSiteDialog.vue'
import { useTheme } from 'vuetify' import { useTheme } from 'vuetify'
import { useI18n } from 'vue-i18n'
// 国际化
const { t } = useI18n()
// 输入参数 // 输入参数
const mediaProps = defineProps({ const mediaProps = defineProps({
@@ -291,10 +295,10 @@ async function addSubscribe(season = 0) {
function showSubscribeAddToast(result: boolean, title: string, season: number, message: string, best_version: number) { function showSubscribeAddToast(result: boolean, title: string, season: number, message: string, best_version: number) {
if (season) title = `${title} ${formatSeason(season.toString())}` if (season) title = `${title} ${formatSeason(season.toString())}`
let subname = '订阅' let subname = t('media.subscribe.normal')
if (best_version > 0) subname = '洗版订阅' if (best_version > 0) subname = t('media.subscribe.bestVersion')
if (!result) $toast.error(`${title} 添加${subname}失败:${message}`) if (!result) $toast.error(`${title} ${t('media.subscribe.addFailed', { reason: message })}`)
} }
// 调用API取消订阅 // 调用API取消订阅
@@ -313,9 +317,9 @@ async function removeSubscribe(season: number) {
if (result.success) { if (result.success) {
isSubscribed.value = false isSubscribed.value = false
if (season) seasonsSubscribed.value[season] = false if (season) seasonsSubscribed.value[season] = false
$toast.success(`${mediaDetail.value?.title} 已取消订阅!`) $toast.success(`${mediaDetail.value?.title} ${t('media.subscribe.canceled')}`)
} else { } else {
$toast.error(`${mediaDetail.value?.title} 取消订阅失败:${result.message}`) $toast.error(`${mediaDetail.value?.title} ${t('media.subscribe.cancelFailed', { reason: result.message })}`)
} }
} catch (error) { } catch (error) {
console.error(error) console.error(error)
@@ -420,11 +424,11 @@ function getExistColor(season: number) {
// 计算存在状态的文本 // 计算存在状态的文本
function getExistText(season: number) { function getExistText(season: number) {
const state = seasonsNotExisted.value[season] const state = seasonsNotExisted.value[season]
if (!state) return '已入库' if (!state) return t('media.status.inLibrary')
if (state === 1) return '部分缺失' if (state === 1) return t('media.status.partiallyMissing')
else if (state === 2) return '缺失' else if (state === 2) return t('media.status.missing')
else return '已入库' else return t('media.status.inLibrary')
} }
// 计算订阅图标 // 计算订阅图标
@@ -558,7 +562,9 @@ onBeforeMount(() => {
<span <span
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full whitespace-nowrap transition !no-underline bg-green-500 bg-opacity-80 border border-green-500 !text-green-100 hover:bg-green-500 hover:bg-opacity-100 false overflow-hidden" class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full whitespace-nowrap transition !no-underline bg-green-500 bg-opacity-80 border border-green-500 !text-green-100 hover:bg-green-500 hover:bg-opacity-100 false overflow-hidden"
> >
<div class="relative z-20 flex items-center false"><span>已入库</span></div> <div class="relative z-20 flex items-center false">
<span>{{ t('media.status.inLibrary') }}</span>
</div>
</span> </span>
</div> </div>
<h1 class="d-flex flex-column flex-lg-row align-baseline justify-center justify-lg-start"> <h1 class="d-flex flex-column flex-lg-row align-baseline justify-center justify-lg-start">
@@ -571,7 +577,7 @@ onBeforeMount(() => {
</h1> </h1>
<span class="media-attributes"> <span class="media-attributes">
<span v-if="mediaDetail.runtime || mediaDetail.episode_run_time[0]" <span v-if="mediaDetail.runtime || mediaDetail.episode_run_time[0]"
>{{ mediaDetail.runtime || mediaDetail.episode_run_time[0] }} 分钟</span >{{ mediaDetail.runtime || mediaDetail.episode_run_time[0] }} {{ t('media.minutes') }}</span
> >
<span v-if="(mediaDetail.runtime || mediaDetail.episode_run_time[0]) && mediaDetail.genres" class="mx-1"> <span v-if="(mediaDetail.runtime || mediaDetail.episode_run_time[0]) && mediaDetail.genres" class="mx-1">
| |
@@ -589,14 +595,14 @@ onBeforeMount(() => {
<template #prepend> <template #prepend>
<VIcon icon="mdi-magnify" /> <VIcon icon="mdi-magnify" />
</template> </template>
搜索资源 {{ t('media.actions.searchResource') }}
<VMenu activator="parent" close-on-content-click> <VMenu activator="parent" close-on-content-click>
<VList> <VList>
<VListItem @click="clickSearch('title')"> <VListItem @click="clickSearch('title')">
<VListItemTitle>标题</VListItemTitle> <VListItemTitle>{{ t('media.search.byTitle') }}</VListItemTitle>
</VListItem> </VListItem>
<VListItem @click="clickSearch('imdb')"> <VListItem @click="clickSearch('imdb')">
<VListItemTitle>IMDB链接</VListItemTitle> <VListItemTitle>{{ t('media.search.byImdb') }}</VListItemTitle>
</VListItem> </VListItem>
</VList> </VList>
</VMenu> </VMenu>
@@ -611,13 +617,13 @@ onBeforeMount(() => {
<template #prepend> <template #prepend>
<VIcon :icon="getSubscribeIcon" /> <VIcon :icon="getSubscribeIcon" />
</template> </template>
{{ isSubscribed ? '已订阅' : '订阅' }} {{ isSubscribed ? t('media.status.subscribed') : t('media.actions.subscribe') }}
</VBtn> </VBtn>
<VBtn v-if="existsItemId" class="ms-2 mb-2" variant="tonal" @click="handlePlay()"> <VBtn v-if="existsItemId" class="ms-2 mb-2" variant="tonal" @click="handlePlay()">
<template #prepend> <template #prepend>
<VIcon icon="mdi-play" /> <VIcon icon="mdi-play" />
</template> </template>
在线播放 {{ t('media.actions.playOnline') }}
</VBtn> </VBtn>
</div> </div>
</div> </div>
@@ -626,7 +632,7 @@ onBeforeMount(() => {
<div v-if="mediaDetail.tagline" class="tagline"> <div v-if="mediaDetail.tagline" class="tagline">
{{ mediaDetail.tagline }} {{ mediaDetail.tagline }}
</div> </div>
<h2 v-if="mediaDetail.overview">简介</h2> <h2 v-if="mediaDetail.overview">{{ t('media.overview') }}</h2>
<p>{{ mediaDetail.overview }}</p> <p>{{ mediaDetail.overview }}</p>
<ul v-if="mediaDetail.tmdb_id" class="media-crew"> <ul v-if="mediaDetail.tmdb_id" class="media-crew">
<li v-for="director in mediaDetail.directors" :key="director.id"> <li v-for="director in mediaDetail.directors" :key="director.id">
@@ -699,7 +705,7 @@ onBeforeMount(() => {
</div> </div>
</a> </a>
</div> </div>
<h2 v-if="mediaDetail.type === '电视剧' && mediaDetail.tmdb_id" class="py-4"></h2> <h2 v-if="mediaDetail.type === '电视剧' && mediaDetail.tmdb_id" class="py-4">{{ t('media.seasons') }}</h2>
<div v-if="mediaDetail.type === '电视剧' && mediaDetail.tmdb_id" class="flex w-full flex-col space-y-2"> <div v-if="mediaDetail.type === '电视剧' && mediaDetail.tmdb_id" class="flex w-full flex-col space-y-2">
<VExpansionPanels> <VExpansionPanels>
<VExpansionPanel <VExpansionPanel
@@ -710,8 +716,12 @@ onBeforeMount(() => {
<VExpansionPanelTitle> <VExpansionPanelTitle>
<template #default> <template #default>
<div class="flex flex-row items-center justify-between"> <div class="flex flex-row items-center justify-between">
<span class="font-weight-bold"> {{ season.season_number }} </span> <span class="font-weight-bold">{{
<VChip size="small" class="ms-1"> {{ season.episode_count }} </VChip> t('media.seasonNumber', { number: season.season_number })
}}</span>
<VChip size="small" class="ms-1">
{{ t('media.episodeCount', { count: season.episode_count }) }}
</VChip>
<div class="absolute right-12"> <div class="absolute right-12">
<VChip v-if="seasonsNotExisted" :color="getExistColor(season.season_number || 0)" flat> <VChip v-if="seasonsNotExisted" :color="getExistColor(season.season_number || 0)" flat>
{{ getExistText(season.season_number || 0) }} {{ getExistText(season.season_number || 0) }}
@@ -787,15 +797,15 @@ onBeforeMount(() => {
<span class="media-fact-value">{{ mediaDetail.tmdb_id }}</span> <span class="media-fact-value">{{ mediaDetail.tmdb_id }}</span>
</div> </div>
<div v-if="mediaDetail.original_title || mediaDetail.original_name" class="media-fact"> <div v-if="mediaDetail.original_title || mediaDetail.original_name" class="media-fact">
<span>原始标题</span> <span>{{ t('media.info.originalTitle') }}</span>
<span class="media-fact-value">{{ mediaDetail.original_title || mediaDetail.original_name }}</span> <span class="media-fact-value">{{ mediaDetail.original_title || mediaDetail.original_name }}</span>
</div> </div>
<div v-if="mediaDetail.status" class="media-fact"> <div v-if="mediaDetail.status" class="media-fact">
<span>状态</span> <span>{{ t('media.info.status') }}</span>
<span class="media-fact-value">{{ mediaDetail.status }}</span> <span class="media-fact-value">{{ mediaDetail.status }}</span>
</div> </div>
<div v-if="mediaDetail.release_date || mediaDetail.first_air_date" class="media-fact"> <div v-if="mediaDetail.release_date || mediaDetail.first_air_date" class="media-fact">
<span>上映日期</span> <span>{{ t('media.info.releaseDate') }}</span>
<span class="media-fact-value"> <span class="media-fact-value">
<span class="flex items-center justify-end"> <span class="flex items-center justify-end">
<svg <svg
@@ -818,11 +828,11 @@ onBeforeMount(() => {
</span> </span>
</div> </div>
<div v-if="mediaDetail.original_language" class="media-fact"> <div v-if="mediaDetail.original_language" class="media-fact">
<span>原始语言</span> <span>{{ t('media.info.originalLanguage') }}</span>
<span class="media-fact-value">{{ mediaDetail.original_language }}</span> <span class="media-fact-value">{{ mediaDetail.original_language }}</span>
</div> </div>
<div v-if="mediaDetail.production_countries" class="media-fact"> <div v-if="mediaDetail.production_countries" class="media-fact">
<span>出品国家</span> <span>{{ t('media.info.productionCountries') }}</span>
<span class="media-fact-value"> <span class="media-fact-value">
<span <span
v-for="country in getProductionCountries" v-for="country in getProductionCountries"
@@ -834,7 +844,7 @@ onBeforeMount(() => {
</span> </span>
</div> </div>
<div class="media-fact border-b-0"> <div class="media-fact border-b-0">
<span>制作公司</span> <span>{{ t('media.info.productionCompanies') }}</span>
<span class="media-fact-value text-end"> <span class="media-fact-value text-end">
<span v-for="company in getProductionCompanies" :key="company" class="block">{{ company }}</span> <span v-for="company in getProductionCompanies" :key="company" class="block">{{ company }}</span>
</span> </span>
@@ -847,21 +857,21 @@ onBeforeMount(() => {
<VRating v-model="mediaDetail.vote_average" density="compact" length="10" class="ma-2" readonly /> <VRating v-model="mediaDetail.vote_average" density="compact" length="10" class="ma-2" readonly />
</div> </div>
<div v-if="mediaDetail.douban_id" class="media-fact"> <div v-if="mediaDetail.douban_id" class="media-fact">
<span>豆瓣ID</span> <span>{{ t('media.info.doubanId') }}</span>
<span class="media-fact-value">{{ mediaDetail.douban_id }}</span> <span class="media-fact-value">{{ mediaDetail.douban_id }}</span>
</div> </div>
<div v-if="mediaDetail.original_title" class="media-fact"> <div v-if="mediaDetail.original_title" class="media-fact">
<span>原始标题</span> <span>{{ t('media.info.originalTitle') }}</span>
<span class="media-fact-value">{{ mediaDetail.original_title }}</span> <span class="media-fact-value">{{ mediaDetail.original_title }}</span>
</div> </div>
<div v-if="mediaDetail.release_date" class="media-fact"> <div v-if="mediaDetail.release_date" class="media-fact">
<span>上映日期</span> <span>{{ t('media.info.releaseDate') }}</span>
<span class="media-fact-value"> <span class="media-fact-value">
{{ mediaDetail.release_date }} {{ mediaDetail.release_date }}
</span> </span>
</div> </div>
<div v-if="mediaDetail.production_countries" class="media-fact border-b-0"> <div v-if="mediaDetail.production_countries" class="media-fact border-b-0">
<span>出品国家</span> <span>{{ t('media.info.productionCountries') }}</span>
<span class="media-fact-value"> <span class="media-fact-value">
<span <span
v-for="country in getProductionCountries" v-for="country in getProductionCountries"
@@ -884,11 +894,11 @@ onBeforeMount(() => {
<span class="media-fact-value">{{ mediaDetail.bangumi_id }}</span> <span class="media-fact-value">{{ mediaDetail.bangumi_id }}</span>
</div> </div>
<div v-if="mediaDetail.original_title" class="media-fact"> <div v-if="mediaDetail.original_title" class="media-fact">
<span>原始标题</span> <span>{{ t('media.info.originalTitle') }}</span>
<span class="media-fact-value">{{ mediaDetail.original_title }}</span> <span class="media-fact-value">{{ mediaDetail.original_title }}</span>
</div> </div>
<div v-if="mediaDetail.release_date" class="media-fact border-b-0"> <div v-if="mediaDetail.release_date" class="media-fact border-b-0">
<span>上映日期</span> <span>{{ t('media.info.releaseDate') }}</span>
<span class="media-fact-value"> <span class="media-fact-value">
{{ mediaDetail.release_date }} {{ mediaDetail.release_date }}
</span> </span>
@@ -899,53 +909,61 @@ onBeforeMount(() => {
<div v-if="mediaDetail.tmdb_id"> <div v-if="mediaDetail.tmdb_id">
<PersonCardSlideView <PersonCardSlideView
:apipath="`tmdb/credits/${mediaDetail.tmdb_id}/${mediaProps.type}`" :apipath="`tmdb/credits/${mediaDetail.tmdb_id}/${mediaProps.type}`"
:linkurl="`/credits/tmdb/credits/${mediaDetail.tmdb_id}/${mediaProps.type}?title=演员阵容&type=tmdb`" :linkurl="`/credits/tmdb/credits/${mediaDetail.tmdb_id}/${mediaProps.type}?title=${t(
title="演员阵容" 'media.castAndCrew',
)}&type=tmdb`"
:title="t('media.castAndCrew')"
type="tmdb" type="tmdb"
/> />
</div> </div>
<div v-else-if="mediaDetail.douban_id"> <div v-else-if="mediaDetail.douban_id">
<PersonCardSlideView <PersonCardSlideView
:apipath="`douban/credits/${mediaDetail.douban_id}/${mediaProps.type}`" :apipath="`douban/credits/${mediaDetail.douban_id}/${mediaProps.type}`"
:linkurl="`/credits/douban/credits/${mediaDetail.douban_id}/${mediaProps.type}?title=演员阵容&type=douban`" :linkurl="`/credits/douban/credits/${mediaDetail.douban_id}/${mediaProps.type}?title=${t(
title="演员阵容" 'media.castAndCrew',
)}&type=douban`"
:title="t('media.castAndCrew')"
type="douban" type="douban"
/> />
</div> </div>
<div v-else-if="mediaDetail.bangumi_id"> <div v-else-if="mediaDetail.bangumi_id">
<PersonCardSlideView <PersonCardSlideView
:apipath="`bangumi/credits/${mediaDetail.bangumi_id}`" :apipath="`bangumi/credits/${mediaDetail.bangumi_id}`"
:linkurl="`/credits/bangumi/credits/${mediaDetail.bangumi_id}?title=演员阵容&type=bangumi`" :linkurl="`/credits/bangumi/credits/${mediaDetail.bangumi_id}?title=${t('media.castAndCrew')}&type=bangumi`"
title="演员阵容" :title="t('media.castAndCrew')"
type="bangumi" type="bangumi"
/> />
</div> </div>
<div v-if="mediaDetail.tmdb_id"> <div v-if="mediaDetail.tmdb_id">
<MediaCardSlideView <MediaCardSlideView
:apipath="`tmdb/recommend/${mediaDetail.tmdb_id}/${mediaProps.type}`" :apipath="`tmdb/recommend/${mediaDetail.tmdb_id}/${mediaProps.type}`"
:linkurl="`/browse/tmdb/recommend/${mediaDetail.tmdb_id}/${mediaProps.type}?title=推荐`" :linkurl="`/browse/tmdb/recommend/${mediaDetail.tmdb_id}/${mediaProps.type}?title=${t(
title="推荐" 'media.recommendations',
)}`"
:title="t('media.recommendations')"
/> />
</div> </div>
<div v-else-if="mediaDetail.douban_id"> <div v-else-if="mediaDetail.douban_id">
<MediaCardSlideView <MediaCardSlideView
:apipath="`douban/recommend/${mediaDetail.douban_id}/${mediaProps.type}`" :apipath="`douban/recommend/${mediaDetail.douban_id}/${mediaProps.type}`"
:linkurl="`/browse/douban/recommend/${mediaDetail.douban_id}/${mediaProps.type}?title=推荐`" :linkurl="`/browse/douban/recommend/${mediaDetail.douban_id}/${mediaProps.type}?title=${t(
title="推荐" 'media.recommendations',
)}`"
:title="t('media.recommendations')"
/> />
</div> </div>
<div v-else-if="mediaDetail.bangumi_id"> <div v-else-if="mediaDetail.bangumi_id">
<MediaCardSlideView <MediaCardSlideView
:apipath="`bangumi/recommend/${mediaDetail.bangumi_id}`" :apipath="`bangumi/recommend/${mediaDetail.bangumi_id}`"
:linkurl="`/browse/bangumi/recommend/${mediaDetail.bangumi_id}?title=推荐`" :linkurl="`/browse/bangumi/recommend/${mediaDetail.bangumi_id}?title=${t('media.recommendations')}`"
title="推荐" :title="t('media.recommendations')"
/> />
</div> </div>
<div v-if="mediaDetail.tmdb_id"> <div v-if="mediaDetail.tmdb_id">
<MediaCardSlideView <MediaCardSlideView
:apipath="`tmdb/similar/${mediaDetail.tmdb_id}/${mediaProps.type}`" :apipath="`tmdb/similar/${mediaDetail.tmdb_id}/${mediaProps.type}`"
:linkurl="`/browse/tmdb/similar/${mediaDetail.tmdb_id}/${mediaProps.type}?title=类似`" :linkurl="`/browse/tmdb/similar/${mediaDetail.tmdb_id}/${mediaProps.type}?title=${t('media.similar')}`"
title="类似" :title="t('media.similar')"
/> />
</div> </div>
</div> </div>
@@ -953,8 +971,8 @@ onBeforeMount(() => {
<NoDataFound <NoDataFound
v-if="!mediaDetail.tmdb_id && !mediaDetail.douban_id && !mediaDetail.bangumi_id && isRefreshed" v-if="!mediaDetail.tmdb_id && !mediaDetail.douban_id && !mediaDetail.bangumi_id && isRefreshed"
error-code="500" error-code="500"
error-title="出错啦!" :error-title="t('media.error.title')"
error-description="未识别到媒体信息。" :error-description="t('media.error.noMediaInfo')"
/> />
<!-- 订阅编辑弹窗 --> <!-- 订阅编辑弹窗 -->
<SubscribeEditDialog <SubscribeEditDialog