| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- <template>
- <view class="portrait-edit-page">
- <view class="section">
- <view class="section-title">我的画像描述</view>
- <view class="hint">请输入你的个人画像描述,AI 会根据这些信息提供更有针对性的建议(最多 2000 字)</view>
-
- <!-- 操作按钮行 -->
- <view class="action-row">
- <button class="action-btn" :loading="generating" @click="onGenerate" v-if="!portraitPrompt">
- 🪄 AI 生成画像
- </button>
- <button class="action-btn secondary" :loading="regenerating" @click="onRegenerate" v-if="portraitPrompt">
- 🔄 重画画像
- </button>
- </view>
-
- <textarea
- class="portrait-textarea"
- :value="portraitPrompt"
- placeholder="例如:我家孩子 8 岁,偏瘦,挑食,容易积食,睡眠不足..."
- maxlength="2000"
- @input="onInput"
- ></textarea>
- <view class="char-count">{{ portraitPrompt.length }}/2000</view>
- </view>
- <view class="section" v-if="renderedSnapshot">
- <view class="section-title">参考指标(自动生成)</view>
- <view class="snapshot-text">{{ renderedSnapshot }}</view>
- </view>
- <button class="save-btn" :loading="saving" @click="onSave">保存</button>
- </view>
- </template>
- <script>
- import { getPortrait, editPortrait, generatePortrait, regeneratePortrait } from '../../utils/api.js'
- export default {
- data() {
- return {
- portraitPrompt: '',
- renderedSnapshot: '',
- saving: false,
- generating: false,
- regenerating: false
- }
- },
- onLoad() {
- this.loadPortrait()
- },
- 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)
- }
- },
- onInput(e) {
- this.portraitPrompt = e.detail.value
- },
- async onGenerate() {
- this.generating = true
- try {
- const memberId = this.$store.state.currentMemberId
- const res = await generatePortrait({ memberId })
- if (res.code === 200 && res.data) {
- this.portraitPrompt = res.data.portraitPrompt || ''
- this.renderedSnapshot = res.data.renderedSnapshot || ''
- uni.showToast({ title: '生成成功', icon: 'success' })
- } else {
- uni.showToast({ title: res.message || '生成失败', icon: 'none' })
- }
- } catch (e) {
- uni.showToast({ title: '生成失败', icon: 'none' })
- } finally {
- this.generating = false
- }
- },
- async onRegenerate() {
- this.regenerating = true
- try {
- const memberId = this.$store.state.currentMemberId
- const res = await regeneratePortrait({
- memberId,
- previousPortrait: this.portraitPrompt // 传入当前画像作为参考
- })
- if (res.code === 200 && res.data) {
- this.portraitPrompt = res.data.portraitPrompt || ''
- this.renderedSnapshot = res.data.renderedSnapshot || ''
- uni.showToast({ title: '重画成功', icon: 'success' })
- } else {
- uni.showToast({ title: res.message || '重画失败', icon: 'none' })
- }
- } catch (e) {
- uni.showToast({ title: '重画失败', icon: 'none' })
- } finally {
- this.regenerating = false
- }
- },
- async onSave() {
- this.saving = true
- try {
- const res = await editPortrait({ portraitPrompt: this.portraitPrompt })
- if (res.code === 200) {
- uni.showToast({ title: '保存成功', icon: 'success' })
- this.loadPortrait()
- } 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;
- min-height: 100vh;
- background: #f5f5f5;
- }
- .section {
- background: #fff;
- border-radius: 16rpx;
- padding: 24rpx;
- margin-bottom: 24rpx;
- }
- .section-title {
- font-size: 32rpx;
- font-weight: bold;
- margin-bottom: 12rpx;
- color: #333;
- }
- .hint {
- font-size: 24rpx;
- color: #999;
- margin-bottom: 16rpx;
- }
- .action-row {
- display: flex;
- gap: 16rpx;
- margin-bottom: 16rpx;
- }
- .action-btn {
- flex: 1;
- height: 72rpx;
- border-radius: 36rpx;
- font-size: 26rpx;
- background: linear-gradient(135deg, #667eea, #764ba2);
- color: #fff;
- }
- .action-btn.secondary {
- background: linear-gradient(135deg, #F97316, #F59E0B);
- }
- .portrait-textarea {
- width: 100%;
- height: 300rpx;
- border: 1rpx solid #eee;
- border-radius: 8rpx;
- padding: 16rpx;
- font-size: 28rpx;
- box-sizing: border-box;
- line-height: 1.6;
- }
- .char-count {
- text-align: right;
- font-size: 22rpx;
- color: #999;
- margin-top: 8rpx;
- }
- .snapshot-text {
- font-size: 26rpx;
- color: #555;
- white-space: pre-wrap;
- line-height: 1.6;
- }
- .save-btn {
- background: #F97316;
- color: #fff;
- border-radius: 44rpx;
- height: 88rpx;
- line-height: 88rpx;
- font-size: 32rpx;
- margin-top: 32rpx;
- }
- </style>
|