mirror of
http://112.124.100.131/ebiz-ai/ebiz-base-ai.git
synced 2025-12-14 21:36:49 +08:00
feat(product): 添加产品推荐功能并优化相关页面
- 新增产品推荐页面,提供个性化的产品推荐服务 - 优化首页布局,增加产品推荐入口 - 调整聊天组件,支持产品推荐功能 - 优化样式文件,统一主题颜色和样式 - 更新路由配置,添加产品推荐路由
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
@import './utils.scss';
|
@import './utils.scss';
|
||||||
@import './transition.scss';
|
@import './transition.scss';
|
||||||
@import './public.scss';
|
@import './public.scss';
|
||||||
@import "./loading.scss";
|
@import './loading.scss';
|
||||||
|
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -33,7 +33,7 @@ a:hover {
|
|||||||
outline: none;
|
outline: none;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
::-webkit-scrollbar{
|
::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
// 宽度设置
|
// 宽度设置
|
||||||
|
|||||||
@@ -1,61 +1,62 @@
|
|||||||
body{
|
body {
|
||||||
background-color: #EEEEEE!important;
|
background-color: #eeeeee !important;
|
||||||
}
|
}
|
||||||
.van-cell-group--inset{
|
.van-cell-group--inset {
|
||||||
margin:0 10px!important;
|
margin: 0 10px !important;
|
||||||
}
|
}
|
||||||
.van-cell-group__title--inset{
|
.van-cell-group__title--inset {
|
||||||
padding:15px 15px 5px 15px!important;
|
padding: 15px 15px 5px 15px !important;
|
||||||
}
|
}
|
||||||
.van-toast--text {
|
.van-toast--text {
|
||||||
max-width: 70vw !important;
|
max-width: 70vw !important;
|
||||||
min-width: 60vw !important;
|
min-width: 60vw !important;
|
||||||
}
|
}
|
||||||
.button-group-container{
|
.button-group-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
#app .van-col{
|
#app .van-col {
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
}
|
}
|
||||||
#app .button-group-container{
|
#app .button-group-container {
|
||||||
margin: 5px!important;
|
margin: 5px !important;
|
||||||
.van-button{
|
.van-button {
|
||||||
margin: unset;
|
margin: unset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#app .van-button + .van-button{
|
#app .van-button + .van-button {
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
#app .van-cell__title{
|
#app .van-cell__title {
|
||||||
color: #646566;
|
color: #646566;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flexPrice {
|
||||||
.flexPrice{
|
display: flex;
|
||||||
display: flex; justify-content: space-between; justify-items: center; align-items: center; width: 100%
|
justify-content: space-between;
|
||||||
|
justify-items: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
.van-tag+.van-tag{margin-left: 5px;}
|
.van-tag + .van-tag {
|
||||||
.speakLoadingToast{
|
margin-left: 5px;
|
||||||
z-index: 2100!important;
|
}
|
||||||
|
.speakLoadingToast {
|
||||||
|
z-index: 2100 !important;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gs-color-g {
|
||||||
|
|
||||||
.gs-color-g{
|
|
||||||
color: #009960;
|
color: #009960;
|
||||||
}
|
}
|
||||||
.gs-bg-color-g{
|
.gs-bg-color-g {
|
||||||
background: #009960;
|
background: #009960;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chat-icon {
|
||||||
.chat-icon{
|
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
margin-left: 0!important;
|
margin-left: 0 !important;
|
||||||
margin-right: 2px!important;
|
margin-right: 2px !important;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,4 +23,12 @@ export default [
|
|||||||
title: '产品对比',
|
title: '产品对比',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/recommend',
|
||||||
|
name: 'recommend',
|
||||||
|
component: () => import('@/views/recommend/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '产品推荐',
|
||||||
|
},
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -90,6 +90,10 @@ export default {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
chatData: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -220,6 +224,12 @@ export default {
|
|||||||
conversationId: this.conversationId,
|
conversationId: this.conversationId,
|
||||||
productName: this.productName,
|
productName: this.productName,
|
||||||
}
|
}
|
||||||
|
// 如果有自定义参数
|
||||||
|
if (this.chatData) {
|
||||||
|
for (let k in this.chatData) {
|
||||||
|
params[k] = this.chatData[k]
|
||||||
|
}
|
||||||
|
}
|
||||||
if (this.$route.query.compareId) {
|
if (this.$route.query.compareId) {
|
||||||
params.compareResult = JSON.parse(sessionStorage.getItem('results'))
|
params.compareResult = JSON.parse(sessionStorage.getItem('results'))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="home-container">
|
<div class="home-container">
|
||||||
<van-swipe class="my-swipe">
|
<van-swipe class="my-swipe">
|
||||||
<van-swipe-item class="item">
|
<van-swipe-item class="item" v-for="swiper in swiper">
|
||||||
<div v-for="item in navigationItems" @click="jumpPage(item)">
|
<div v-for="item in swiper" @click="jumpPage(item)">
|
||||||
<div class="icon-contact mt20">
|
<div class="icon-contact mt20">
|
||||||
<svg-icon :icon-class="item.icon" class-name="icon "></svg-icon>
|
<svg-icon :icon-class="item.icon" class-name="icon "></svg-icon>
|
||||||
</div>
|
</div>
|
||||||
@@ -39,10 +39,14 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
navigationItems: [
|
swiper: [
|
||||||
{ title: 'AI智能助手', icon: 'product', path: '/chatPage' },
|
[
|
||||||
{ title: 'AI客服助手', icon: 'sale', path: '/customer' },
|
{ title: 'AI智能助手', icon: 'product', path: '/chatPage' },
|
||||||
{ title: '产品对比', icon: 'earth', path: '/comparison' },
|
{ title: '产品对比', icon: 'earth', path: '/comparison' },
|
||||||
|
{ title: '产品推荐', icon: 'product', path: '/recommend' },
|
||||||
|
],
|
||||||
|
|
||||||
|
[{ title: 'AI客服助手', icon: 'sale', path: '/customer' }],
|
||||||
],
|
],
|
||||||
list: [
|
list: [
|
||||||
{
|
{
|
||||||
@@ -63,6 +67,17 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
// 主题颜色定义
|
||||||
|
$primary-color: #2e5ca9;
|
||||||
|
$primary-text-color: #f6aa21;
|
||||||
|
$primary-trans-color: #87a2d0;
|
||||||
|
.van-nav-bar__text,
|
||||||
|
.van-nav-bar .van-icon {
|
||||||
|
color: $primary-color !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
// 主题颜色定义
|
// 主题颜色定义
|
||||||
$primary-color: #2e5ca9;
|
$primary-color: #2e5ca9;
|
||||||
|
|||||||
157
src/views/recommend/index.vue
Normal file
157
src/views/recommend/index.vue
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
<template>
|
||||||
|
<div class="chat-page">
|
||||||
|
<van-nav-bar title="产品推荐" left-text="返回" left-arrow @click-left="$router.history.go(-1)" />
|
||||||
|
<main class="chat-main">
|
||||||
|
<div class="chat-content">
|
||||||
|
<div class="message-area" ref="messageArea" @scroll="handleScroll">
|
||||||
|
<!-- -->
|
||||||
|
<messageComponent
|
||||||
|
:messagesList="messages"
|
||||||
|
:is-deep="isDeep"
|
||||||
|
:is-search="isSearching"
|
||||||
|
:think-ok="isThink"
|
||||||
|
@setProductName="setProductName"
|
||||||
|
></messageComponent>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 滚动到顶部按钮 -->
|
||||||
|
<div class="button-container" style="background: #fff">
|
||||||
|
<van-icon name="upgrade" size="2em" @click="scrollToTop" />
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<chatMessage
|
||||||
|
:messages.sync="messages"
|
||||||
|
:messageStatus.sync="messageStatus"
|
||||||
|
:is-deep.sync="isDeep"
|
||||||
|
:conversation-id.sync="conversationId"
|
||||||
|
:is-searching.sync="isSearching"
|
||||||
|
:product-name.sync="productName"
|
||||||
|
:autoScrollEnabled.sync="autoScrollEnabled"
|
||||||
|
:chatData="{ action: 'product_recommand' }"
|
||||||
|
@getIsThink="getIsThink"
|
||||||
|
></chatMessage>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { Icon, NavBar } from 'vant'
|
||||||
|
import SvgIcon from '@/components/svg-icon/index.vue'
|
||||||
|
import HotProducts from '@/views/AI/components/HotProducts.vue'
|
||||||
|
import chatMessage from '@/views/AI/components/chat.vue'
|
||||||
|
import messageComponent from '@/views/AI/components/message.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
SvgIcon,
|
||||||
|
messageComponent,
|
||||||
|
[Icon.name]: Icon,
|
||||||
|
[NavBar.name]: NavBar,
|
||||||
|
HotProducts,
|
||||||
|
chatMessage,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
hotList: [],
|
||||||
|
productName: '',
|
||||||
|
conversationId: '',
|
||||||
|
messageStatus: 'stop',
|
||||||
|
isThink: null,
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
type: 'bot',
|
||||||
|
text: '欢迎使用产品推荐智能助手,我将推荐符合您的产品',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
isSearching: false,
|
||||||
|
isDeep: false,
|
||||||
|
autoScrollEnabled: true,
|
||||||
|
scrollPosition: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getIsThink(e) {
|
||||||
|
this.isThink = e
|
||||||
|
},
|
||||||
|
getHotProducts(e) {
|
||||||
|
console.log(e)
|
||||||
|
this.hotList = e
|
||||||
|
},
|
||||||
|
|
||||||
|
scrollToTop() {
|
||||||
|
const messageArea = this.$refs.messageArea
|
||||||
|
if (messageArea) {
|
||||||
|
messageArea.scrollTop = 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scrollToBottom() {
|
||||||
|
if (!this.autoScrollEnabled) return
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const messageArea = this.$refs.messageArea
|
||||||
|
if (messageArea) {
|
||||||
|
messageArea.scrollTop = messageArea.scrollHeight
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
setProductName(e) {
|
||||||
|
this.productName = e
|
||||||
|
},
|
||||||
|
handleScroll() {
|
||||||
|
const messageArea = this.$refs.messageArea
|
||||||
|
if (!messageArea) return
|
||||||
|
const threshold = 10
|
||||||
|
const isAtBottom = messageArea.scrollHeight - messageArea.clientHeight <= messageArea.scrollTop + threshold
|
||||||
|
this.autoScrollEnabled = isAtBottom
|
||||||
|
this.scrollPosition = messageArea.scrollTop
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
messages: {
|
||||||
|
handler() {
|
||||||
|
this.$nextTick(() => this.scrollToBottom())
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
$primary-color: #2e5ca9;
|
||||||
|
$primary-text-color: #f6aa21;
|
||||||
|
$primary-trans-color: rgba(135, 162, 208, 0.5);
|
||||||
|
|
||||||
|
.chat-page {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100vh;
|
||||||
|
//-webkit-user-select: none;
|
||||||
|
//-moz-user-select: none;
|
||||||
|
//-ms-user-select: none;
|
||||||
|
//user-select: none;
|
||||||
|
|
||||||
|
.chat-main {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 10px;
|
||||||
|
background: #f7f8fa;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.button-container {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 150px;
|
||||||
|
right: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-content {
|
||||||
|
height: 100%;
|
||||||
|
.message-area {
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
const autoprefixer = require('autoprefixer');
|
const autoprefixer = require('autoprefixer')
|
||||||
const pxtoviewport = require('postcss-px-to-viewport');
|
const pxtoviewport = require('postcss-px-to-viewport')
|
||||||
const path = require('path');
|
const path = require('path')
|
||||||
|
|
||||||
function resolve(dir) {
|
function resolve(dir) {
|
||||||
return path.join(__dirname, dir);
|
return path.join(__dirname, dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@@ -12,37 +12,36 @@ module.exports = {
|
|||||||
outputDir: 'dist',
|
outputDir: 'dist',
|
||||||
productionSourceMap: false,
|
productionSourceMap: false,
|
||||||
devServer: {
|
devServer: {
|
||||||
https: false,
|
https: true,
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
disableHostCheck: true,
|
disableHostCheck: true,
|
||||||
},
|
},
|
||||||
css: {
|
css: {
|
||||||
sourceMap: true,
|
sourceMap: true,
|
||||||
loaderOptions: {
|
loaderOptions: {
|
||||||
postcss: {
|
postcss: {
|
||||||
postcssOptions: {
|
postcssOptions: {
|
||||||
plugins: [
|
plugins: [
|
||||||
autoprefixer(), // 自动加浏览器前缀
|
autoprefixer(), // 自动加浏览器前缀
|
||||||
pxtoviewport({ // px 转 viewport
|
pxtoviewport({
|
||||||
|
// px 转 viewport
|
||||||
viewportWidth: 375,
|
viewportWidth: 375,
|
||||||
selectorBlackList: ['van-circle__layer'] // 排除vant circle组件样式转换
|
selectorBlackList: ['van-circle__layer'], // 排除vant circle组件样式转换
|
||||||
})
|
}),
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
chainWebpack: (config) => {
|
chainWebpack: (config) => {
|
||||||
// 设置路径别名
|
// 设置路径别名
|
||||||
config.resolve.alias
|
config.resolve.alias.set('@', resolve('src')).set('@utils', resolve('src/assets/js/utils'))
|
||||||
.set('@', resolve('src'))
|
|
||||||
.set('@utils', resolve('src/assets/js/utils'));
|
|
||||||
|
|
||||||
// 移除 prefetch 插件(减少初始加载体积)
|
// 移除 prefetch 插件(减少初始加载体积)
|
||||||
config.plugins.delete('prefetch');
|
config.plugins.delete('prefetch')
|
||||||
|
|
||||||
// SVG 图标配置
|
// SVG 图标配置
|
||||||
config.module.rule('svg').exclude.add(resolve('src/icons')).end();
|
config.module.rule('svg').exclude.add(resolve('src/icons')).end()
|
||||||
config.module
|
config.module
|
||||||
.rule('icons')
|
.rule('icons')
|
||||||
.test(/\.svg$/)
|
.test(/\.svg$/)
|
||||||
@@ -52,7 +51,7 @@ module.exports = {
|
|||||||
.loader('svg-sprite-loader')
|
.loader('svg-sprite-loader')
|
||||||
.options({
|
.options({
|
||||||
symbolId: 'icon-[name]',
|
symbolId: 'icon-[name]',
|
||||||
});
|
})
|
||||||
|
|
||||||
// 处理 .mjs 文件(用于 mermaid 等模块)
|
// 处理 .mjs 文件(用于 mermaid 等模块)
|
||||||
config.module
|
config.module
|
||||||
@@ -61,7 +60,7 @@ module.exports = {
|
|||||||
.include.add(/node_modules/)
|
.include.add(/node_modules/)
|
||||||
.end()
|
.end()
|
||||||
.use('babel-loader')
|
.use('babel-loader')
|
||||||
.loader('babel-loader');
|
.loader('babel-loader')
|
||||||
|
|
||||||
// 添加对现代 JS 的支持(如 ?? 和 ?.)
|
// 添加对现代 JS 的支持(如 ?? 和 ?.)
|
||||||
config.module
|
config.module
|
||||||
@@ -71,19 +70,19 @@ module.exports = {
|
|||||||
.loader('babel-loader')
|
.loader('babel-loader')
|
||||||
.options({
|
.options({
|
||||||
presets: ['@babel/preset-env'],
|
presets: ['@babel/preset-env'],
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
configureWebpack: (config) => {
|
configureWebpack: (config) => {
|
||||||
// 强制使用 CommonJS 模块加载 mermaid
|
// 强制使用 CommonJS 模块加载 mermaid
|
||||||
config.resolve = config.resolve || {};
|
config.resolve = config.resolve || {}
|
||||||
config.resolve.alias = config.resolve.alias || {};
|
config.resolve.alias = config.resolve.alias || {}
|
||||||
// config.resolve.alias.mermaid$ = path.resolve(
|
// config.resolve.alias.mermaid$ = path.resolve(
|
||||||
// __dirname,
|
// __dirname,
|
||||||
// './node_modules/mermaid/dist/mermaid.common.js'
|
// './node_modules/mermaid/dist/mermaid.common.js'
|
||||||
// );
|
// );
|
||||||
|
|
||||||
// Webpack devtool 设置
|
// Webpack devtool 设置
|
||||||
config.devtool = 'source-map';
|
config.devtool = 'source-map'
|
||||||
|
|
||||||
// 打包性能提示设置
|
// 打包性能提示设置
|
||||||
config.performance = {
|
config.performance = {
|
||||||
@@ -91,8 +90,8 @@ module.exports = {
|
|||||||
maxEntrypointSize: 7168000, // 7MB
|
maxEntrypointSize: 7168000, // 7MB
|
||||||
maxAssetSize: 7168000, // 7MB
|
maxAssetSize: 7168000, // 7MB
|
||||||
assetFilter: function (assetFilename) {
|
assetFilter: function (assetFilename) {
|
||||||
return assetFilename.endsWith('.js');
|
return assetFilename.endsWith('.js')
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user