feat: 前后台静态资源路径冲突修复 + 用户管理功能

- 修改 admin/vite.config.js 添加 base: '/admin/' 解决静态资源路径问题
- 修复后台 index.html 资源引用从 /assets/ → /admin/assets/
- 更新优化器默认 API 地址为服务器地址
- 添加友链管理相关代码
- 修复多处分类管理页面
This commit is contained in:
bwstudio
2026-06-10 07:30:49 +08:00
parent aecf1f1f15
commit 595394fcb7
22 changed files with 740 additions and 100 deletions
+89 -5
View File
@@ -7,11 +7,28 @@
<span class="tag tag-crowd" v-for="(name, i) in (joke.crowd_names||[])" :key="'c'+i">{{ name }}</span>
</div>
<!-- 标题 -->
<h1 class="detail-title">{{ joke.title }}</h1>
<!-- 标题 + AI评价 -->
<div class="title-row">
<h1 class="detail-title">{{ joke.title }}</h1>
<span v-if="joke.ai_level" class="ai-badge" :class="'ai-' + joke.ai_level">
{{ getLevelText(joke.ai_level) }}
</span>
</div>
<!-- 内容 -->
<div class="detail-content">{{ joke.content }}</div>
<!-- 内容优先显示润色内容 -->
<div class="detail-content">
<template v-if="joke.polished_content">
<div class="polished-label"> AI 润色版</div>
{{ joke.polished_content }}
</template>
<template v-else>{{ joke.content }}</template>
</div>
<!-- 原始内容如果有润色 -->
<details v-if="joke.polished_content" class="original-content">
<summary>查看原文</summary>
{{ joke.content }}
</details>
<!-- 操作栏 -->
<div class="detail-actions">
@@ -33,6 +50,8 @@
</button>
<span class="sep">|</span>
<span>{{ formatDate(joke.created_at) }}</span>
<span v-if="joke.ai_score" class="sep">|</span>
<span v-if="joke.ai_score" class="ai-score">评分: {{ joke.ai_score.toFixed(1) }}</span>
</div>
</div>
</div>
@@ -106,6 +125,16 @@ const formatDate = (dateStr) => {
return new Date(dateStr).toLocaleDateString('zh-CN', { year: 'numeric', month: 'long', day: 'numeric' })
}
function getLevelText(level) {
const map = {
'excellent': '⭐⭐⭐ 精品',
'good': '⭐⭐ 良好',
'ordinary': '⭐ 普通',
'poor': '⚠️ 待优化'
}
return map[level] || ''
}
onMounted(() => loadJoke())
watch(() => route.params.id, () => loadJoke())
@@ -131,7 +160,60 @@ watch(() => route.params.id, () => loadJoke())
transition: background 0.3s, box-shadow 0.3s;
}
.detail-tags { display: flex; gap: 10px; }
.detail-tags { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
/* 标题行:标题 + AI评价右对齐 */
.title-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
}
/* AI 评价徽章 */
.ai-badge {
font-size: 13px;
padding: 4px 14px;
border-radius: 14px;
font-weight: 600;
white-space: nowrap;
flex-shrink: 0;
}
.ai-excellent { background: linear-gradient(135deg, #fef3c7, #fde68a); color: #92400e; }
.ai-good { background: linear-gradient(135deg, #d1fae5, #a7f3d0); color: #065f46; }
.ai-ordinary { background: #f3f4f6; color: #4b5563; }
.ai-poor { background: #fee2e2; color: #991b1b; }
/* AI 润色标签 */
.polished-label {
font-size: 12px;
color: #9333ea;
font-weight: 600;
margin-bottom: 8px;
display: inline-block;
}
/* 原文折叠 */
.original-content {
margin-top: 16px;
padding: 12px;
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius);
font-size: 13px;
color: var(--text-muted);
}
.original-content summary {
cursor: pointer;
font-weight: 600;
color: var(--text-secondary);
}
/* AI 评分 */
.ai-score {
color: #9333ea;
font-weight: 600;
}
.detail-title {
font-size: 26px;
@@ -140,6 +222,8 @@ watch(() => route.params.id, () => loadJoke())
line-height: 1.4;
letter-spacing: 0.5px;
transition: color 0.3s;
margin: 0;
flex: 1;
}
.detail-content {