Files
smartDriveEEUniApp/pages/xixi/xixi.vue
2026-04-05 22:23:19 +08:00

503 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="page">
<!-- #ifdef APP -->
<statusBar></statusBar>
<!-- #endif -->
<uni-nav-bar
:fixed="true"
:statusBar="true"
:border="false"
:title="navTitle"
:leftIcon="viewMode === 'detail' ? 'left' : ''"
@clickLeft="onNavLeftClick"
color="#333"
backgroundColor="#FFFFFF"
/>
<view class="content">
<!-- 首页 AI 分析一致的功能分区 + 卡片网格 -->
<scroll-view
v-show="viewMode === 'home'"
class="content-scroll"
scroll-y="true"
:style="{ top: computedContentTop || contentTop, bottom: contentBottom || '96rpx' }"
>
<view class="category-section two-column">
<view class="category-header">
<text class="category-icon">💕</text>
<text class="category-title">恋爱自画像</text>
</view>
<view class="function-grid">
<view class="function-item" @click="openDetail('who')">
<view class="function-icon">🪞</view>
<text class="function-name">我是谁</text>
<text class="function-desc">自我描述与对另一半的期待</text>
</view>
<view class="function-item" @click="openDetail('meet')">
<view class="function-icon">🌷</view>
<text class="function-name">约会准备</text>
<text class="function-desc">关系阶段与本次见面目标</text>
</view>
</view>
<view class="advice-full" @click="openDetail('advice')">
<view class="advice-full-inner">
<view class="function-icon advice-full-icon">💡</view>
<view class="advice-full-texts">
<text class="function-name">约会建议</text>
<text class="function-desc">根据我是谁约会准备生成今日参考</text>
</view>
</view>
</view>
</view>
<view class="category-section two-column">
<view class="category-header">
<text class="category-icon">💬</text>
<text class="category-title">相处与复盘</text>
</view>
<view class="function-grid">
<view class="function-item" @click="openDetail('record')">
<view class="function-icon">🎉</view>
<text class="function-name">幸福相遇</text>
<text class="function-desc">录音记录与智能分析</text>
</view>
<view class="function-item" @click="openDetail('diag')">
<view class="function-icon">📋</view>
<text class="function-name">诊断结果</text>
<text class="function-desc">查看历史诊断与建议</text>
</view>
</view>
</view>
</scroll-view>
<!-- 详情固定区域 + 子组件自带 scroll-view避免双层 scroll 嵌套 -->
<view
v-show="viewMode === 'detail'"
class="detail-wrap"
:style="{ top: computedContentTop || contentTop, bottom: contentBottom || '96rpx' }"
>
<view v-show="detailKey === 'who'" class="detail-panel">
<XixiWhoAmI @saved="onFormSaved" />
</view>
<view v-show="detailKey === 'meet'" class="detail-panel">
<XixiFirstMeeting @saved="onFormSaved" />
</view>
<view v-show="detailKey === 'record'" class="detail-panel">
<XixiRecord @recordings-changed="onRecordingsChanged" />
</view>
<view v-show="detailKey === 'diag'" class="detail-panel">
<XixiDiagnosis :refresh-tick="diagTick" />
</view>
<view v-show="detailKey === 'advice'" class="detail-panel">
<XixiDatingAdvice :refresh-tick="adviceRefreshTick" />
</view>
</view>
</view>
</view>
</template>
<script>
// #ifdef APP
import statusBar from '@/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar';
// #endif
// 微信小程序:由 pages.json 的 componentPlaceholder 注册分包组件(分包异步化)
// H5 / App 等:须显式 import
// #ifndef MP-WEIXIN
import XixiWhoAmI from '@/pages-subpackage/xixi/components/xixi-who-am-i.vue';
import XixiFirstMeeting from '@/pages-subpackage/xixi/components/xixi-first-meeting.vue';
import XixiRecord from '@/pages-subpackage/xixi/components/xixi-record.vue';
import XixiDiagnosis from '@/pages-subpackage/xixi/components/xixi-diagnosis.vue';
import XixiDatingAdvice from '@/pages-subpackage/xixi/components/xixi-dating-advice.vue';
// #endif
function getSystemInfo() {
// #ifdef MP-WEIXIN
if (typeof wx !== 'undefined' && wx.getWindowInfo) {
try {
const windowInfo = wx.getWindowInfo();
return {
statusBarHeight: windowInfo.statusBarHeight || 20,
windowWidth: windowInfo.windowWidth,
windowHeight: windowInfo.windowHeight,
};
} catch (e) {
console.warn('[Xixi] wx.getWindowInfo failed:', e);
}
}
// #endif
try {
const systemInfo = uni.getSystemInfoSync();
return {
statusBarHeight: systemInfo.statusBarHeight || 20,
windowWidth: systemInfo.windowWidth,
windowHeight: systemInfo.windowHeight,
};
} catch (e) {
return {
statusBarHeight: 20,
windowWidth: 375,
windowHeight: 667,
};
}
}
const DETAIL_LABELS = {
who: '我是谁',
meet: '约会准备',
record: '幸福相遇',
diag: '诊断结果',
advice: '约会建议',
};
export default {
components: {
// #ifdef APP
statusBar,
// #endif
// #ifndef MP-WEIXIN
XixiWhoAmI,
XixiFirstMeeting,
XixiRecord,
XixiDiagnosis,
XixiDatingAdvice,
// #endif
},
data() {
const systemInfo = getSystemInfo();
const statusBarHeightRpx = systemInfo.statusBarHeight * 2;
const navbarHeightRpx = 44 * 2;
const totalNavbarHeight = statusBarHeightRpx + navbarHeightRpx;
return {
viewMode: 'home',
detailKey: null,
contentTop: totalNavbarHeight + 'rpx',
contentBottom: '96rpx',
diagTick: 0,
adviceRefreshTick: 0,
};
},
computed: {
computedContentTop() {
const systemInfo = getSystemInfo();
const statusBarHeightRpx = systemInfo.statusBarHeight * 2;
const navbarHeightRpx = 44 * 2;
return statusBarHeightRpx + navbarHeightRpx + 'rpx';
},
navTitle() {
if (this.viewMode === 'detail' && this.detailKey && DETAIL_LABELS[this.detailKey]) {
return DETAIL_LABELS[this.detailKey];
}
return '喜喜';
},
},
onLoad() {
this.updateNavbarPosition();
// #ifdef MP-WEIXIN
setTimeout(() => this.preloadSubpackage(), 100);
// #endif
},
onShow() {
this.diagTick += 1;
this.adviceRefreshTick += 1;
this.$nextTick(() => {
this.updateNavbarPosition();
setTimeout(() => this.updateNavbarPosition(), 50);
});
},
onReady() {
this.$nextTick(() => {
const query = uni.createSelectorQuery().in(this);
query
.select('.uni-navbar__content')
.boundingClientRect((data) => {
if (data && data.height) {
const adjustedHeight = Math.max(data.height * 2 - 4, 0);
this.$set(this, 'contentTop', adjustedHeight + 'rpx');
} else {
const systemInfo = getSystemInfo();
const total =
systemInfo.statusBarHeight * 2 + 44 * 2;
this.$set(this, 'contentTop', Math.max(total - 4, 0) + 'rpx');
}
})
.exec();
const queryTabbar = uni.createSelectorQuery().in(this);
queryTabbar
.select('.tabbar')
.boundingClientRect((tabbarData) => {
if (tabbarData && tabbarData.height) {
const adjustedBottom = Math.max(tabbarData.height * 2 - 4, 0);
this.$set(this, 'contentBottom', adjustedBottom + 'rpx');
} else {
this.$set(this, 'contentBottom', '96rpx');
}
})
.exec();
setTimeout(() => {
const q2 = uni.createSelectorQuery().in(this);
q2
.select('.uni-navbar__content')
.boundingClientRect((data) => {
if (data && data.height) {
const adjustedHeight = Math.max(data.height * 2 - 4, 0);
this.$set(this, 'contentTop', adjustedHeight + 'rpx');
}
})
.exec();
const qTab2 = uni.createSelectorQuery().in(this);
qTab2
.select('.tabbar')
.boundingClientRect((tabbarData) => {
if (tabbarData && tabbarData.height) {
const adjustedBottom = Math.max(tabbarData.height * 2 - 4, 0);
this.$set(this, 'contentBottom', adjustedBottom + 'rpx');
}
})
.exec();
}, 200);
});
},
methods: {
// #ifdef MP-WEIXIN
preloadSubpackage() {
if (typeof wx !== 'undefined' && wx.loadSubpackage) {
wx.loadSubpackage({
name: 'pages-subpackage',
success: () => console.log('[Xixi] subpackage loaded'),
fail: (err) => console.warn('[Xixi] subpackage preload:', err),
});
}
},
// #endif
updateNavbarPosition() {
const systemInfo = getSystemInfo();
const totalNavbarHeight = systemInfo.statusBarHeight * 2 + 44 * 2;
this.$set(this, 'contentTop', totalNavbarHeight + 'rpx');
// #ifdef MP-WEIXIN
this.$forceUpdate();
// #endif
},
onNavLeftClick() {
if (this.viewMode === 'detail') {
this.backHome();
return;
}
const pages = getCurrentPages();
if (pages.length <= 1) {
return;
}
uni.navigateBack({
fail: (err) => console.error('[Xixi] navigateBack:', err),
});
},
openDetail(key) {
this.detailKey = key;
this.viewMode = 'detail';
if (key === 'diag' || key === 'record') {
this.diagTick += 1;
}
if (key === 'advice') {
this.adviceRefreshTick += 1;
}
},
backHome() {
this.viewMode = 'home';
this.detailKey = null;
},
onFormSaved() {},
onRecordingsChanged() {
this.diagTick += 1;
},
},
};
</script>
<style scoped>
page {
background-color: #f5f5f5;
}
.page {
min-height: 100vh;
background-color: #f5f5f5;
}
.content {
padding-top: 0 !important;
margin-top: 0 !important;
position: relative;
min-height: 100vh;
top: 0;
}
::v-deep .uni-navbar__placeholder {
display: none !important;
height: 0 !important;
}
::v-deep .uni-navbar__placeholder-view {
display: none !important;
height: 0 !important;
}
::v-deep [class*='tabbar'][class*='placeholder'] {
display: none !important;
height: 0 !important;
}
::v-deep [class*='safe-area'][class*='bottom'],
::v-deep [class*='bottom'][class*='safe'] {
display: none !important;
height: 0 !important;
}
.content-scroll {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
padding: 0 !important;
padding-bottom: 0 !important;
margin: 0 !important;
box-sizing: border-box;
}
.detail-wrap {
position: absolute;
left: 0;
right: 0;
overflow: hidden;
box-sizing: border-box;
background-color: #f5f5f5;
}
.detail-panel {
height: 100%;
width: 100%;
box-sizing: border-box;
}
.category-section {
margin: 20rpx 30rpx;
background: #ffffff;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
}
.category-section:first-child {
margin-top: 0;
}
.category-section:last-child {
margin-bottom: 0;
}
.category-header {
display: flex;
align-items: center;
padding: 30rpx;
background: #ffffff;
border-bottom: 1rpx solid #f0f0f0;
}
.category-icon {
font-size: 32rpx;
margin-right: 20rpx;
}
.category-title {
font-size: 32rpx;
font-weight: 500;
color: #333;
}
.function-grid {
display: flex;
flex-wrap: wrap;
padding: 10rpx;
}
.two-column .function-item {
flex: 1;
background: #ffffff;
border-radius: 12rpx;
padding: 24rpx 16rpx;
margin: 0 5rpx;
text-align: center;
box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.04);
transition: all 0.3s ease;
border: 1rpx solid #f0f0f0;
}
.function-item:active {
transform: scale(0.95);
box-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.1);
}
.function-icon {
font-size: 48rpx;
margin-bottom: 12rpx;
}
.function-name {
display: block;
font-size: 28rpx;
font-weight: 500;
color: #333;
margin-bottom: 8rpx;
}
.function-desc {
display: block;
font-size: 24rpx;
color: #666;
line-height: 1.4;
}
.advice-full {
padding: 0 15rpx 18rpx;
box-sizing: border-box;
}
.advice-full-inner {
display: flex;
flex-direction: row;
align-items: center;
background: linear-gradient(135deg, #fff8fa 0%, #ffffff 100%);
border-radius: 12rpx;
padding: 24rpx 20rpx;
margin: 0 5rpx;
border: 1rpx solid #f5d0e0;
box-shadow: 0 2rpx 8rpx rgba(233, 30, 140, 0.08);
}
.advice-full:active .advice-full-inner {
transform: scale(0.98);
opacity: 0.96;
}
.advice-full-icon {
margin-bottom: 0;
margin-right: 20rpx;
flex-shrink: 0;
}
.advice-full-texts {
flex: 1;
min-width: 0;
text-align: left;
}
.advice-full-texts .function-name,
.advice-full-texts .function-desc {
text-align: left;
}
</style>