| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407 |
- <template>
- <view class="portrait-edit-page" :class="{ 'no-scroll': editing }" v-if="!editing">
- <!-- 五维画像(原我的页面内容迁移至此) -->
- <view class="section" v-if="portrait">
- <view class="section-header-row">
- <text class="section-title">🧭 用户画像</text>
- <text class="section-sub">{{ portrait.tierLabel }}</text>
- </view>
- <view class="portrait-summary">
- <text class="portrait-desc">{{ portrait.summary }}</text>
- </view>
- <view class="portrait-dimensions">
- <view class="portrait-dim-item" v-for="dim in portrait.dims" :key="dim.code">
- <view class="portrait-dim-header">
- <view class="portrait-dim-icon" :style="{ background: dim.color }">{{ dim.icon }}</view>
- <text class="portrait-dim-name">{{ dim.name }}</text>
- <text class="portrait-dim-score">{{ dim.score }}</text>
- </view>
- <view class="portrait-dim-bar-track">
- <view class="portrait-dim-bar-fill" :style="{ width: dim.score + '%', background: dim.color }"></view>
- </view>
- <text class="portrait-dim-desc">{{ dim.desc }}</text>
- </view>
- </view>
- <view class="portrait-tip" v-if="portrait.tip">
- <text class="portrait-tip-text">{{ portrait.tip }}</text>
- </view>
- </view>
- <!-- 画像描述(可编辑) -->
- <view class="section">
- <view class="section-header-row">
- <text class="section-title">我的画像描述</text>
- <text class="edit-btn" @click="enterEdit">编辑</text>
- </view>
- <view class="hint">AI 会根据这些信息提供更有针对性的建议(最多 2000 字)。每日自动基于最新使用情况更新。 </view>
- <view class="portrait-prompt-text" v-if="portraitPrompt">{{ portraitPrompt }}</view>
- <view class="portrait-prompt-empty" v-else>暂无画像描述,点击「编辑」填写或由 AI 自动生成</view>
- </view>
- <!-- 参考指标(自动生成) -->
- <view class="section" v-if="renderedSnapshot">
- <view class="section-title">参考指标(自动生成)</view>
- <view class="snapshot-text">{{ renderedSnapshot }}</view>
- </view>
- </view>
- <!-- 全屏编辑模式 -->
- <view class="editor-fullscreen" v-else>
- <view class="editor-topbar">
- <text class="editor-cancel" @click="cancelEdit">取消</text>
- <text class="editor-title">编辑画像</text>
- <text class="editor-save" @click="saveEdit">保存</text>
- </view>
- <view class="editor-body">
- <textarea
- class="editor-textarea"
- :value="editingText"
- placeholder="例如:我家孩子 8 岁,偏瘦,挑食,容易积食,睡眠不足..."
- maxlength="2000"
- :focus="editing"
- @input="onEditInput"
- ></textarea>
- <view class="char-count">{{ editingText.length }}/2000</view>
- </view>
- </view>
- </template>
- <script>
- import { getPortrait, editPortrait, getEnergySandbox } from '../../utils/api.js'
- export default {
- data() {
- return {
- portrait: null,
- portraitPrompt: '',
- renderedSnapshot: '',
- saving: false,
- editing: false,
- editingText: ''
- }
- },
- onLoad() {
- this.loadPortrait()
- this.loadEnergy()
- },
- methods: {
- async loadPortrait() {
- try {
- const res = await getPortrait({ memberId: this.$store.state.currentMemberId })
- if (res.code === 200 && res.data) {
- this.portraitPrompt = res.data.portraitPrompt || ''
- this.renderedSnapshot = res.data.renderedSnapshot || ''
- }
- } catch (e) {
- console.error('加载画像失败', e)
- }
- },
- loadEnergy() {
- var self = this
- getEnergySandbox('family').then(function(res) {
- if (res && res.code === 200 && res.data) {
- var d = res.data.familyData || null
- if (!d && res.data.personalData) {
- // 个人数据模式:使用个人数据
- d = res.data.personalData
- }
- if (!d || typeof d !== 'object') return
- self.portrait = self.mapToPortrait(d)
- }
- }).catch(function(e) {
- console.error('获取五维能量沙盘失败', e)
- self.portrait = null
- })
- },
- mapToPortrait(d) {
- // 计算总分和平均分
- var scores = { body: 0, mind: 0, wisdom: 0, action: 0, wealth: 0 }
- var dims = []
- var total = 0
- var count = 0
- var defs = [
- { key: 'body', label: '身', icon: '💪', color: '#FF8C42', desc: '身体健康状况,体质好坏的综合反映' },
- { key: 'mind', label: '心', icon: '💖', color: '#FF6B9D', desc: '情绪与心理状态,情绪波动与心理平衡' },
- { key: 'wisdom', label: '智', icon: '📚', color: '#6366F1', desc: '智力与知识水平,认知能力与知识储备' },
- { key: 'action', label: '行', icon: '🚀', color: '#10B981', desc: '行动力与执行力,计划与行动的落实情况' },
- { key: 'wealth', label: '富', icon: '💰', color: '#F59E0B', desc: '财富与资源,物质丰富程度与创富能力' }
- ]
- for (var i = 0; i < defs.length; i++) {
- var raw = d[defs[i].key + 'Score']
- var n = 0
- if (raw != null && !isNaN(raw)) {
- n = Math.max(0, Math.min(100, Number(raw)))
- }
- scores[defs[i].key] = n
- total += n
- count++
- dims.push({
- code: defs[i].key,
- name: defs[i].label,
- icon: defs[i].icon,
- score: n,
- color: defs[i].color,
- desc: defs[i].desc
- })
- }
- var overall = count > 0 ? Math.round(total / count) : 0
- // 确定等级和描述
- var tierLabel = ''
- var summary = ''
- var tip = ''
- if (overall >= 80) {
- tierLabel = '卓越守护者'
- summary = '你的五维能量处于卓越水平,身心健康,知行合一。家庭成员都受益于你的正向能量场。'
- tip = '保持当前的良好习惯,继续关注家庭成员的需求'
- } else if (overall >= 60) {
- tierLabel = '进取者'
- summary = '你的五维能量处于良好水平,各方面有所提升,但仍有提升空间。'
- tip = '可以尝试在其中一个维度上突破瓶颈,带动全家进步'
- } else if (overall >= 40) {
- tierLabel = '探索者'
- summary = '你的五维能量处于起步水平,正在探索和学习中。这是成长的起点。'
- tip = '从关注最容易改进的维度开始,每一点进步都很重要'
- } else {
- tierLabel = '起步者'
- summary = '你的五维能量处于起始水平,正处于开始关注全家健康的阶段。'
- tip = '从现在开始,每天关注一项小健康行动,逐步建立五维能量基础'
- }
- return {
- overall: overall,
- tierLabel: tierLabel,
- summary: summary,
- dims: dims,
- tip: tip
- }
- },
- enterEdit() {
- this.editingText = this.portraitPrompt
- this.editing = true
- },
- cancelEdit() {
- this.editing = false
- this.editingText = ''
- },
- onEditInput(e) {
- this.editingText = e.detail.value
- },
- async saveEdit() {
- this.saving = true
- try {
- const res = await editPortrait({ portraitPrompt: this.editingText })
- if (res.code === 200) {
- uni.showToast({ title: '保存成功', icon: 'success' })
- this.portraitPrompt = this.editingText
- this.editing = false
- this.editingText = ''
- } else {
- uni.showToast({ title: res.message || '保存失败', icon: 'none' })
- }
- } catch (e) {
- uni.showToast({ title: '保存失败', icon: 'none' })
- } finally {
- this.saving = false
- }
- }
- }
- }
- </script>
- <style scoped>
- .portrait-edit-page {
- padding: 24rpx;
- padding-bottom: 60rpx;
- min-height: 100vh;
- background: #f5f5f5;
- box-sizing: border-box;
- }
- .no-scroll {
- overflow: hidden;
- height: 100vh;
- }
- .section {
- background: #fff;
- border-radius: 16rpx;
- padding: 24rpx;
- margin-bottom: 24rpx;
- }
- .section-header-row {
- display: flex;
- align-items: center;
- justify-content: space-between;
- margin-bottom: 12rpx;
- }
- .section-title {
- font-size: 32rpx;
- font-weight: bold;
- color: #333;
- }
- .section-sub {
- font-size: 24rpx;
- color: #F97316;
- }
- .hint {
- font-size: 24rpx;
- color: #999;
- margin-bottom: 16rpx;
- line-height: 1.5;
- }
- .edit-btn {
- font-size: 26rpx;
- color: #F97316;
- padding: 6rpx 24rpx;
- border: 1rpx solid #F97316;
- border-radius: 30rpx;
- }
- .portrait-summary {
- margin-bottom: 16rpx;
- }
- .portrait-desc {
- font-size: 26rpx;
- color: #555;
- line-height: 1.6;
- }
- .portrait-dimensions {
- margin-bottom: 12rpx;
- }
- .portrait-dim-item {
- margin-bottom: 20rpx;
- }
- .portrait-dim-header {
- display: flex;
- align-items: center;
- margin-bottom: 8rpx;
- }
- .portrait-dim-icon {
- width: 44rpx;
- height: 44rpx;
- line-height: 44rpx;
- text-align: center;
- border-radius: 50%;
- font-size: 24rpx;
- margin-right: 12rpx;
- }
- .portrait-dim-name {
- font-size: 28rpx;
- color: #333;
- flex: 1;
- }
- .portrait-dim-score {
- font-size: 28rpx;
- font-weight: bold;
- color: #333;
- }
- .portrait-dim-bar-track {
- height: 16rpx;
- background: #eee;
- border-radius: 8rpx;
- overflow: hidden;
- margin-bottom: 8rpx;
- }
- .portrait-dim-bar-fill {
- height: 100%;
- border-radius: 8rpx;
- }
- .portrait-dim-desc {
- font-size: 22rpx;
- color: #999;
- line-height: 1.5;
- display: block;
- }
- .portrait-tip {
- margin-top: 8rpx;
- background: rgba(249, 115, 22, 0.08);
- border-radius: 12rpx;
- padding: 16rpx 20rpx;
- }
- .portrait-tip-text {
- font-size: 24rpx;
- color: #d97706;
- line-height: 1.5;
- }
- .portrait-prompt-text {
- font-size: 28rpx;
- color: #333;
- white-space: pre-wrap;
- line-height: 1.7;
- word-break: break-all;
- }
- .portrait-prompt-empty {
- font-size: 26rpx;
- color: #999;
- line-height: 1.6;
- }
- .snapshot-text {
- font-size: 26rpx;
- color: #555;
- white-space: pre-wrap;
- line-height: 1.6;
- }
- /* ===== 全屏编辑模式 ===== */
- .editor-fullscreen {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- width: 100%;
- height: 100%;
- background: #fff;
- z-index: 999;
- display: flex;
- flex-direction: column;
- box-sizing: border-box;
- }
- .editor-topbar {
- height: 88rpx;
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 0 24rpx;
- border-bottom: 1rpx solid #f0f0f0;
- flex-shrink: 0;
- }
- .editor-cancel {
- font-size: 30rpx;
- color: #666;
- }
- .editor-title {
- font-size: 32rpx;
- font-weight: bold;
- color: #333;
- }
- .editor-save {
- font-size: 30rpx;
- color: #F97316;
- font-weight: bold;
- }
- .editor-body {
- flex: 1;
- display: flex;
- flex-direction: column;
- min-height: 0;
- position: relative;
- }
- .editor-textarea {
- width: 100%;
- height: calc(100vh - 88rpx);
- padding: 24rpx;
- font-size: 30rpx;
- line-height: 1.6;
- box-sizing: border-box;
- color: #333;
- }
- .char-count {
- position: absolute;
- right: 24rpx;
- bottom: 16rpx;
- font-size: 22rpx;
- color: #999;
- }
- </style>
|