Files
smartDriveEEUniApp/pages-subpackage/ai_features/deal_attribution/deal_attribution.vue
2026-02-07 19:51:03 +08:00

360 lines
10 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="feature-page">
<view class="page-header" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="nav-bar">
<view class="nav-left" @click="goBack">
<text class="back-icon"></text>
</view>
<text class="nav-title">成交归因分析</text>
</view>
</view>
<scroll-view class="content-scroll" scroll-y="true" :style="{ top: contentTop, bottom: contentBottom }">
<view class="stats-card">
<view class="stat-item">
<text class="stat-value">{{ conversionRate }}%</text>
<text class="stat-label">总体转化率</text>
</view>
<view class="stat-item">
<text class="stat-value">{{ totalDeals }}</text>
<text class="stat-label">成交总数</text>
</view>
</view>
<view class="chart-section">
<text class="section-title">成交因素分析图</text>
<view class="chart-placeholder">
<text class="placeholder-text">📊 图表区域</text>
<text class="placeholder-desc">此处将显示成交归因分析图表</text>
</view>
</view>
<view class="data-list">
<text class="section-title">关键成交因素</text>
<view class="list-item" v-for="(item, index) in dataList" :key="index">
<view class="item-left">
<text class="item-date">{{ item.factor }}</text>
<text class="item-desc">{{ item.description }}</text>
</view>
<view class="item-right">
<text class="item-percentage">{{ item.percentage }}%</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
// 获取系统信息的辅助函数
function getSystemInfo() {
// #ifdef MP-WEIXIN
// 微信小程序:优先使用新的 API
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('[DealAttribution] Failed to use wx.getWindowInfo, fallback to getSystemInfoSync:', e);
}
}
// #endif
// 回退到旧的 API 或使用 uni-app 的 API
try {
const systemInfo = uni.getSystemInfoSync();
return {
statusBarHeight: systemInfo.statusBarHeight || 20,
windowWidth: systemInfo.windowWidth,
windowHeight: systemInfo.windowHeight
};
} catch (e) {
console.error('[DealAttribution] Failed to get system info:', e);
return {
statusBarHeight: 20,
windowWidth: 375,
windowHeight: 667
};
}
}
export default {
data() {
// 动态计算导航栏高度
const systemInfo = getSystemInfo();
const statusBarHeight = systemInfo.statusBarHeight;
const navbarHeight = 44; // navbar高度44px
// 转换为rpx1px = 2rpx在375px设计稿下
const statusBarHeightRpx = statusBarHeight * 2;
const navbarHeightRpx = navbarHeight * 2;
const totalNavbarHeight = statusBarHeightRpx + navbarHeightRpx;
return {
statusBarHeight: statusBarHeight, // 状态栏高度px
contentTop: totalNavbarHeight + 'rpx', // 内容区域距离顶部的距离
contentBottom: '0rpx', // 内容区域距离底部的距离(默认值,会在 onReady 中更新)
conversionRate: '15.8',
totalDeals: '201',
dataList: [
{ factor: '产品优势', description: '突出产品核心优势', percentage: '35.2' },
{ factor: '价格优惠', description: '提供合适的价格优惠', percentage: '28.4' },
{ factor: '服务质量', description: '优质的售后服务', percentage: '18.7' },
{ factor: '客户关系', description: '良好的客户关系维护', percentage: '12.3' },
{ factor: '竞争优势', description: '相对于竞品的优势', percentage: '5.4' }
]
}
},
onReady() {
// 页面渲染完成后,查询实际导航栏高度并更新位置
this.$nextTick(() => {
// 查询导航栏实际高度
const query = uni.createSelectorQuery().in(this);
query.select('.nav-bar').boundingClientRect((data) => {
if (data && data.height) {
// 使用实际高度
const actualHeight = data.height; // 实际导航栏高度px
// 转换为rpx1px = 2rpx在375px设计稿下
const actualHeightRpx = actualHeight * 2;
const statusBarHeightRpx = this.statusBarHeight * 2;
const totalHeight = statusBarHeightRpx + actualHeightRpx;
// 稍微减小一点,确保紧贴
const adjustedHeight = Math.max(totalHeight - 4, 0);
this.$set(this, 'contentTop', adjustedHeight + 'rpx');
console.log('[DealAttribution] 使用实际导航栏高度:', actualHeight, 'px =', actualHeightRpx, 'rpx, 总高度:', totalHeight, 'rpx, 调整后:', adjustedHeight, 'rpx');
} else {
// 如果查询失败,使用默认计算
const systemInfo = getSystemInfo();
const statusBarHeight = systemInfo.statusBarHeight;
const navbarHeight = 44;
const statusBarHeightRpx = statusBarHeight * 2;
const navbarHeightRpx = navbarHeight * 2;
const totalNavbarHeight = statusBarHeightRpx + navbarHeightRpx;
const adjustedHeight = Math.max(totalNavbarHeight - 4, 0);
this.$set(this, 'contentTop', adjustedHeight + 'rpx');
}
}).exec();
// 查询底部 tabbar 实际高度(如果有)
const queryTabbar = uni.createSelectorQuery().in(this);
queryTabbar.select('.tabbar').boundingClientRect((tabbarData) => {
if (tabbarData && tabbarData.height) {
// 使用实际高度
const tabbarHeight = tabbarData.height; // tabbar 实际高度px
const tabbarHeightRpx = tabbarHeight * 2;
// 稍微减小一点,确保紧贴
const adjustedBottom = Math.max(tabbarHeightRpx - 4, 0);
this.$set(this, 'contentBottom', adjustedBottom + 'rpx');
console.log('[DealAttribution] 使用实际tabbar高度:', tabbarHeight, 'px =', tabbarHeightRpx, 'rpx, 调整后:', adjustedBottom, 'rpx');
} else {
// 如果查询失败使用默认值无tabbar
this.$set(this, 'contentBottom', '0rpx');
}
}).exec();
// 延迟再次更新确保DOM已渲染
setTimeout(() => {
// 再次查询并更新导航栏
const query2 = uni.createSelectorQuery().in(this);
query2.select('.nav-bar').boundingClientRect((data) => {
if (data && data.height) {
const actualHeight = data.height;
const actualHeightRpx = actualHeight * 2;
const statusBarHeightRpx = this.statusBarHeight * 2;
const totalHeight = statusBarHeightRpx + actualHeightRpx;
const adjustedHeight = Math.max(totalHeight - 4, 0);
this.$set(this, 'contentTop', adjustedHeight + 'rpx');
console.log('[DealAttribution] 延迟更新导航栏高度:', actualHeight, 'px =', actualHeightRpx, 'rpx, 总高度:', totalHeight, 'rpx, 调整后:', adjustedHeight, 'rpx');
}
}).exec();
// 再次查询 tabbar 高度
const queryTabbar2 = uni.createSelectorQuery().in(this);
queryTabbar2.select('.tabbar').boundingClientRect((tabbarData) => {
if (tabbarData && tabbarData.height) {
const tabbarHeight = tabbarData.height;
const tabbarHeightRpx = tabbarHeight * 2;
const adjustedBottom = Math.max(tabbarHeightRpx - 4, 0);
this.$set(this, 'contentBottom', adjustedBottom + 'rpx');
console.log('[DealAttribution] 延迟更新tabbar高度:', tabbarHeight, 'px =', tabbarHeightRpx, 'rpx, 调整后:', adjustedBottom, 'rpx');
}
}).exec();
}, 200);
});
},
methods: {
goBack() {
uni.navigateBack()
}
}
}
</script>
<style scoped>
.feature-page {
min-height: 100vh;
background: #f8f9fa;
}
.page-header {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 999;
background: #ffffff;
border-bottom: 1rpx solid #e9ecef;
}
.nav-bar {
display: flex;
align-items: center;
height: 88rpx;
padding: 0 30rpx;
}
.nav-left {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
}
.back-icon {
font-size: 36rpx;
color: #333333;
}
.nav-title {
flex: 1;
text-align: center;
font-size: 32rpx;
font-weight: bold;
color: #333333;
}
.content-scroll {
position: absolute;
left: 0;
right: 0;
top: 0; /* 先从顶部开始,通过内联样式动态设置 */
bottom: 0; /* 先从底部开始,通过内联样式动态设置 */
/* top和bottom值通过内联样式动态设置覆盖上面的默认值 */
padding: 0 !important;
margin: 0 !important;
box-sizing: border-box;
}
.stats-card {
display: flex;
margin: 20rpx 30rpx;
background: #ffffff;
border-radius: 16rpx;
padding: 40rpx 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.stat-item {
flex: 1;
text-align: center;
}
.stat-item:not(:last-child) {
border-right: 1rpx solid #e9ecef;
}
.stat-value {
display: block;
font-size: 48rpx;
font-weight: bold;
color: #007aff;
margin-bottom: 10rpx;
}
.stat-label {
display: block;
font-size: 24rpx;
color: #666666;
}
.chart-section, .data-list {
margin: 20rpx 30rpx;
background: #ffffff;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.section-title {
display: block;
font-size: 32rpx;
font-weight: bold;
color: #333333;
padding: 30rpx;
border-bottom: 1rpx solid #e9ecef;
}
.chart-placeholder {
padding: 80rpx 30rpx;
text-align: center;
}
.placeholder-text {
display: block;
font-size: 48rpx;
margin-bottom: 20rpx;
}
.placeholder-desc {
display: block;
font-size: 28rpx;
color: #666666;
}
.list-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.list-item:last-child {
border-bottom: none;
}
.item-left {
flex: 1;
}
.item-date {
display: block;
font-size: 28rpx;
font-weight: bold;
color: #333333;
margin-bottom: 8rpx;
}
.item-desc {
display: block;
font-size: 24rpx;
color: #666666;
}
.item-right {
text-align: right;
}
.item-percentage {
display: block;
font-size: 28rpx;
font-weight: bold;
color: #007aff;
}
</style>