593 lines
13 KiB
Vue
593 lines
13 KiB
Vue
<script setup>
|
||
import { ref, onMounted, nextTick } from 'vue'
|
||
import { useRouter, useRoute } from 'vue-router'
|
||
import axios from 'axios'
|
||
|
||
const router = useRouter()
|
||
const route = useRoute()
|
||
|
||
// 响应式数据
|
||
const isExpanded = ref(false)
|
||
const loading = ref(false)
|
||
const dramaData = ref({
|
||
title: '',
|
||
mix_name: '',
|
||
mix_id: '',
|
||
classification_type: '', // 女频/玄等
|
||
episodes: '',
|
||
release_date: '', // 上线日期
|
||
cover_image_url: '',
|
||
desc: '', // 剧情介绍(使用desc字段)
|
||
Copyright_field: '', // 版权方
|
||
Manufacturing_Field: '', // 承制方
|
||
comments_summary: '' // 用户评论总结
|
||
})
|
||
|
||
// API基础URL
|
||
const API_BASE_URL = 'http://localhost:8443/api' // 本地服务器
|
||
|
||
// 返回上一页(直接返回首页)
|
||
const goBack = () => {
|
||
router.push('/')
|
||
}
|
||
|
||
// 切换展开/收起
|
||
const toggleExpanded = () => {
|
||
isExpanded.value = !isExpanded.value
|
||
}
|
||
|
||
// 处理带【】格式的评论总结,返回HTML
|
||
const formatCommentsSummary = (text, spacing = 1) => {
|
||
if (!text) return ''
|
||
|
||
// 简单方法:将【】加粗,然后在每个【前面加间距
|
||
let result = text.replace(/【([^】]+)】/g, '<strong>【$1】</strong>')
|
||
|
||
// 使用margin-top来精确控制间距(支持小数)
|
||
const spacingPx = spacing * 10 // spacing=1.5 -> 15px
|
||
|
||
// 在每个【前面添加带margin的div(除了第一个)
|
||
let isFirst = true
|
||
result = result.replace(/(<strong>【)/g, (match) => {
|
||
if (isFirst) {
|
||
isFirst = false
|
||
return match
|
||
}
|
||
return `<div style="margin-top: ${spacingPx}px">${match}`
|
||
})
|
||
|
||
// 为每个添加了开始div的地方添加结束div
|
||
const parts = result.split(/(<div style="margin-top: \d+px">)/)
|
||
let finalResult = ''
|
||
for (let i = 0; i < parts.length; i++) {
|
||
if (parts[i].includes('margin-top')) {
|
||
finalResult += parts[i]
|
||
if (i + 1 < parts.length) {
|
||
finalResult += parts[i + 1] + '</div>'
|
||
i++ // 跳过下一个part
|
||
}
|
||
} else {
|
||
finalResult += parts[i]
|
||
}
|
||
}
|
||
|
||
return finalResult
|
||
}
|
||
|
||
// 获取短剧详细信息
|
||
const fetchDramaDetail = async (dramaId) => {
|
||
loading.value = true
|
||
try {
|
||
const response = await axios.get(`${API_BASE_URL}/rank/drama/${dramaId}`)
|
||
|
||
if (response.data.success) {
|
||
const data = response.data.data
|
||
dramaData.value = {
|
||
title: data.mix_name || data.title || '',
|
||
mix_name: data.mix_name || '',
|
||
mix_id: data.mix_id || '',
|
||
classification_type: data.classification_type || '',
|
||
episodes: data.updated_to_episode ? `${data.updated_to_episode}集` : '',
|
||
release_date: data.release_date || '',
|
||
cover_image_url: data.cover_image_url || '',
|
||
desc: data.desc || '', // 使用desc字段作为剧情介绍
|
||
Copyright_field: data.Copyright_field || '',
|
||
Manufacturing_Field: data.Manufacturing_Field || '',
|
||
comments_summary: data.comments_summary || '',
|
||
series_author: data.series_author || ''
|
||
}
|
||
|
||
// 如果URL中有hash参数,滚动到对应位置
|
||
if (route.hash) {
|
||
await nextTick()
|
||
const element = document.querySelector(route.hash)
|
||
if (element) {
|
||
element.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||
}
|
||
}
|
||
} else {
|
||
console.error('获取短剧详情失败:', response.data.message)
|
||
}
|
||
} catch (error) {
|
||
console.error('API调用失败:', error)
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
// 跳转到认领页面
|
||
const goToClaim = (type) => {
|
||
// type: 'copyright' 或 'manufacturing'
|
||
const dramaId = route.params.id
|
||
if (dramaId) {
|
||
router.push(`/claim/${dramaId}?type=${type}`)
|
||
} else {
|
||
console.error('无法获取短剧ID')
|
||
}
|
||
}
|
||
|
||
// 页面加载时获取短剧数据
|
||
onMounted(() => {
|
||
// 从路由参数获取短剧ID
|
||
const dramaId = route.params.id
|
||
console.log('短剧详情页,ID:', dramaId)
|
||
|
||
if (dramaId) {
|
||
fetchDramaDetail(dramaId)
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<template>
|
||
<div class="page">
|
||
<div class="card">
|
||
<!-- 顶部导航栏 -->
|
||
<div class="header">
|
||
<button class="icon-button" @click="goBack">
|
||
<svg viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" fill="none" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||
<polyline points="15 18 9 12 15 6" />
|
||
</svg>
|
||
</button>
|
||
<p class="title-header">短剧详情</p>
|
||
<div class="header-spacer" />
|
||
</div>
|
||
|
||
<!-- 内容区域 -->
|
||
<div class="content">
|
||
<!-- 加载状态 -->
|
||
<div v-if="loading" class="loading">
|
||
<div class="loading-spinner"></div>
|
||
<p>加载中...</p>
|
||
</div>
|
||
|
||
<!-- 短剧基本信息 -->
|
||
<div v-else class="block">
|
||
<div class="drama-info">
|
||
<div class="cover">
|
||
<img
|
||
:src="dramaData.cover_image_url || '/placeholder-poster.svg'"
|
||
:alt="dramaData.title"
|
||
class="cover-img"
|
||
/>
|
||
</div>
|
||
<div class="info">
|
||
<div class="title-row">
|
||
<p class="title">{{ dramaData.title || '短剧名称' }}</p>
|
||
</div>
|
||
<p class="small-text">
|
||
<span class="field-label">类型/元素:</span>
|
||
<span class="field-value">{{ dramaData.classification_type || '' }}</span>
|
||
</p>
|
||
<p class="small-text">
|
||
<span class="field-label">集数:</span>
|
||
<span class="field-value">{{ dramaData.episodes || '' }}</span>
|
||
</p>
|
||
<p class="small-text">
|
||
<span class="field-label">上线日期:</span>
|
||
<span class="field-value">{{ dramaData.release_date || '' }}</span>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 短剧介绍 -->
|
||
<div class="description">
|
||
<p class="description-label">剧情介绍:</p>
|
||
<p class="description-text" v-if="dramaData.desc">
|
||
{{ isExpanded ? dramaData.desc : (dramaData.desc.length > 100 ? dramaData.desc.substring(0, 100) + '...' : dramaData.desc) }}
|
||
</p>
|
||
<p class="description-text description-empty" v-else></p>
|
||
<button v-if="dramaData.desc && dramaData.desc.length > 100" class="toggle-btn" @click="toggleExpanded">
|
||
{{ isExpanded ? '收起' : '展开' }}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 关联方信息 -->
|
||
<div v-if="!loading" class="block">
|
||
<p class="section-title">关联方</p>
|
||
|
||
<div class="assoc-group">
|
||
<p class="label">版权方</p>
|
||
<div class="assoc-row">
|
||
<div>
|
||
<span v-if="dramaData.Copyright_field" class="chip-blue">{{ dramaData.Copyright_field }}</span>
|
||
<span v-else class="chip-empty"></span>
|
||
</div>
|
||
<p v-if="!dramaData.Copyright_field" class="claim" @click="goToClaim('copyright')">我要认领</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="assoc-group">
|
||
<p class="label">承制方</p>
|
||
<div class="assoc-row">
|
||
<div>
|
||
<span v-if="dramaData.Manufacturing_Field" class="chip-red">{{ dramaData.Manufacturing_Field }}</span>
|
||
<span v-else class="chip-empty"></span>
|
||
</div>
|
||
<p v-if="!dramaData.Manufacturing_Field" class="claim" @click="goToClaim('manufacturing')">我要认领</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 用户评论 -->
|
||
<div v-if="!loading" id="comments" class="block">
|
||
<p class="section-title-sm">抖音用户整体评论</p>
|
||
<div class="comment-row">
|
||
<img
|
||
src="https://oss.xintiao85.com/story/materials/image/6912ff82dc05328e25f9442c/1762926632_cfc62d9f.png"
|
||
alt="用户评论"
|
||
class="avatar"
|
||
/>
|
||
<div class="comment-text" v-if="dramaData.comments_summary" v-html="formatCommentsSummary(dramaData.comments_summary, 1.5)"></div>
|
||
<p class="comment-text comment-empty" v-else></p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
* {
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.page {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
background: #ebedf2;
|
||
padding: 14px;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
.card {
|
||
display: flex;
|
||
flex-direction: column;
|
||
border-radius: 0;
|
||
overflow: hidden;
|
||
background: #f3f4f6;
|
||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.08);
|
||
width: 100%;
|
||
max-width: 428px;
|
||
}
|
||
|
||
.header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
background: #ffffff;
|
||
padding: 12px 16px;
|
||
}
|
||
|
||
.icon-button {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 4px;
|
||
color: #111827;
|
||
background: none;
|
||
border: none;
|
||
cursor: pointer;
|
||
transition: opacity 0.2s;
|
||
}
|
||
|
||
.icon-button:hover {
|
||
opacity: 0.7;
|
||
}
|
||
|
||
.icon-button-sm {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 4px;
|
||
background: none;
|
||
border: none;
|
||
cursor: pointer;
|
||
transition: opacity 0.2s;
|
||
}
|
||
|
||
.icon-button-sm:hover {
|
||
opacity: 0.7;
|
||
}
|
||
|
||
.title-header {
|
||
color: #1f2937;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
margin: 0;
|
||
}
|
||
|
||
.header-spacer {
|
||
width: 24px;
|
||
height: 24px;
|
||
}
|
||
|
||
.content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 16px;
|
||
padding: 16px;
|
||
}
|
||
|
||
.block {
|
||
background: #ffffff;
|
||
border-radius: 12px;
|
||
padding: 20px;
|
||
}
|
||
|
||
.drama-info {
|
||
display: flex;
|
||
gap: 16px;
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.cover {
|
||
width: 84px;
|
||
height: 112px;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
background: #fce7f3;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.cover-img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.info {
|
||
flex: 1;
|
||
}
|
||
|
||
.title-row {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
justify-content: space-between;
|
||
margin-bottom: 26px;
|
||
}
|
||
|
||
.title {
|
||
color: #111827;
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
margin: 0;
|
||
}
|
||
|
||
.small-text {
|
||
font-size: 12px;
|
||
color: #6b7280;
|
||
margin: 8px 0;
|
||
}
|
||
|
||
.description {
|
||
display: flex;
|
||
flex-direction: column;
|
||
margin-top: 16px;
|
||
}
|
||
|
||
.description-text {
|
||
color: #4b5563;
|
||
font-size: 14px;
|
||
margin-bottom: 6px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.description-empty {
|
||
color: #9ca3af;
|
||
min-height: 20px;
|
||
}
|
||
|
||
.toggle-btn {
|
||
color: #3b82f6;
|
||
font-size: 12px;
|
||
background: transparent;
|
||
border: none;
|
||
padding: 0;
|
||
cursor: pointer;
|
||
text-align: left;
|
||
transition: opacity 0.2s;
|
||
}
|
||
|
||
.toggle-btn:hover {
|
||
opacity: 0.7;
|
||
}
|
||
|
||
.section-title {
|
||
color: #111827;
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
margin: 0 0 16px 0;
|
||
padding-bottom: 12px;
|
||
border-bottom: 1px solid #e5e7eb;
|
||
}
|
||
|
||
.assoc-group {
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.assoc-group:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.assoc-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-top: 8px;
|
||
}
|
||
|
||
.label {
|
||
color: #374151;
|
||
font-size: 14px;
|
||
margin: 0 0 0 0;
|
||
}
|
||
|
||
.claim {
|
||
color: #ef4444;
|
||
font-size: 14px;
|
||
margin: 0;
|
||
cursor: pointer;
|
||
transition: opacity 0.2s;
|
||
}
|
||
|
||
.claim:hover {
|
||
opacity: 0.7;
|
||
}
|
||
|
||
.chip-blue {
|
||
display: inline-block;
|
||
padding: 6px 14px;
|
||
border-radius: 8px;
|
||
background: #eff6ff;
|
||
color: #2563eb;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.chip-red {
|
||
display: inline-block;
|
||
padding: 6px 14px;
|
||
border-radius: 8px;
|
||
background: #fef2f2;
|
||
color: #dc2626;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.chip-green {
|
||
display: inline-block;
|
||
padding: 6px 14px;
|
||
border-radius: 8px;
|
||
background: #ecfdf5;
|
||
color: #059669;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.section-title-sm {
|
||
color: #1f2937;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
margin: 0 0 12px 0;
|
||
padding: 16px 0 12px 0;
|
||
border-bottom: 1px solid #e5e7eb;
|
||
}
|
||
|
||
.qr-box {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 128px;
|
||
height: 128px;
|
||
margin: 0 auto;
|
||
border: 1px solid #d1d5db;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.qr-img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.comment-row {
|
||
display: flex;
|
||
gap: 12px;
|
||
}
|
||
|
||
.avatar {
|
||
width: 32px;
|
||
height: 32px;
|
||
border-radius: 6px;
|
||
object-fit: cover;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.comment-text {
|
||
font-size: 13px;
|
||
color: #4b5563;
|
||
margin: 0;
|
||
line-height: 2;
|
||
}
|
||
|
||
.comment-text strong {
|
||
font-weight: 600;
|
||
color: #1f2937;
|
||
}
|
||
|
||
.comment-empty {
|
||
color: #9ca3af;
|
||
font-style: italic;
|
||
}
|
||
|
||
.chip-empty {
|
||
display: inline-block;
|
||
padding: 4px 10px;
|
||
border-radius: 8px;
|
||
background: #f3f4f6;
|
||
color: #9ca3af;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.description-label {
|
||
color: #374151;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
margin: 0 0 8px 0;
|
||
}
|
||
|
||
/* 加载状态 */
|
||
.loading {
|
||
text-align: center;
|
||
padding: 40px 20px;
|
||
color: #666;
|
||
}
|
||
|
||
.loading-spinner {
|
||
width: 40px;
|
||
height: 40px;
|
||
border: 4px solid #f3f3f3;
|
||
border-top: 4px solid #4a90e2;
|
||
border-radius: 50%;
|
||
animation: spin 1s linear infinite;
|
||
margin: 0 auto 20px;
|
||
}
|
||
|
||
@keyframes spin {
|
||
0% { transform: rotate(0deg); }
|
||
100% { transform: rotate(360deg); }
|
||
}
|
||
|
||
/* 响应式设计 */
|
||
@media (max-width: 480px) {
|
||
.page {
|
||
padding: 0;
|
||
}
|
||
|
||
.card {
|
||
max-width: 100%;
|
||
border-radius: 0;
|
||
}
|
||
}
|
||
</style>
|