feat: 优化管理后台页面UI、修复TS编译错误、新增人生事件模块

- 优化 AI 配置列表页面:重构统计卡片、搜索表单、表格列展示
- 修复 3 处 TypeScript TS6133 编译错误,恢复构建
- 新增管理员修改密码和重置密码功能
- 优化小程序多个页面样式和交互
- 人生事件模块完善

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-10 23:23:09 +08:00
parent 60c63850ee
commit 755059807a
62 changed files with 4661 additions and 3019 deletions
+335 -103
View File
@@ -16,7 +16,10 @@
<view class="mode-tabs kos-card">
<view class="mode-tab" :class="{ active: mode === 'inspiration' }" @click="mode = 'inspiration'">
<text class="tab-icon"></text>
<view class="tab-icon sparkles-mini">
<view class="spark main"></view>
<view class="spark dot"></view>
</view>
<view>
<text class="tab-title">灵感模式</text>
<text class="tab-sub">一句话生成爽文</text>
@@ -34,37 +37,37 @@
<view v-if="mode === 'inspiration'" class="prompt-panel kos-card">
<view class="panel-head">
<view class="panel-title-row">
<text class="spark"></text>
<view class="panel-sparkles">
<view class="panel-spark large"></view>
<view class="panel-spark small"></view>
</view>
<text class="panel-title">写下你想要的故事设定或人生想法</text>
</view>
<view class="help-chip" @click="shuffleInspirations">不知道写什么?</view>
<view class="help-chip" @click="shuffleInspirations">
<view class="bulb-icon"></view>
<text>不知道写什么?</text>
</view>
</view>
<textarea
class="prompt-box"
maxlength="500"
v-model="prompt"
placeholder="例如:&#10;“如果我没有分手,现在会怎样?”&#10;“我成为顶级作曲家的人生”&#10;“重生回18岁改变一切”&#10;“从小镇做题家到世界首富”"
:placeholder="promptPlaceholder"
placeholder-class="placeholder"
/>
<view class="prompt-tools">
<view class="tool-pill kos-pill">语音输入</view>
<text class="counter">{{ prompt.length }}/500</text>
<view class="tool-pill kos-pill" @click="shuffleInspirations">随机灵感</view>
</view>
<scroll-view class="style-scroll" scroll-x :show-scrollbar="false">
<view class="style-row">
<text
v-for="item in styleOptions"
:key="item.value"
class="style-chip kos-pill"
:class="{ active: style === item.value }"
@click="style = item.value"
>{{ item.label }}</text>
<view class="tool-pill kos-pill" @click="handleVoiceInput">
<view class="mic-icon"></view>
<text>语音输入</text>
</view>
</scroll-view>
<text class="counter">{{ prompt.length }}/500</text>
<view class="tool-pill kos-pill" @click="shuffleInspirations">
<view class="refresh-icon"></view>
<text>随机灵感</text>
</view>
</view>
<button class="generate-btn kos-primary" :disabled="generating || !prompt.trim()" :loading="generating" @click="generateByPrompt">
<view>
@@ -72,7 +75,11 @@
<text class="generate-sub">今日剩余生成次数{{ remainingCount }}</text>
</view>
<view class="planet-badge">
<view></view>
<view class="planet-face">
<view class="planet-eye left"></view>
<view class="planet-eye right"></view>
<view class="planet-mouth"></view>
</view>
</view>
</button>
</view>
@@ -171,6 +178,13 @@ const generating = ref(false)
const remainingCount = ref(3)
const style = ref('career')
const randomRecommendations = ref([])
const promptPlaceholder = [
'例如:',
'“如果我没有分手,现在会怎样?”',
'“我成为顶级作曲家的人生”',
'“重生回18岁改变一切”',
'“从小镇做题家到世界首富”'
].join('\n')
const custom = reactive({
theme: '',
style: 'career',
@@ -249,6 +263,7 @@ const generateByPrompt = async () => {
}
prompt.value = ''
if (typeof res.data?.remainingCount === 'number') remainingCount.value = res.data.remainingCount
mode.value = 'list'
uni.showToast({ title: '剧本已生成', icon: 'success' })
}
@@ -286,14 +301,28 @@ const selectScript = async (id) => {
}
uni.navigateTo({ url: '/pages/main/PathView' })
}
const handleVoiceInput = () => {
uni.showModal({
title: '语音输入',
content: '语音识别入口已保留。后续接入微信录音和AI语音转文字后,会把识别结果自动填入灵感输入框。',
confirmText: '填入示例',
cancelText: '知道了',
success: (res) => {
if (res.confirm && !prompt.value.trim()) {
prompt.value = '如果我能重新选择一次,我想把人生过成更勇敢的版本'
}
}
})
}
</script>
<style scoped>
.script-view {
display: flex;
flex-direction: column;
gap: 28rpx;
padding-bottom: 32rpx;
gap: 24rpx;
padding-bottom: 24rpx;
}
.page-head,
@@ -313,7 +342,7 @@ const selectScript = async (id) => {
.title {
color: #fff;
font-size: 46rpx;
font-size: 40rpx;
line-height: 1.08;
font-weight: 900;
}
@@ -326,23 +355,24 @@ const selectScript = async (id) => {
.subtitle {
display: block;
margin-top: 16rpx;
margin-top: 14rpx;
color: rgba(219, 203, 247, 0.74);
font-size: 27rpx;
font-size: 24rpx;
}
.script-list-btn,
.help-chip,
.tool-pill,
.rewrite-btn {
height: 60rpx;
padding: 0 22rpx;
height: 56rpx;
padding: 0 20rpx;
border-radius: 999rpx;
display: flex;
align-items: center;
justify-content: center;
color: #caa0ff;
font-size: 24rpx;
font-size: 22rpx;
gap: 9rpx;
}
.list-icon {
@@ -354,7 +384,7 @@ const selectScript = async (id) => {
}
.mode-tabs {
height: 94rpx;
height: 88rpx;
border-radius: 28rpx;
padding: 6rpx;
display: grid;
@@ -366,27 +396,209 @@ const selectScript = async (id) => {
display: flex;
align-items: center;
justify-content: center;
gap: 16rpx;
gap: 14rpx;
color: rgba(216, 207, 238, 0.62);
}
.mode-tab.active {
color: #fff;
border: 1rpx solid rgba(191, 91, 255, 0.85);
background: rgba(115, 45, 255, 0.2);
box-shadow: 0 0 30rpx rgba(167, 60, 255, 0.5);
background:
radial-gradient(circle at 18% 12%, rgba(208, 118, 255, 0.22), transparent 42%),
rgba(115, 45, 255, 0.22);
box-shadow: 0 0 30rpx rgba(167, 60, 255, 0.48), inset 0 0 22rpx rgba(190, 92, 255, 0.18);
}
.tab-icon {
position: relative;
width: 34rpx;
height: 34rpx;
color: #d875ff;
font-size: 34rpx;
flex-shrink: 0;
}
.gear-icon {
position: relative;
width: 30rpx;
height: 30rpx;
border: 3rpx solid currentColor;
border-radius: 50%;
box-sizing: border-box;
color: currentColor;
}
.gear-icon::before {
content: '';
position: absolute;
left: 8rpx;
top: 8rpx;
width: 8rpx;
height: 8rpx;
border-radius: 50%;
background: currentColor;
}
.spark,
.panel-spark,
.book-sparkle {
position: absolute;
transform: rotate(45deg);
}
.spark::before,
.spark::after,
.panel-spark::before,
.panel-spark::after {
content: '';
position: absolute;
left: 50%;
top: 50%;
border-radius: 999rpx;
background: currentColor;
transform: translate(-50%, -50%);
}
.spark::before,
.panel-spark::before {
width: 4rpx;
height: 100%;
}
.spark::after,
.panel-spark::after {
width: 100%;
height: 4rpx;
}
.spark.main {
left: 8rpx;
top: 7rpx;
width: 19rpx;
height: 19rpx;
}
.spark.dot {
right: 3rpx;
top: 2rpx;
width: 10rpx;
height: 10rpx;
opacity: 0.8;
}
.panel-sparkles {
position: relative;
width: 38rpx;
height: 38rpx;
flex-shrink: 0;
color: #cf78ff;
}
.panel-spark.large {
left: 9rpx;
top: 8rpx;
width: 24rpx;
height: 24rpx;
}
.panel-spark.small {
left: 1rpx;
bottom: 3rpx;
width: 12rpx;
height: 12rpx;
opacity: 0.78;
}
.bulb-icon {
position: relative;
width: 26rpx;
height: 30rpx;
flex-shrink: 0;
}
.bulb-icon::before {
content: '';
position: absolute;
left: 5rpx;
top: 2rpx;
width: 16rpx;
height: 18rpx;
border: 3rpx solid currentColor;
border-radius: 50% 50% 45% 45%;
box-sizing: border-box;
}
.bulb-icon::after {
content: '';
position: absolute;
left: 9rpx;
bottom: 2rpx;
width: 10rpx;
height: 8rpx;
border-top: 3rpx solid currentColor;
border-bottom: 3rpx solid currentColor;
box-sizing: border-box;
}
.mic-icon {
position: relative;
width: 24rpx;
height: 30rpx;
flex-shrink: 0;
}
.mic-icon::before {
content: '';
position: absolute;
left: 7rpx;
top: 1rpx;
width: 10rpx;
height: 19rpx;
border: 3rpx solid currentColor;
border-radius: 999rpx;
box-sizing: border-box;
}
.mic-icon::after {
content: '';
position: absolute;
left: 4rpx;
bottom: 1rpx;
width: 16rpx;
height: 11rpx;
border: 3rpx solid currentColor;
border-top: 0;
border-radius: 0 0 999rpx 999rpx;
box-sizing: border-box;
}
.refresh-icon {
position: relative;
width: 28rpx;
height: 28rpx;
border: 4rpx solid currentColor;
flex-shrink: 0;
}
.refresh-icon::before {
content: '';
position: absolute;
inset: 5rpx;
border: 3rpx solid currentColor;
border-left-color: transparent;
border-radius: 50%;
transform: rotate(-28deg);
}
.refresh-icon::after {
content: '';
position: absolute;
right: 3rpx;
top: 3rpx;
width: 0;
height: 0;
border-left: 8rpx solid currentColor;
border-top: 5rpx solid transparent;
border-bottom: 5rpx solid transparent;
transform: rotate(25deg);
}
.tab-title,
@@ -395,45 +607,51 @@ const selectScript = async (id) => {
}
.tab-title {
font-size: 27rpx;
font-size: 24rpx;
font-weight: 800;
}
.tab-sub {
margin-top: 4rpx;
font-size: 20rpx;
font-size: 19rpx;
color: rgba(224, 214, 243, 0.52);
}
.prompt-panel,
.custom-panel {
border-radius: 32rpx;
padding: 30rpx;
padding: 28rpx;
border-color: rgba(173, 84, 255, 0.36);
background:
radial-gradient(circle at 16% 0%, rgba(143, 64, 255, 0.18), transparent 42%),
rgba(12, 12, 42, 0.62);
box-shadow: inset 0 0 34rpx rgba(132, 56, 255, 0.14), 0 0 26rpx rgba(140, 55, 255, 0.12);
}
.panel-title-row {
display: flex;
align-items: center;
gap: 14rpx;
gap: 12rpx;
min-width: 0;
}
.panel-title {
color: #c783ff;
font-size: 29rpx;
font-size: 26rpx;
font-weight: 900;
}
.prompt-box {
width: 100%;
height: 216rpx;
height: 214rpx;
box-sizing: border-box;
margin-top: 28rpx;
padding: 30rpx;
margin-top: 26rpx;
padding: 26rpx 28rpx;
border-radius: 24rpx;
border: 1rpx solid rgba(151, 111, 255, 0.26);
background: rgba(13, 15, 43, 0.72);
background: rgba(12, 14, 42, 0.86);
color: #f7f1ff;
font-size: 27rpx;
font-size: 24rpx;
line-height: 1.55;
}
@@ -442,50 +660,28 @@ const selectScript = async (id) => {
grid-template-columns: 1fr auto 1fr;
gap: 16rpx;
align-items: center;
margin-top: 18rpx;
margin-top: 16rpx;
}
.counter {
color: rgba(222, 211, 240, 0.62);
font-size: 23rpx;
}
.style-scroll {
width: 100%;
margin-top: 20rpx;
white-space: nowrap;
}
.style-row {
display: inline-flex;
gap: 14rpx;
}
.style-chip {
display: inline-flex;
height: 52rpx;
padding: 0 20rpx;
border-radius: 999rpx;
align-items: center;
color: rgba(224, 214, 243, 0.66);
font-size: 22rpx;
}
.style-chip.active {
color: #fff;
background: rgba(152, 62, 255, 0.34);
border-color: rgba(191, 91, 255, 0.82);
}
.generate-btn {
width: 100%;
height: 104rpx;
margin-top: 28rpx;
margin-top: 24rpx;
border-radius: 28rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 28rpx;
overflow: visible;
background:
radial-gradient(circle at 92% 44%, rgba(244, 187, 255, 0.42), transparent 22%),
linear-gradient(135deg, #b64cff 0%, #762fff 48%, #4a20ff 100%);
box-shadow: 0 0 34rpx rgba(168, 85, 247, 0.58), inset 0 1rpx 0 rgba(255, 255, 255, 0.25);
}
.generate-title,
@@ -495,38 +691,72 @@ const selectScript = async (id) => {
}
.generate-title {
font-size: 30rpx;
font-size: 28rpx;
font-weight: 900;
}
.generate-sub {
margin-top: 8rpx;
font-size: 22rpx;
font-size: 21rpx;
color: rgba(255, 255, 255, 0.74);
}
.planet-badge {
position: relative;
width: 68rpx;
height: 68rpx;
width: 78rpx;
height: 78rpx;
border-radius: 50%;
background: radial-gradient(circle, #e8b7ff, #8b37ff 62%, #4c1d95);
box-shadow: 0 0 24rpx rgba(219, 143, 255, 0.7);
background: radial-gradient(circle at 36% 28%, #f0c7ff, #a64cff 62%, #5a22d6);
box-shadow: 0 0 30rpx rgba(219, 143, 255, 0.82);
flex-shrink: 0;
}
.planet-badge::after {
content: '';
position: absolute;
left: -12rpx;
top: 27rpx;
width: 90rpx;
height: 20rpx;
left: -20rpx;
top: 34rpx;
width: 124rpx;
height: 24rpx;
border: 4rpx solid rgba(231, 201, 255, 0.72);
border-top-color: transparent;
border-radius: 50%;
transform: rotate(-18deg);
}
.planet-face {
position: absolute;
inset: 0;
z-index: 1;
}
.planet-eye {
position: absolute;
top: 31rpx;
width: 7rpx;
height: 12rpx;
border-radius: 999rpx;
background: #16062f;
}
.planet-eye.left {
left: 27rpx;
}
.planet-eye.right {
right: 27rpx;
}
.planet-mouth {
position: absolute;
left: 34rpx;
bottom: 23rpx;
width: 14rpx;
height: 9rpx;
border-bottom: 3rpx solid #16062f;
border-radius: 0 0 999rpx 999rpx;
}
.field {
margin-top: 24rpx;
}
@@ -577,43 +807,44 @@ const selectScript = async (id) => {
.section-title {
color: #c684ff;
font-size: 31rpx;
font-size: 28rpx;
font-weight: 900;
}
.refresh {
color: #c99fff;
font-size: 24rpx;
font-size: 22rpx;
}
.recommend-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 18rpx;
gap: 16rpx;
margin-top: 18rpx;
}
.recommend-card {
min-height: 118rpx;
min-height: 110rpx;
border-radius: 22rpx;
padding: 24rpx;
padding: 22rpx;
background: rgba(12, 12, 42, 0.54);
}
.recommend-text {
display: block;
color: rgba(248, 244, 255, 0.9);
font-size: 25rpx;
font-size: 22rpx;
line-height: 1.5;
}
.recommend-tag {
display: inline-flex;
margin-top: 18rpx;
padding: 8rpx 16rpx;
margin-top: 16rpx;
padding: 7rpx 14rpx;
border-radius: 999rpx;
color: #d985ff;
background: rgba(151, 66, 255, 0.18);
font-size: 21rpx;
font-size: 20rpx;
}
.recent-section,
@@ -625,8 +856,9 @@ const selectScript = async (id) => {
.script-card {
border-radius: 24rpx;
padding: 22rpx;
gap: 20rpx;
padding: 20rpx;
gap: 18rpx;
background: rgba(12, 12, 42, 0.6);
}
.script-card.selected {
@@ -634,15 +866,15 @@ const selectScript = async (id) => {
}
.script-cover {
width: 92rpx;
height: 92rpx;
width: 86rpx;
height: 86rpx;
flex-shrink: 0;
border-radius: 18rpx;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 42rpx;
font-size: 38rpx;
font-weight: 900;
background: linear-gradient(135deg, #7d35ff, #1bb7ff);
}
@@ -660,20 +892,20 @@ const selectScript = async (id) => {
.script-title {
color: #fff;
font-size: 27rpx;
font-size: 24rpx;
font-weight: 900;
}
.script-date {
margin-top: 8rpx;
margin-top: 7rpx;
color: rgba(218, 204, 243, 0.62);
font-size: 22rpx;
font-size: 21rpx;
}
.script-summary {
margin-top: 8rpx;
margin-top: 7rpx;
color: rgba(218, 204, 243, 0.68);
font-size: 23rpx;
font-size: 22rpx;
line-height: 1.45;
overflow: hidden;
text-overflow: ellipsis;
@@ -682,7 +914,7 @@ const selectScript = async (id) => {
.empty-panel {
border-radius: 24rpx;
padding: 28rpx;
padding: 26rpx;
color: rgba(230, 218, 250, 0.66);
}
</style>