feat: 修复 Redis 超时问题、固定小程序端口、新增人生事件模块及优化多个页面
- 修复 Redis 超时:添加 commons-pool2 依赖,启用 Lettuce 连接池,超时提升至 15s - 固定 mini-program H5 端口为 5175,避免与 web 项目端口冲突 - 新增人生事件(life-event)模块:表单和详情页面 - 新增 EpicScript 灵感接口(Controller/Service/DTO) - 优化登录、引导、主页、记录、剧本详情等多个页面 - 优化服务管理脚本和 Nginx 配置 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,404 @@
|
||||
<template>
|
||||
<view class="event-form-page">
|
||||
<view class="space-bg"></view>
|
||||
<view class="top-safe" :style="{ height: safeAreaTop + 'px' }"></view>
|
||||
|
||||
<view class="header">
|
||||
<text class="back" @click="goBack">‹</text>
|
||||
<view>
|
||||
<text class="title">记录人生经历 ✦</text>
|
||||
<text class="subtitle">记录每一个重要时刻,AI将帮你生成专属人生轨迹</text>
|
||||
</view>
|
||||
<text class="save" @click="submit">保存</text>
|
||||
</view>
|
||||
|
||||
<scroll-view class="content" scroll-y :show-scrollbar="false">
|
||||
<view class="form-card kos-card">
|
||||
<view class="group">
|
||||
<view class="group-title">
|
||||
<text class="group-icon">◷</text>
|
||||
<text>时间</text>
|
||||
</view>
|
||||
<view class="segmented">
|
||||
<text v-for="item in timeModes" :key="item" class="seg active-first">{{ item }}</text>
|
||||
</view>
|
||||
<picker mode="date" :value="form.time" @change="e => form.time = e.detail.value">
|
||||
<view class="date-picker field-box">
|
||||
<text>{{ formatDate(form.time) }}</text>
|
||||
<text class="chevron">›</text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<view class="group">
|
||||
<view class="label-row">
|
||||
<view class="group-title">
|
||||
<text class="group-icon">✎</text>
|
||||
<text>事件标题 ✦</text>
|
||||
</view>
|
||||
<text class="count">{{ form.title.length }}/30</text>
|
||||
</view>
|
||||
<input class="field-box input" maxlength="30" v-model="form.title" placeholder="给这段经历起个标题吧..." placeholder-class="placeholder" />
|
||||
</view>
|
||||
|
||||
<view class="group">
|
||||
<view class="label-row">
|
||||
<view class="group-title">
|
||||
<text class="group-icon">▣</text>
|
||||
<text>具体内容 ✦</text>
|
||||
</view>
|
||||
<text class="count">{{ form.content.length }}/500</text>
|
||||
</view>
|
||||
<view class="textarea-wrap">
|
||||
<textarea class="textarea" maxlength="500" v-model="form.content" placeholder="请详细记录这段经历的背景、发生的事情、你的感受和收获..." placeholder-class="placeholder" />
|
||||
<view class="ai-btn kos-pill" @click="assistWrite">✦ AI 帮我写</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="group">
|
||||
<view class="label-row">
|
||||
<view class="group-title">
|
||||
<text class="group-icon">♡</text>
|
||||
<text>相关标签</text>
|
||||
</view>
|
||||
<view class="custom-tag kos-pill">+ 自定义标签</view>
|
||||
</view>
|
||||
<view class="tag-grid">
|
||||
<text
|
||||
v-for="tag in tags"
|
||||
:key="tag"
|
||||
class="tag kos-pill"
|
||||
:class="{ active: form.tags.includes(tag) }"
|
||||
@click="toggleTag(tag)"
|
||||
>{{ tag }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<button class="submit kos-primary" :loading="saving" @click="submit">
|
||||
<text>✦ 提交记录</text>
|
||||
<text class="submit-sub">记录后可在时间轴查看</text>
|
||||
</button>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, onMounted } from 'vue'
|
||||
import { useAppStore } from '../../stores/app.js'
|
||||
|
||||
const store = useAppStore()
|
||||
const safeAreaTop = ref(20)
|
||||
const saving = ref(false)
|
||||
|
||||
const form = reactive({
|
||||
title: '',
|
||||
time: new Date().toISOString().slice(0, 10),
|
||||
content: '',
|
||||
tags: [],
|
||||
eventType: 'daily_log'
|
||||
})
|
||||
|
||||
const timeModes = ['具体日期', '年月', '季节', '时间轴范围']
|
||||
const tags = ['成长', '学习', '工作', '旅行', '感情', '家庭', '友情', '挑战', '突破', '收获', '感动', '迷茫']
|
||||
|
||||
onMounted(() => {
|
||||
const info = uni.getWindowInfo()
|
||||
safeAreaTop.value = info.safeAreaInsets?.top || info.statusBarHeight || 20
|
||||
})
|
||||
|
||||
const formatDate = (value) => {
|
||||
if (!value) return '选择具体日期'
|
||||
const [year, month, day] = value.split('-')
|
||||
return `${year}年${month}月${day}日`
|
||||
}
|
||||
|
||||
const toggleTag = (tag) => {
|
||||
const index = form.tags.indexOf(tag)
|
||||
if (index >= 0) form.tags.splice(index, 1)
|
||||
else form.tags.push(tag)
|
||||
}
|
||||
|
||||
const assistWrite = () => {
|
||||
if (!form.content) {
|
||||
form.content = '那一天,我清楚地感受到自己正在经历一次变化。事情本身也许并不宏大,但它让我重新看见了自己的选择、情绪和力量。'
|
||||
}
|
||||
}
|
||||
|
||||
const submit = async () => {
|
||||
if (!form.title || !form.content || saving.value) {
|
||||
uni.showToast({ title: '请填写标题和内容', icon: 'none' })
|
||||
return
|
||||
}
|
||||
saving.value = true
|
||||
const result = await store.createEvent({ ...form, aiFeedback: buildAiFeedback() })
|
||||
saving.value = false
|
||||
if (result.success) {
|
||||
uni.navigateBack()
|
||||
} else {
|
||||
uni.showToast({ title: result.error || '保存失败', icon: 'none' })
|
||||
}
|
||||
}
|
||||
|
||||
const buildAiFeedback = () => {
|
||||
return '这段经历体现了你的自我观察能力。它可能意味着你正在从旧的反应模式里走出来,开始更主动地理解自己的处境。建议保留当时的细节,因为这些细节会成为后续人生剧本的重要素材。'
|
||||
}
|
||||
|
||||
const goBack = () => {
|
||||
uni.navigateBack()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.event-form-page {
|
||||
position: relative;
|
||||
height: 100vh;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: #fff;
|
||||
background: #050615;
|
||||
}
|
||||
|
||||
.space-bg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background:
|
||||
radial-gradient(circle at 86% 88%, rgba(126, 57, 255, 0.34), transparent 30%),
|
||||
radial-gradient(circle at 18% 8%, rgba(63, 120, 255, 0.18), transparent 26%),
|
||||
linear-gradient(180deg, #07091d 0%, #07031a 52%, #04020e 100%);
|
||||
}
|
||||
|
||||
.top-safe,
|
||||
.header,
|
||||
.content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.top-safe,
|
||||
.header {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.header {
|
||||
min-height: 138rpx;
|
||||
display: grid;
|
||||
grid-template-columns: 72rpx 1fr 72rpx;
|
||||
align-items: center;
|
||||
padding: 0 28rpx;
|
||||
}
|
||||
|
||||
.back {
|
||||
color: #fff;
|
||||
font-size: 70rpx;
|
||||
}
|
||||
|
||||
.title,
|
||||
.subtitle {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #fff;
|
||||
font-size: 36rpx;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin-top: 14rpx;
|
||||
color: rgba(218, 204, 243, 0.72);
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.save {
|
||||
text-align: right;
|
||||
color: #c06dff;
|
||||
font-size: 27rpx;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
height: 0;
|
||||
min-height: 0;
|
||||
box-sizing: border-box;
|
||||
padding: 0 28rpx 34rpx;
|
||||
}
|
||||
|
||||
.form-card {
|
||||
border-radius: 34rpx;
|
||||
padding: 30rpx;
|
||||
}
|
||||
|
||||
.group + .group {
|
||||
margin-top: 34rpx;
|
||||
}
|
||||
|
||||
.group-title,
|
||||
.label-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.label-row {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.group-title {
|
||||
gap: 14rpx;
|
||||
color: #d4b7ff;
|
||||
font-size: 31rpx;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.group-icon {
|
||||
color: #a855ff;
|
||||
font-size: 38rpx;
|
||||
}
|
||||
|
||||
.count,
|
||||
.custom-tag {
|
||||
color: #b58bff;
|
||||
font-size: 23rpx;
|
||||
}
|
||||
|
||||
.custom-tag {
|
||||
height: 50rpx;
|
||||
padding: 0 18rpx;
|
||||
border-radius: 999rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.segmented {
|
||||
height: 66rpx;
|
||||
margin-top: 24rpx;
|
||||
border-radius: 999rpx;
|
||||
padding: 5rpx;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
border: 1rpx solid rgba(151, 111, 255, 0.26);
|
||||
background: rgba(11, 12, 38, 0.7);
|
||||
}
|
||||
|
||||
.seg {
|
||||
border-radius: 999rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: rgba(224, 214, 243, 0.72);
|
||||
font-size: 23rpx;
|
||||
}
|
||||
|
||||
.seg:first-child {
|
||||
color: #fff;
|
||||
background: linear-gradient(135deg, #a843ff, #7630ff);
|
||||
box-shadow: 0 0 24rpx rgba(168, 67, 255, 0.5);
|
||||
}
|
||||
|
||||
.field-box,
|
||||
.textarea {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
border-radius: 22rpx;
|
||||
border: 1rpx solid rgba(151, 111, 255, 0.24);
|
||||
background: rgba(12, 15, 46, 0.72);
|
||||
color: #fff;
|
||||
font-size: 27rpx;
|
||||
}
|
||||
|
||||
.date-picker {
|
||||
height: 78rpx;
|
||||
margin-top: 22rpx;
|
||||
padding: 0 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.chevron {
|
||||
color: rgba(224, 214, 243, 0.62);
|
||||
font-size: 52rpx;
|
||||
}
|
||||
|
||||
.input {
|
||||
height: 82rpx;
|
||||
margin-top: 18rpx;
|
||||
padding: 0 24rpx;
|
||||
}
|
||||
|
||||
.textarea-wrap {
|
||||
position: relative;
|
||||
margin-top: 18rpx;
|
||||
}
|
||||
|
||||
.textarea {
|
||||
height: 258rpx;
|
||||
padding: 24rpx;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.ai-btn {
|
||||
position: absolute;
|
||||
right: 18rpx;
|
||||
bottom: 18rpx;
|
||||
height: 54rpx;
|
||||
padding: 0 18rpx;
|
||||
border-radius: 999rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #d5b3ff;
|
||||
font-size: 23rpx;
|
||||
}
|
||||
|
||||
.tag-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: 16rpx;
|
||||
margin-top: 22rpx;
|
||||
}
|
||||
|
||||
.tag {
|
||||
height: 58rpx;
|
||||
border-radius: 18rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: rgba(224, 214, 243, 0.68);
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.tag.active {
|
||||
color: #fff;
|
||||
border-color: rgba(192, 100, 255, 0.84);
|
||||
background: rgba(137, 51, 255, 0.35);
|
||||
box-shadow: 0 0 22rpx rgba(168, 67, 255, 0.34);
|
||||
}
|
||||
|
||||
.submit {
|
||||
width: 100%;
|
||||
height: 112rpx;
|
||||
margin-top: 38rpx;
|
||||
border-radius: 28rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.submit text {
|
||||
display: block;
|
||||
color: #fff;
|
||||
font-size: 31rpx;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.submit-sub {
|
||||
margin-top: 8rpx;
|
||||
color: rgba(255, 255, 255, 0.72) !important;
|
||||
font-size: 23rpx !important;
|
||||
font-weight: 500 !important;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user