AI问答框架

This commit is contained in:
zhonghua.li
2026-03-29 20:11:22 +08:00
parent 703fc70a17
commit d14ef1ef05
17 changed files with 2055 additions and 16 deletions

380
pages/ai_qa/ai_qa.vue Normal file
View File

@@ -0,0 +1,380 @@
<template>
<view class="page">
<!-- #ifdef APP -->
<statusBar></statusBar>
<!-- #endif -->
<uni-nav-bar
:fixed="true"
:statusBar="true"
title="AI问答"
leftIcon="left"
@clickLeft="goBack"
color="#333"
backgroundColor="#FFFFFF">
</uni-nav-bar>
<view class="content">
<scroll-view 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="navigateToQa('general_qa')">
<view class="function-icon">🙋</view>
<text class="function-name">通用问答</text>
<text class="function-desc">日常接待与通用话术</text>
</view>
<view class="function-item" @click="navigateToQa('after_sales_qa')">
<view class="function-icon">🛠</view>
<text class="function-name">售后问答</text>
<text class="function-desc">安装质保与售后流程</text>
</view>
</view>
</view>
<view class="category-section sales-analysis">
<view class="category-header">
<text class="category-icon">🎯</text>
<text class="category-title">需求与信任</text>
</view>
<view class="function-grid">
<view class="function-item full-row-item" @click="navigateToQa('demand_qa')">
<view class="function-icon">📌</view>
<text class="function-name">需求类问答</text>
<text class="function-desc">是否匹配需求场景方案</text>
</view>
<view class="function-item full-row-item" @click="navigateToQa('price_qa')">
<view class="function-icon">💳</view>
<text class="function-name">价格类问答</text>
<text class="function-desc">价格优惠与性价比</text>
</view>
<view class="function-item full-row-item" @click="navigateToQa('trust_qa')">
<view class="function-icon">🤝</view>
<text class="function-name">信任类问答</text>
<text class="function-desc">质量售后与案例资质</text>
</view>
<view class="function-item full-row-item" @click="navigateToQa('decision_qa')">
<view class="function-icon"></view>
<text class="function-name">决策类问答</text>
<text class="function-desc">再考虑犹豫与促单</text>
</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="navigateToQa('competitor_qa')">
<view class="function-icon">🔀</view>
<text class="function-name">竞品类问答</text>
<text class="function-desc">与竞品对比与选型</text>
</view>
<view class="function-item" @click="navigateToQa('objection_qa')">
<view class="function-icon">🚧</view>
<text class="function-name">抗拒点问答</text>
<text class="function-desc">拖延借口与隐性拒绝</text>
</view>
<view class="function-item full-span" @click="navigateToQa('sharp_qa')">
<view class="function-icon">📍</view>
<text class="function-name">针尖问答</text>
<text class="function-desc">尖锐与刁难型问题</text>
</view>
</view>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
// #ifdef APP
import statusBar from "@/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar";
// #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('[AIQa] wx.getWindowInfo fallback:', e);
}
}
// #endif
try {
const systemInfo = uni.getSystemInfoSync();
return {
statusBarHeight: systemInfo.statusBarHeight || 20,
windowWidth: systemInfo.windowWidth,
windowHeight: systemInfo.windowHeight
};
} catch (e) {
console.error('[AIQa] getSystemInfoSync:', e);
return { statusBarHeight: 20, windowWidth: 375, windowHeight: 667 };
}
}
export default {
// #ifdef APP
components: { statusBar },
// #endif
data() {
const systemInfo = getSystemInfo();
const statusBarHeightRpx = systemInfo.statusBarHeight * 2;
const navbarHeightRpx = 44 * 2;
const totalNavbarHeight = statusBarHeightRpx + navbarHeightRpx;
return {
contentTop: totalNavbarHeight + 'rpx',
contentBottom: '96rpx',
};
},
computed: {
computedContentTop() {
const s = getSystemInfo();
return (s.statusBarHeight * 2 + 44 * 2) + 'rpx';
}
},
onLoad() {
this.updateNavbarPosition();
// #ifdef MP-WEIXIN
setTimeout(() => this.preloadSubpackage(), 100);
// #endif
},
onShow() {
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 adjusted = Math.max(data.height * 2 - 4, 0);
this.$set(this, 'contentTop', adjusted + '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');
}
}).exec();
setTimeout(() => {
const q2 = uni.createSelectorQuery().in(this);
q2.select('.uni-navbar__content').boundingClientRect((data) => {
if (data && data.height) {
this.$set(this, 'contentTop', Math.max(data.height * 2 - 4, 0) + 'rpx');
}
}).exec();
const qt2 = uni.createSelectorQuery().in(this);
qt2.select('.tabbar').boundingClientRect((d) => {
if (d && d.height) {
this.$set(this, 'contentBottom', Math.max(d.height * 2 - 4, 0) + 'rpx');
}
}).exec();
}, 200);
});
},
methods: {
// #ifdef MP-WEIXIN
preloadSubpackage() {
if (typeof wx !== 'undefined' && wx.loadSubpackage) {
wx.loadSubpackage({
name: 'pages-subpackage',
success: () => {},
fail: (err) => console.error('[AIQa] loadSubpackage:', err)
});
}
},
// #endif
updateNavbarPosition() {
const s = getSystemInfo();
const total = s.statusBarHeight * 2 + 44 * 2;
this.$set(this, 'contentTop', total + 'rpx');
// #ifdef MP-WEIXIN
this.$forceUpdate();
// #endif
},
goBack() {
const pages = getCurrentPages();
if (pages.length <= 1) return;
uni.navigateBack({
fail: (err) => console.error('[AIQa] goBack:', err)
});
},
navigateToQa(slug) {
uni.navigateTo({
url: `/pages-subpackage/ai_qa/${slug}/${slug}`,
fail: (err) => {
console.error('[AIQa] navigate:', err);
uni.showToast({ title: '页面跳转失败', icon: 'none' });
}
});
},
}
};
</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;
margin: 0 !important;
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-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.full-span {
flex: 1 1 100%;
margin-top: 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;
}
.sales-analysis .function-item.full-row-item {
flex: 1;
width: calc(50% - 10rpx);
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);
border: 1rpx solid #F0F0F0;
}
.sales-analysis .function-item.new-line-item {
flex-basis: 100%;
width: calc(50% - 10rpx);
margin-top: 10rpx;
}
.function-item:active {
transform: scale(0.95);
}
.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;
}
</style>

