feat: 增加AI模块功能
This commit is contained in:
@@ -5,14 +5,15 @@
|
||||
:safe-area-inset-bottom="true"
|
||||
custom-style="border-radius: 32rpx 32rpx 0 0; padding: 40rpx 32rpx"
|
||||
>
|
||||
<view class="flex flex-col max-h-[70vh] overflow-y-auto">
|
||||
<view class="mb-4 text-center text-base text-gray-700 font-semibold">
|
||||
编辑个人信息
|
||||
</view>
|
||||
<wd-form :model="profileStore.profileForm" layout="horizontal" title-width="80" size="large" border>
|
||||
<wd-form-item title="昵称" prop="name">
|
||||
<wd-form-item title="昵称">
|
||||
<wd-input v-model="profileStore.profileForm.name" placeholder="请输入昵称" />
|
||||
</wd-form-item>
|
||||
<wd-form-item title="性别" prop="gender">
|
||||
<wd-form-item title="性别">
|
||||
<wd-radio-group v-model="profileStore.profileForm.gender" type="button">
|
||||
<wd-radio value="male">
|
||||
男
|
||||
@@ -22,12 +23,58 @@
|
||||
</wd-radio>
|
||||
</wd-radio-group>
|
||||
</wd-form-item>
|
||||
<wd-form-item title="身高" prop="height">
|
||||
<wd-form-item title="出生年份">
|
||||
<wd-input-number
|
||||
v-model="profileStore.profileForm.birthYear"
|
||||
:min="1"
|
||||
:max="3000"
|
||||
:step="1"
|
||||
/>
|
||||
</wd-form-item>
|
||||
<wd-form-item title="身高">
|
||||
<view class="flex items-center gap-2">
|
||||
<wd-input v-model.number="profileStore.profileForm.height" type="number" placeholder="请输入身高" />
|
||||
<wd-input-number
|
||||
v-model="profileStore.profileForm.height"
|
||||
:min="1"
|
||||
:max="300"
|
||||
:step="1"
|
||||
/>
|
||||
<text class="shrink-0 text-sm text-gray-400">cm</text>
|
||||
</view>
|
||||
</wd-form-item>
|
||||
<wd-form-item title="职业性质">
|
||||
<wd-radio-group v-model="profileStore.profileForm.occupation" type="button">
|
||||
<wd-radio
|
||||
v-for="item in occupationColumns"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</wd-radio>
|
||||
</wd-radio-group>
|
||||
</wd-form-item>
|
||||
<wd-form-item title="疾病史">
|
||||
<wd-checkbox-group v-model="profileStore.profileForm.condition" type="button">
|
||||
<wd-checkbox
|
||||
v-for="item in conditionColumns"
|
||||
:key="item.value"
|
||||
:name="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</wd-checkbox>
|
||||
</wd-checkbox-group>
|
||||
</wd-form-item>
|
||||
<wd-form-item title="健康目标">
|
||||
<wd-radio-group v-model="profileStore.profileForm.goal" type="button">
|
||||
<wd-radio
|
||||
v-for="item in goalColumns"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</wd-radio>
|
||||
</wd-radio-group>
|
||||
</wd-form-item>
|
||||
</wd-form>
|
||||
|
||||
<view class="flex w-full items-center justify-evenly">
|
||||
@@ -38,6 +85,7 @@
|
||||
保存
|
||||
</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</wd-popup>
|
||||
</template>
|
||||
|
||||
@@ -47,6 +95,26 @@ import { useProfileStore } from '@/store/profile'
|
||||
|
||||
const profileStore = useProfileStore()
|
||||
|
||||
const occupationColumns = [
|
||||
{ label: '久坐少动', value: '久坐少动' },
|
||||
{ label: '体力活动', value: '体力活动' },
|
||||
{ label: '作息紊乱', value: '作息紊乱' }
|
||||
]
|
||||
|
||||
const conditionColumns = [
|
||||
{ label: '高血压', value: '高血压' },
|
||||
{ label: '糖尿病', value: '糖尿病' },
|
||||
{ label: '高血脂', value: '高血脂' },
|
||||
{ label: '脂肪肝', value: '脂肪肝' }
|
||||
]
|
||||
|
||||
const goalColumns = [
|
||||
{ label: '控制体重', value: '控制体重' },
|
||||
{ label: '均衡饮食', value: '均衡饮食' },
|
||||
{ label: '改善睡眠', value: '改善睡眠' },
|
||||
{ label: '缓解压力', value: '缓解压力' }
|
||||
]
|
||||
|
||||
function cancelClick() {
|
||||
profileStore.showProfilePopup = false
|
||||
}
|
||||
|
||||
35
src/components/Record/AiResultPopup.vue
Normal file
35
src/components/Record/AiResultPopup.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<wd-popup
|
||||
v-model="recordStore.showAIResultPopup"
|
||||
position="bottom"
|
||||
:safe-area-inset-bottom="true"
|
||||
:close-on-click-modal="false"
|
||||
custom-style="border-radius: 32rpx 32rpx 0 0; padding: 40rpx 32rpx"
|
||||
>
|
||||
<view class="flex flex-col align-center gap-2 max-h-[70vh] overflow-y-auto">
|
||||
<view class="flex items-center justify-between mb-4">
|
||||
<text class="text-lg font-bold">AI 健康分析</text>
|
||||
<wd-icon name="close" size="20px" @click="recordStore.showAIResultPopup = false" />
|
||||
</view>
|
||||
|
||||
<view class="bg-blue-50 rounded-lg">
|
||||
<text class="text-gray-800 leading-relaxed whitespace-pre-wrap">{{ recordStore.aiResult }}</text>
|
||||
</view>
|
||||
|
||||
<view class="flex w-full items-center justify-evenly">
|
||||
<wd-button type="info" @click="recordStore.showAIResultPopup = false">
|
||||
取消
|
||||
</wd-button>
|
||||
<wd-button type="primary">
|
||||
分享
|
||||
</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</wd-popup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRecordStore } from '@/store/record'
|
||||
|
||||
const recordStore = useRecordStore()
|
||||
</script>
|
||||
@@ -80,7 +80,7 @@
|
||||
<template #title>
|
||||
<view class="wd-card__title-text flex items-center justify-between">
|
||||
<text>🔥 健康资讯</text>
|
||||
<text class="text-11px text-[#999]">🔄 每日更新</text>
|
||||
<text class="text-11px text-[#999]">每日更新</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
<template #title>
|
||||
<view class="wd-card__title-text flex items-center justify-between">
|
||||
<text>🌿 养生食谱</text>
|
||||
<text class="text-11px text-[#999]">🔄 每日更新</text>
|
||||
<text class="text-11px text-[#999]">每日更新</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<template>
|
||||
<wd-config-provider :theme-vars="getTheme(appStore.themeColor)">
|
||||
<view class="min-h-screen flex flex-col" :style="getThemeStyle[appStore.themeColor]">
|
||||
<view class="min-h-screen flex flex-col position" :style="getThemeStyle[appStore.themeColor]">
|
||||
<liu-drag-button :background="getThemePrimaryColor[appStore.themeColor]" @clickBtn="AIClick">
|
||||
<wd-text color="#fff" size="18px" text="AI"/>
|
||||
</liu-drag-button>
|
||||
|
||||
<wd-segmented v-model:value="appStore.viewRecordType" :options="viewTypeSegmentList"
|
||||
@change="handleChangeSegment"
|
||||
custom-style="align-self: center; width: 60%; margin: 10px;">
|
||||
@@ -81,6 +85,16 @@
|
||||
:max-date="Date.now()"
|
||||
@confirm="onDateConfirm"
|
||||
/>
|
||||
|
||||
<wd-dialog />
|
||||
|
||||
<wd-overlay :show="recordStore.isAnalyzing">
|
||||
<view class="flex flex-col items-center justify-center h-full">
|
||||
<wd-loading type="spinner" color="#FFF" >AI 正在分析中...</wd-loading>
|
||||
</view>
|
||||
</wd-overlay>
|
||||
|
||||
<ai-result-popup/>
|
||||
</view>
|
||||
</wd-config-provider>
|
||||
</template>
|
||||
@@ -101,6 +115,9 @@ import type { ICalendarSelect, IGroupHealthRecord, IHealthRecord } from "@/type/
|
||||
import { useAppStore } from "@/store/app";
|
||||
import { getTheme, getThemePrimaryColor, getThemeStyle } from "@/utils/themes";
|
||||
import { share } from '@/utils/share'
|
||||
import { useDialog } from '@wot-ui/ui'
|
||||
import AiResultPopup from "@/components/Record/AiResultPopup.vue";
|
||||
import { useRecordStore } from '@/store/record'
|
||||
|
||||
const { onShareAppMessage } = share()
|
||||
onShareAppMessage()
|
||||
@@ -115,6 +132,7 @@ const calendarSelect = ref<ICalendarSelect[]>([])
|
||||
const selectDate = ref<string>(getDateStr(Date.now()))
|
||||
|
||||
const appStore = useAppStore()
|
||||
const recordStore = useRecordStore()
|
||||
|
||||
interface SegmentOption {
|
||||
value: string
|
||||
@@ -140,6 +158,7 @@ const displayDate = computed(() => {
|
||||
})
|
||||
|
||||
function loadListRecords() {
|
||||
selectMonth.value = getMonthStr(currentDate.value)
|
||||
const all = getMonthRecords(getMonthStr(currentDate.value), currentRecordTypeSegment.value)
|
||||
records.value = all
|
||||
grouped.value = groupByDate(all)
|
||||
@@ -226,6 +245,68 @@ function deleteClick(item: IHealthRecord) {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const dialog = useDialog()
|
||||
|
||||
const AIClick = () => {
|
||||
dialog
|
||||
.confirm({
|
||||
title: `开始分析${selectMonth.value}记录数据,本次分析将消耗 100 积分,是否继续?`,
|
||||
})
|
||||
.then(async () => {
|
||||
recordStore.isAnalyzing = true
|
||||
recordStore.aiResult = ''
|
||||
|
||||
// 模拟请求延迟
|
||||
await new Promise(r => setTimeout(r, 2000))
|
||||
|
||||
recordStore.isAnalyzing = false
|
||||
// 模拟返回结果
|
||||
recordStore.aiResult = generateMockResult()
|
||||
recordStore.showAIResultPopup = true
|
||||
})
|
||||
.catch(() => {
|
||||
console.log('点击了取消')
|
||||
})
|
||||
}
|
||||
|
||||
function generateMockResult() {
|
||||
return `📊 ${selectMonth.value}健康分析
|
||||
|
||||
• 本月共记录 23 天,覆盖率 74%
|
||||
• 平均睡眠 6.5 小时,低于目标 7.5 小时
|
||||
• 血压整体平稳,但有 3 天偏高(周一、周三)
|
||||
• 建议:增加周末睡眠时间,注意周一血压波动
|
||||
• 本月共记录 23 天,覆盖率 74%
|
||||
• 平均睡眠 6.5 小时,低于目标 7.5 小时
|
||||
• 血压整体平稳,但有 3 天偏高(周一、周三)
|
||||
• 建议:增加周末睡眠时间,注意周一血压波动
|
||||
• 本月共记录 23 天,覆盖率 74%
|
||||
• 平均睡眠 6.5 小时,低于目标 7.5 小时
|
||||
• 血压整体平稳,但有 3 天偏高(周一、周三)
|
||||
• 建议:增加周末睡眠时间,注意周一血压波动
|
||||
• 本月共记录 23 天,覆盖率 74%
|
||||
• 平均睡眠 6.5 小时,低于目标 7.5 小时
|
||||
• 血压整体平稳,但有 3 天偏高(周一、周三)
|
||||
• 建议:增加周末睡眠时间,注意周一血压波动
|
||||
• 本月共记录 23 天,覆盖率 74%
|
||||
• 平均睡眠 6.5 小时,低于目标 7.5 小时
|
||||
• 血压整体平稳,但有 3 天偏高(周一、周三)
|
||||
• 建议:增加周末睡眠时间,注意周一血压波动
|
||||
• 本月共记录 23 天,覆盖率 74%
|
||||
• 平均睡眠 6.5 小时,低于目标 7.5 小时
|
||||
• 血压整体平稳,但有 3 天偏高(周一、周三)
|
||||
• 建议:增加周末睡眠时间,注意周一血压波动
|
||||
• 本月共记录 23 天,覆盖率 74%
|
||||
• 平均睡眠 6.5 小时,低于目标 7.5 小时
|
||||
• 血压整体平稳,但有 3 天偏高(周一、周三)
|
||||
• 建议:增加周末睡眠时间,注意周一血压波动
|
||||
• 本月共记录 23 天,覆盖率 74%
|
||||
• 平均睡眠 6.5 小时,低于目标 7.5 小时
|
||||
• 血压整体平稳,但有 3 天偏高(周一、周三)
|
||||
• 建议:增加周末睡眠时间,注意周一血压波动
|
||||
`
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -443,3 +443,10 @@ function handleChangeBloodSugar() {
|
||||
getBloodSugarChartData()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
:deep(.wd-segmented__item) {
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
{{ profile.name || '未设置姓名' }}
|
||||
</text>
|
||||
<text class="mt-0.5 text-xs text-gray-400">
|
||||
{{ genderText }} · {{ profile.height ? `${profile.height} cm` : '身高未填' }}
|
||||
{{ genderText }} · {{ ageText }} · {{ heightText }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="ml-auto">
|
||||
@@ -109,6 +109,21 @@ const genderText = computed(() => {
|
||||
return '性别未填'
|
||||
})
|
||||
|
||||
const ageText = computed(() => {
|
||||
const year = profile.value.birthYear
|
||||
if (!year) return '年龄未填'
|
||||
|
||||
const age = new Date().getFullYear() - year
|
||||
return `${age}岁`
|
||||
})
|
||||
|
||||
const heightText = computed(() => {
|
||||
if (profile.value.height)
|
||||
return `${profile.value.height} cm`
|
||||
|
||||
return '身高未填'
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
appStore.points = getPoints()
|
||||
appStore.recordCount = getRecordCount()
|
||||
@@ -121,7 +136,8 @@ watch(() => profileStore.isRefresh, () => {
|
||||
|
||||
function openProfile() {
|
||||
Object.assign(profileStore.profileForm, profile.value)
|
||||
|
||||
profileStore.profileForm.height = 170
|
||||
profileStore.profileForm.birthYear = 1990
|
||||
profileStore.showProfilePopup = true
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,11 @@ export const useProfileStore = defineStore('profile', {
|
||||
showAboutPopup: false,
|
||||
profileForm: {
|
||||
gender: '',
|
||||
height: 0,
|
||||
height: 170,
|
||||
birthYear: 1990,
|
||||
occupation: '',
|
||||
condition: [],
|
||||
goal: '',
|
||||
name: '',
|
||||
} as IProfile,
|
||||
themeColor: 'purple',
|
||||
|
||||
@@ -50,6 +50,9 @@ export const useRecordStore = defineStore('record', {
|
||||
time: Date.now(),
|
||||
},
|
||||
isRecord: false,
|
||||
isAnalyzing: false,
|
||||
showAIResultPopup: false,
|
||||
aiResult: ''
|
||||
}),
|
||||
actions: {
|
||||
openWeight() {
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
export interface IProfile {
|
||||
name: string
|
||||
gender: 'male' | 'female' | ''
|
||||
birthYear: number | null
|
||||
height: number | null
|
||||
// 职业性质
|
||||
occupation: string
|
||||
// 疾病史
|
||||
condition: string[]
|
||||
// 健康目标
|
||||
goal: string
|
||||
}
|
||||
|
||||
export interface IOverview {
|
||||
|
||||
@@ -11,6 +11,10 @@ function getEmptyProfile(): IProfile {
|
||||
return {
|
||||
gender: '',
|
||||
height: null,
|
||||
birthYear: null,
|
||||
occupation: '',
|
||||
condition: [],
|
||||
goal: '',
|
||||
name: '',
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user