| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- <template>
- <view class="page-compare">
- <!-- Loading state -->
- <view v-if="loading" class="loading-state">
- <view class="skeleton-chart" />
- <view class="skeleton-chart" />
- </view>
- <template v-else>
- <!-- Info header -->
- <view class="compare-header">
- <text class="compare-title">{{ relationName }} vs 我</text>
- <text class="compare-subtitle">{{ relationTypeLabel(relationType) }}关系能量对比</text>
- </view>
- <!-- Dual charts -->
- <view class="charts-row">
- <view class="chart-wrapper">
- <view class="chart-label">
- <text class="chart-label-name">{{ relationName }}</text>
- <text class="chart-label-date">{{ relationBirthDate }}</text>
- </view>
- <TriangleChart
- :chartData="relationChart"
- :birthYear="relationBirthYear"
- :birthMonth="relationBirthMonth"
- :birthDay="relationBirthDay"
- />
- </view>
- <view class="chart-wrapper">
- <view class="chart-label">
- <text class="chart-label-name">我</text>
- <text class="chart-label-date">{{ myBirthDate }}</text>
- </view>
- <TriangleChart
- :chartData="myChart"
- :birthYear="myBirthYear"
- :birthMonth="myBirthMonth"
- :birthDay="myBirthDay"
- />
- </view>
- </view>
- <!-- AI Interpretation button -->
- <view class="action-bar">
- <view class="btn-interpret" :class="{ interpreting: interpreting }" @click="handleInterpret">
- <text class="btn-interpret-text">
- {{ interpreting ? '解读中...' : interpretation ? '重新解读' : 'AI 关系能量解读' }}
- </text>
- </view>
- </view>
- <!-- Interpretation result -->
- <view v-if="interpretation" class="interpretation-card">
- <view class="interpretation-header">
- <text class="interpretation-title">🔗 关系能量解读</text>
- <text v-if="interpretDate" class="interpretation-date">{{ interpretDate }}</text>
- </view>
- <view class="interpretation-body">
- <text class="interpretation-content">{{ interpretation }}</text>
- </view>
- </view>
- </template>
- </view>
- </template>
- <script setup>
- import { ref } from 'vue'
- import { onLoad } from '@dcloudio/uni-app'
- import requestInstance from '@/utils/api'
- import TriangleChart from '@/components/TriangleChart.vue'
- const relationId = ref(null)
- const loading = ref(true)
- const relationName = ref('')
- const relationType = ref('')
- const relationBirthDate = ref('')
- const relationBirthYear = ref(0)
- const relationBirthMonth = ref(0)
- const relationBirthDay = ref(0)
- const relationChart = ref(null)
- const myChart = ref(null)
- const myBirthDate = ref('')
- const myBirthYear = ref(0)
- const myBirthMonth = ref(0)
- const myBirthDay = ref(0)
- const interpretation = ref('')
- const interpretDate = ref('')
- const interpreting = ref(false)
- const relationApi = {
- get: (id) => requestInstance.post('/relation/get', { id }),
- compare: (relationId) => requestInstance.post('/relation/compare', { relationId }),
- interpret: (relationId) => requestInstance.post('/relation/compare/interpret', { relationId })
- }
- const myApi = {
- profile: () => requestInstance.post('/profile/get', {})
- }
- const relationTypeMap = {
- spouse: '夫妻',
- parent_child: '亲子',
- colleague: '同事',
- friend: '朋友',
- superior: '上下级'
- }
- function relationTypeLabel(type) {
- return relationTypeMap[type] || type || '未知'
- }
- onLoad(async (query) => {
- relationId.value = query?.id || null
- if (!relationId.value) {
- uni.showToast({ title: '缺少关系人信息', icon: 'none' })
- setTimeout(() => uni.navigateBack(), 1500)
- return
- }
- await fetchData(relationId.value)
- })
- async function fetchData(relationId) {
- loading.value = true
- try {
- const [rel, profile] = await Promise.all([
- relationApi.get(relationId),
- myApi.profile()
- ])
- // Relation data
- relationName.value = rel.name || '未知'
- relationType.value = rel.relationType || ''
- relationBirthDate.value = rel.birthDate || ''
- relationChart.value = rel.chartData || null
- if (rel.birthDate) {
- const parts = rel.birthDate.split('-')
- relationBirthYear.value = parseInt(parts[0]) || 0
- relationBirthMonth.value = parseInt(parts[1]) || 0
- relationBirthDay.value = parseInt(parts[2]) || 0
- }
- // Current user data
- myChart.value = profile.chartData || null
- if (profile.birthDate) {
- myBirthDate.value = profile.birthDate
- const parts = profile.birthDate.split('-')
- myBirthYear.value = parseInt(parts[0]) || 0
- myBirthMonth.value = parseInt(parts[1]) || 0
- myBirthDay.value = parseInt(parts[2]) || 0
- }
- // Check if interpretation already exists
- try {
- const cmp = await relationApi.compare(relationId)
- if (cmp && cmp.interpretation) {
- interpretation.value = cmp.interpretation
- interpretDate.value = cmp.updatedAt || cmp.createdAt || ''
- }
- } catch (e) {
- // No existing comparison — user can trigger interpret
- }
- } catch (e) {
- uni.showToast({ title: '加载数据失败', icon: 'none' })
- } finally {
- loading.value = false
- }
- }
- async function handleInterpret() {
- if (interpreting.value) return
- interpreting.value = true
- try {
- const result = await relationApi.interpret(relationId.value)
- if (result && result.interpretation) {
- interpretation.value = result.interpretation
- interpretDate.value = result.updatedAt || result.createdAt || ''
- } else {
- uni.showToast({ title: '解读失败,请重试', icon: 'none' })
- }
- } catch (e) {
- uni.showToast({ title: '解读请求失败', icon: 'none' })
- } finally {
- interpreting.value = false
- }
- }
- </script>
- <style scoped lang="scss">
- .page-compare {
- min-height: 100vh;
- background: var(--color-bg);
- padding: 16px;
- }
- .loading-state {
- display: flex;
- flex-direction: column;
- gap: 16px;
- padding-top: 20px;
- }
- .skeleton-chart {
- height: 320px;
- background: linear-gradient(90deg, var(--color-bg-secondary) 25%, var(--color-bg) 50%, var(--color-bg-secondary) 75%);
- background-size: 400px 100%;
- animation: shimmer 1.5s ease-in-out infinite;
- border-radius: 16px;
- }
- @keyframes shimmer {
- 0% { background-position: -200px 0; }
- 100% { background-position: 200px 0; }
- }
- .compare-header {
- margin-bottom: 16px;
- }
- .compare-title {
- font-size: 20px;
- font-weight: 700;
- color: var(--color-text-primary);
- display: block;
- margin-bottom: 4px;
- }
- .compare-subtitle {
- font-size: 13px;
- color: var(--color-text-secondary);
- }
- .charts-row {
- display: flex;
- flex-direction: column;
- gap: 16px;
- }
- .chart-wrapper {
- width: 100%;
- }
- .chart-label {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 8px;
- padding: 0 4px;
- }
- .chart-label-name {
- font-size: 14px;
- font-weight: 600;
- color: var(--color-text-primary);
- }
- .chart-label-date {
- font-size: 12px;
- color: var(--color-text-secondary);
- }
- .action-bar {
- margin-top: 20px;
- margin-bottom: 16px;
- }
- .btn-interpret {
- width: 100%;
- height: 48px;
- background: var(--color-brand);
- border-radius: 12px;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .btn-interpret:active {
- transform: scale(0.97);
- opacity: 0.9;
- }
- .btn-interpret.interpreting {
- opacity: 0.7;
- pointer-events: none;
- }
- .btn-interpret-text {
- color: var(--color-bg-card);
- font-size: 16px;
- font-weight: 600;
- }
- .interpretation-card {
- background: var(--color-bg-card);
- border-radius: 16px;
- padding: 20px 16px;
- box-shadow: var(--shadow-card);
- }
- .interpretation-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 12px;
- }
- .interpretation-title {
- font-size: 16px;
- font-weight: 600;
- color: var(--color-text-primary);
- }
- .interpretation-date {
- font-size: 12px;
- color: var(--color-text-secondary);
- }
- .interpretation-body {
- background: var(--color-bg-secondary);
- border-radius: 12px;
- padding: 16px;
- }
- .interpretation-content {
- font-size: 14px;
- color: var(--color-text-secondary);
- line-height: 1.8;
- white-space: pre-wrap;
- }
- </style>
|