View File

@@ -2493,7 +2493,8 @@
this.urlPathMap = {
'pages/furniture_customer/furniture_customer': '/customer',
'pages/furniture_reception/furniture_reception': '/reception',
'pages/furniture_top_sales/furniture_top_sales': '/top-sales',
'pages/ai_qa/ai_qa': '/ai-qa',
'pages/ai_analysis/ai_analysis': '/ai-analysis',
'pages/ucenter/ucenter': '/profile'
};

View File

@@ -985,7 +985,8 @@ import ServiceListFurniture from "./serviceListFurniture.vue";
this.urlPathMap = {
'pages/furniture_customer/furniture_customer': '/customer',
'pages/furniture_reception/furniture_reception': '/reception',
'pages/furniture_top_sales/furniture_top_sales': '/top-sales',
'pages/ai_qa/ai_qa': '/ai-qa',
'pages/ai_analysis/ai_analysis': '/ai-analysis',
'pages/ucenter/ucenter': '/profile'
};

View File

@@ -375,7 +375,7 @@
this[item.event]();
} else if (item.to) {
// 如果是 tabBar 页面,使用 switchTab
const tabBarPages = ['/pages/furniture_reception/furniture_reception', '/pages/furniture_customer/furniture_customer', '/pages/furniture_top_sales/furniture_top_sales', '/pages/ucenter/ucenter']
const tabBarPages = ['/pages/furniture_reception/furniture_reception', '/pages/furniture_customer/furniture_customer', '/pages/ai_qa/ai_qa', '/pages/ai_analysis/ai_analysis', '/pages/ucenter/ucenter']
if (tabBarPages.includes(item.to)) {
uni.switchTab({
url: item.to
@@ -700,7 +700,8 @@
this.urlPathMap = {
'pages/furniture_customer/furniture_customer': '/customer',
'pages/furniture_reception/furniture_reception': '/reception',
'pages/furniture_top_sales/furniture_top_sales': '/top-sales',
'pages/ai_qa/ai_qa': '/ai-qa',
'pages/ai_analysis/ai_analysis': '/ai-analysis',
'pages/ucenter/ucenter': '/profile'
};