Refactor(云灵AI):列表数据对接

This commit is contained in:
lifizer
2026-04-22 17:42:58 +08:00
parent b4b6fed1ab
commit 9c90600c82
7 changed files with 306 additions and 139 deletions
+2 -1
View File
@@ -128,8 +128,9 @@ export default {
* 怎么申请地理标志保护产品? * 怎么申请地理标志保护产品?
* 想找个像《去有风的地方》那样的治愈县城 * 想找个像《去有风的地方》那样的治愈县城
* 云南有哪些小众县城适合旅居? * 云南有哪些小众县城适合旅居?
* 查询最近一个月的订单
*/ */
prompt: '查询最近一个月的订单', prompt: '',
recordManager: null, recordManager: null,
// 录音权限是否开通 // 录音权限是否开通
recordPermission: false, recordPermission: false,
+2
View File
@@ -9,6 +9,7 @@
:src="webUrl + '/aiChat/icon-57.png'" :src="webUrl + '/aiChat/icon-57.png'"
class="icon" class="icon"
mode="widthFix" mode="widthFix"
lazy-load
/> />
{{ item.merName }} {{ item.merName }}
</view> </view>
@@ -31,6 +32,7 @@
:src="cartItem.productInfo.image" :src="cartItem.productInfo.image"
class="img" class="img"
mode="widthFix" mode="widthFix"
lazy-load
/> />
</view> </view>
<view class="name-wrap"> <view class="name-wrap">
+17 -2
View File
@@ -251,10 +251,11 @@ export const chatMixinsV2 = {
orderList: [], orderList: [],
type: -2, type: -2,
showMoreInfo: {}, showMoreInfo: {},
currentSwiperIndex: 0 currentSwiperIndex: 0,
showChangeBtn: true
}) })
_this.loading = true _this.loading = true
_this.showCursor = false _this.showCursor = false
getSseConfigV1({ getSseConfigV1({
'chatNumber': _this.chatNumber, 'chatNumber': _this.chatNumber,
prompt prompt
@@ -453,12 +454,26 @@ export const chatMixinsV2 = {
} }
}) })
}, },
productItemSupplierClickHandle(item) {
if (!item.storeId) {
return
}
this.audioStopHandle()
this.$yrouter.push({
path: '/pagesInn/inn/innHome',
query: {
id: item.storeId
}
})
},
productItemBuyNowClickHandle(item) { productItemBuyNowClickHandle(item) {
this.audioStopHandle()
this.showGiftBtn = false this.showGiftBtn = false
this.cart_num = 1 this.cart_num = 1
this.addToCart(item) this.addToCart(item)
}, },
productItemGiftBuyClickHandle(item) { productItemGiftBuyClickHandle(item) {
this.audioStopHandle()
this.showGiftBtn = true this.showGiftBtn = true
this.cart_num = 1 this.cart_num = 1
this.addToCart(item) this.addToCart(item)
+161 -22
View File
@@ -45,14 +45,14 @@
</view> </view>
</view> </view>
<view v-if="item.type === -2" class="chat-message-body"> <view v-if="item.type === -2" class="chat-message-body">
<view class="avatar"> <view v-if="item.nodes && item.nodes.length" class="avatar">
<image <image
:src="webUrl + '/aiChat/avatar.png'" :src="webUrl + '/aiChat/avatar.png'"
class="chat-avatar" class="chat-avatar"
mode="widthFix" mode="widthFix"
/> />
</view> </view>
<view class="item-left"> <view v-if="item.nodes && item.nodes.length" class="item-left">
<view class="txt"> <view class="txt">
<view <view
ref="rich-text-box" ref="rich-text-box"
@@ -126,12 +126,71 @@
</view> </view>
<!-- 换一换 --> <!-- 换一换 -->
<view <view
v-if="!item.showCursor && item.nodes && item.nodes.length && !item.isError && item.showMoreInfo.show" v-if="!item.showCursor &&
!item.isError &&
(item.orderList.length > 0 ||
item.giftGoodsList.length > 0 ||
item.goodsList.length > 0 ||
item.shopList.length > 0 ||
item.prizeList.length > 0 ||
item.ichList.length > 0 ||
item.qianxianList.length > 0
)
"
class="change-list-wrap" class="change-list-wrap"
> >
<view v-if="item.showMoreInfo.showType === '特产商品'" class="list-wrap"> <!-- 礼品 -->
<view
v-if="item.giftGoodsList.length > 0"
class="recommend-goods-wrap"
>
<swiper
class="goods-swiper"
:indicator-dots="false"
:autoplay="false"
:current="item.currentSwiperIndex"
previous-margin="120rpx"
next-margin="120rpx"
circular
@change="e => onSwiperChange(index, e)"
>
<swiper-item
v-for="(goodItem, goodIndex) in item.giftGoodsList"
:key="goodIndex"
>
<view
:class="{ 'active': item.currentSwiperIndex === goodIndex }"
class="goods-item"
@click="productItemClickHandle(goodItem)"
>
<view class="supplier" @click.stop="productItemSupplierClickHandle(goodItem)">
<image
:src="webUrl + '/aiChat/icon-57.png'"
class="icon"
mode="widthFix"
lazy-load
/>
<text class="s-name">{{ goodItem.storeName }}</text>
<u-icon name="arrow-right" color="#999" size="12"></u-icon>
</view>
<view class="title one-t">{{ goodItem.name }}</view>
<image :src="goodItem.img" class="p-img" mode="aspectFill" lazy-load />
<view class="price">
<text class="unit">¥</text>
<text class="num">{{ goodItem.price }}</text>
</view>
<view class="actions">
<view class="btn-buy" @click.stop="productItemBuyNowClickHandle(goodItem)">立即下单</view>
<view class="btn-gift" @click.stop="productItemGiftBuyClickHandle(goodItem)">送给朋友</view>
</view>
</view>
</swiper-item>
</swiper>
</view>
<!-- 特产商品 -->
<view v-if="item.goodsList.length > 0" class="list-wrap">
<view <view
v-for="(productItem, productIndex) in item.showMoreInfo.resultItems" v-for="(productItem, productIndex) in item.goodsList"
:key="productIndex" :key="productIndex"
class="product-item" class="product-item"
@click="productItemClickHandle(productItem)" @click="productItemClickHandle(productItem)"
@@ -139,6 +198,7 @@
<image <image
:src="productItem.img" :src="productItem.img"
class="img" class="img"
lazy-load
/> />
<view class="product-info"> <view class="product-info">
<view class="name one-t"> <view class="name one-t">
@@ -153,9 +213,10 @@
</view> </view>
</view> </view>
</view> </view>
<view v-if="item.showMoreInfo.showType === '店铺'" class="list-wrap"> <!-- 店铺 -->
<view v-if="item.shopList.length > 0" class="list-wrap">
<view <view
v-for="(hotel, hotelIndex) in item.showMoreInfo.resultItems" v-for="(hotel, hotelIndex) in item.shopList"
:key="hotelIndex" :key="hotelIndex"
class="product-item" class="product-item"
@click="hotelItemClickHandle(hotel)" @click="hotelItemClickHandle(hotel)"
@@ -166,6 +227,7 @@
<image <image
:src="hotel.img" :src="hotel.img"
class="img" class="img"
lazy-load
/> />
<view class="product-info"> <view class="product-info">
<view class="name one-t"> <view class="name one-t">
@@ -177,9 +239,10 @@
</view> </view>
</view> </view>
</view> </view>
<view v-if="item.showMoreInfo.showType === '非遗文化'" class="list-wrap"> <!-- 非遗 -->
<view v-if="item.ichList.length > 0" class="list-wrap">
<view <view
v-for="(ich, ichIndex) in item.showMoreInfo.resultItems" v-for="(ich, ichIndex) in item.ichList"
:key="ichIndex" :key="ichIndex"
class="product-item" class="product-item"
@click="ichItemClickHandle(ich)" @click="ichItemClickHandle(ich)"
@@ -198,9 +261,10 @@
</view> </view>
</view> </view>
</view> </view>
<view v-if="item.showMoreInfo.showType === '千县名品'" class="list-wrap"> <!-- 千县 -->
<view v-if="item.qianxianList.length > 0" class="list-wrap">
<view <view
v-for="(qianxian, qianxianIndex) in item.showMoreInfo.resultItems" v-for="(qianxian, qianxianIndex) in item.qianxianList"
:key="qianxianIndex" :key="qianxianIndex"
class="product-item" class="product-item"
@click="qianxianItemClickHandle(qianxian)" @click="qianxianItemClickHandle(qianxian)"
@@ -208,6 +272,7 @@
<image <image
:src="qianxian.img" :src="qianxian.img"
class="img" class="img"
lazy-load
/> />
<view class="product-info"> <view class="product-info">
<view class="name name2 one-t"> <view class="name name2 one-t">
@@ -216,9 +281,10 @@
</view> </view>
</view> </view>
</view> </view>
<view v-if="item.showMoreInfo.showType === '优惠活动'" class="list-wrap prize-list-wrap"> <!-- 抽奖 -->
<view v-if="item.prizeList.length > 0" class="list-wrap prize-list-wrap">
<view <view
v-for="(prize, prizeIndex) in item.showMoreInfo.resultItems" v-for="(prize, prizeIndex) in item.prizeList"
:key="prizeIndex" :key="prizeIndex"
class="prize-item" class="prize-item"
@click="prizeItemClickHandle(prize)" @click="prizeItemClickHandle(prize)"
@@ -227,18 +293,42 @@
:src="prize.img" :src="prize.img"
class="img" class="img"
mode="widthFix" mode="widthFix"
lazy-load
/> />
</view> </view>
</view> </view>
<view v-if="item.showMoreInfo.showType !== '优惠活动'" class="change-btn"> <!-- 订单 -->
<view v-if="item.orderList.length > 0" class="order-list-wrap">
<view
v-for="(order, orderIndex) in item.orderList"
:key="orderIndex"
class="order-item-wrap"
>
<order-item
:item="order"
@viewOrderTrack="viewOrderTrackHandle"
/>
</view>
</view>
<view
v-if="(item.shopList.length > 0 ||
item.goodsList.length > 0 ||
item.giftGoodsList.length > 0 ||
item.ichList.length > 0 ||
item.qianxianList.length > 0 ||
item.shopList.length > 0) && item.showChangeBtn
"
class="change-btn"
>
<image <image
:src="webUrl + '/aiChat/icon-change.png'" :src="webUrl + '/aiChat/icon-change.png'"
class="icon" class="icon"
mode="widthFix" mode="widthFix"
@click="analyzeKeywordsChangeHandle(item.conversationId, item.showMoreInfo)" @click="analyzeKeywordsChangeHandle(item.conversationId, item)"
/> />
</view> </view>
</view> </view>
<!-- 建议提问 -->
<view <view
v-if="index === (chatMessageListLength - 1) && item.listQuestion.length > 0" v-if="index === (chatMessageListLength - 1) && item.listQuestion.length > 0"
class="suggestion-wrap" class="suggestion-wrap"
@@ -328,9 +418,10 @@
</template> </template>
<script> <script>
import { chatMixins } from '../mixins/chatMixins.js' import { chatMixinsV2 } from '../mixins/chatMixinsV2.js'
import AiHistoryList from '../components/historyList' import AiHistoryList from '../components/historyList'
import BottomSend from '../components/bottomSend.vue' import BottomSend from '../components/bottomSend.vue'
import OrderItem from '../components/orderItem.vue'
import { import {
historyChatMessage historyChatMessage
} from '@/api/chat/index' } from '@/api/chat/index'
@@ -339,9 +430,10 @@ export default {
name: 'AiChatHistoryPage', name: 'AiChatHistoryPage',
components: { components: {
AiHistoryList, AiHistoryList,
BottomSend BottomSend,
OrderItem
}, },
mixins: [chatMixins], mixins: [chatMixinsV2],
data() { data() {
return { return {
pageTitle: '' pageTitle: ''
@@ -370,20 +462,67 @@ export default {
prompt: item.content prompt: item.content
}) })
} else { } else {
if (item.contentType === 'text') { if (['text', 'skill_list'].includes(item.contentType)) {
this.chatMessageList.push({ const tempItemData = {
...item, ...item,
nodes: formatAiMsgContent(item.content), nodes: formatAiMsgContent(item.content),
prompt: this.getParentPrompt(data, item.parentMessageId), prompt: this.getParentPrompt(data, item.parentMessageId),
conversationId: item.messageId, conversationId: item.messageId,
showCursor: false, showCursor: false,
listQuestion: item.listQuestion || [], listQuestion: item.listQuestion || [],
// 特产商品
goodsList: [],
// 礼品商品
giftGoodsList: [],
// 非遗文化
ichList: [],
// 千县名品
qianxianList: [],
// 抽奖
prizeList: [],
// 店铺
shopList: [],
// 地标好物--屏蔽原因:实现此功能时,后端通过AI动态返回的图片不满足需求,后面就不要了
landmarksList: [],
// 订单
orderList: [],
type: -2, type: -2,
showMoreInfo: {} showMoreInfo: {},
}) currentSwiperIndex: 0,
showChangeBtn: false
}
if (item.cardData) {
const { contentType, finish, items = [] } = item.cardData
if (contentType === 'order') {
tempItemData.orderList = items || []
}
if (contentType === 'shop') {
tempItemData.shopList = items || []
}
if (contentType === 'gift') {
tempItemData.giftGoodsList = items || []
}
if (contentType === 'discount') {
tempItemData.prizeList = items || []
}
if (contentType === 'goods') {
tempItemData.goodsList = items || []
}
if (contentType === 'famous') {
tempItemData.qianxianList = items || []
}
if (contentType === 'ich') {
tempItemData.ichList = items || []
}
if (contentType === 'landmarks') {
tempItemData.landmarksList = items || []
}
}
this.chatMessageList.push(tempItemData)
} }
} }
}) })
console.log(this.chatMessageList)
} catch (err) { } catch (err) {
uni.showModal({ uni.showModal({
title: '错误提示', title: '错误提示',
+6 -2
View File
@@ -26,10 +26,11 @@
<view class="sheet"> <view class="sheet">
<view class="sheet-thumb-row"> <view class="sheet-thumb-row">
<image <image
v-if="imageUrl && confidence > 0" v-if="imageUrl && confidenceFlag"
:src="imageUrl" :src="imageUrl"
class="sheet-thumb" class="sheet-thumb"
mode="aspectFill" mode="aspectFill"
lazy-load
/> />
<view v-else class="empty-state"> <view v-else class="empty-state">
<view class="empty-text">未识别到物品,请重新尝试</view> <view class="empty-text">未识别到物品,请重新尝试</view>
@@ -48,6 +49,7 @@
:src="item.img" :src="item.img"
class="goods-img" class="goods-img"
mode="aspectFill" mode="aspectFill"
lazy-load
/> />
<view class="goods-title"> <view class="goods-title">
{{ item.name }} {{ item.name }}
@@ -61,6 +63,7 @@
:src="webUrl + '/home/icon-cart.png'" :src="webUrl + '/home/icon-cart.png'"
class="cart-btn" class="cart-btn"
mode="aspectFit" mode="aspectFit"
lazy-load
/> />
</view> </view>
</view> </view>
@@ -85,7 +88,7 @@ export default {
statusBarHeight: 20, statusBarHeight: 20,
imageUrl: '', imageUrl: '',
goodsList: [], goodsList: [],
confidence: 0 confidenceFlag: true
} }
}, },
onLoad(options) { onLoad(options) {
@@ -106,6 +109,7 @@ export default {
const { data, success } = res const { data, success } = res
if (success) { if (success) {
this.goodsList = data.cards || [] this.goodsList = data.cards || []
this.confidenceFlag = data.success
} }
}).finally(() => { }).finally(() => {
this.loaded = true this.loaded = true
+9 -112
View File
@@ -275,17 +275,18 @@
class="goods-item" class="goods-item"
@click="productItemClickHandle(goodItem)" @click="productItemClickHandle(goodItem)"
> >
<view class="supplier"> <view class="supplier" @click.stop="productItemSupplierClickHandle(goodItem)">
<image <image
:src="webUrl + '/aiChat/icon-57.png'" :src="webUrl + '/aiChat/icon-57.png'"
class="icon" class="icon"
mode="widthFix" mode="widthFix"
lazy-load
/> />
<text class="s-name">{{ goodItem.supplierName }}</text> <text class="s-name">{{ goodItem.storeName }}</text>
<u-icon name="arrow-right" color="#999" size="12"></u-icon> <u-icon name="arrow-right" color="#999" size="12"></u-icon>
</view> </view>
<view class="title one-t">{{ goodItem.name }}</view> <view class="title one-t">{{ goodItem.name }}</view>
<image :src="goodItem.img" class="p-img" mode="aspectFill" /> <image :src="goodItem.img" class="p-img" mode="aspectFill" lazy-load />
<view class="price"> <view class="price">
<text class="unit">¥</text> <text class="unit">¥</text>
<text class="num">{{ goodItem.price }}</text> <text class="num">{{ goodItem.price }}</text>
@@ -309,6 +310,7 @@
<image <image
:src="productItem.img" :src="productItem.img"
class="img" class="img"
lazy-load
/> />
<view class="product-info"> <view class="product-info">
<view class="name one-t"> <view class="name one-t">
@@ -337,6 +339,7 @@
<image <image
:src="hotel.img" :src="hotel.img"
class="img" class="img"
lazy-load
/> />
<view class="product-info"> <view class="product-info">
<view class="name one-t"> <view class="name one-t">
@@ -359,6 +362,7 @@
<image <image
:src="ich.img" :src="ich.img"
class="img" class="img"
lazy-load
/> />
<view class="product-info"> <view class="product-info">
<view class="name one-t"> <view class="name one-t">
@@ -381,6 +385,7 @@
<image <image
:src="qianxian.img" :src="qianxian.img"
class="img" class="img"
lazy-load
/> />
<view class="product-info"> <view class="product-info">
<view class="name name2 one-t"> <view class="name name2 one-t">
@@ -401,6 +406,7 @@
:src="prize.img" :src="prize.img"
class="img" class="img"
mode="widthFix" mode="widthFix"
lazy-load
/> />
</view> </view>
</view> </view>
@@ -719,107 +725,6 @@ export default {
} }
} }
} }
.recommend-goods-wrap {
width: calc(100% + 48rpx);
padding: 24rpx 0;
margin: 0 -24rpx;
.goods-swiper {
width: 100%;
height: 750rpx;
}
.goods-item {
display: block;
width: 460rpx;
margin: 24rpx auto 0 auto;
padding: 24rpx;
box-sizing: border-box;
background: #fff;
border-radius: 20rpx;
box-shadow: 0rpx 0 24rpx rgba(0,0,0,0.16);
white-space: normal;
transition: all 0.3s ease;
transform: scale(0.9);
opacity: 0.8;
&.active {
transform: scale(1);
opacity: 1;
}
.supplier {
display: flex;
align-items: center;
font-size: 24rpx;
color: #666;
margin-bottom: 16rpx;
.icon {
display: block;
width: 28rpx;
height: 28rpx;
}
.s-name {
flex: 1;
margin: 0 8rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.title {
font-size: 28rpx;
font-weight: bold;
color: #333;
margin-bottom: 16rpx;
line-height: 40rpx;
}
.p-img {
width: 412rpx;
height: 412rpx;
margin-bottom: 16rpx;
border-radius: 12rpx;
background-color: #f5f5f5;
}
.price {
color: #C52733;
text-align: center;
margin-bottom: 16rpx;
.unit {
font-size: 28rpx;
font-weight: bold;
}
.num {
font-size: 36rpx;
font-weight: bold;
}
}
.actions {
display: flex;
justify-content: center;
.btn-buy {
width: 160rpx;
height: 56rpx;
margin: 0 12rpx;
line-height: 56rpx;
text-align: center;
background: #C52733;
color: #fff;
font-size: 24rpx;
border-radius: 28rpx;
}
.btn-gift {
width: 160rpx;
height: 56rpx;
margin: 0 12rpx;
line-height: 52rpx;
text-align: center;
background: #fff;
color: #C52733;
font-size: 24rpx;
border-radius: 28rpx;
border: 2rpx solid #C52733;
box-sizing: border-box;
}
}
}
}
.topic-wrap { .topic-wrap {
width: 100%; width: 100%;
.title { .title {
@@ -869,14 +774,6 @@ export default {
} }
} }
} }
.order-list-wrap {
width: 100%;
padding: 24rpx 0;
box-sizing: border-box;
.order-item-wrap + .order-item-wrap {
margin: 24rpx 0 0 0;
}
}
@keyframes translateXSwiper { @keyframes translateXSwiper {
0% { 0% {
transform: translateX(0); transform: translateX(0);
+109
View File
@@ -1226,3 +1226,112 @@ page {
} }
.page-to-bottom { .page-to-bottom {
} }
.recommend-goods-wrap {
width: calc(100% + 48rpx);
padding: 24rpx 0;
margin: 0 -24rpx;
.goods-swiper {
width: 100%;
height: 750rpx;
}
.goods-item {
display: block;
width: 460rpx;
margin: 24rpx auto 0 auto;
padding: 24rpx;
box-sizing: border-box;
background: #fff;
border-radius: 20rpx;
box-shadow: 0rpx 0 24rpx rgba(0,0,0,0.16);
white-space: normal;
transition: all 0.3s ease;
transform: scale(0.9);
opacity: 0.8;
&.active {
transform: scale(1);
opacity: 1;
}
.supplier {
display: flex;
align-items: center;
font-size: 24rpx;
color: #666;
margin-bottom: 16rpx;
.icon {
display: block;
width: 28rpx;
height: 28rpx;
}
.s-name {
flex: 1;
margin: 0 8rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.title {
font-size: 28rpx;
font-weight: bold;
color: #333;
margin-bottom: 16rpx;
line-height: 40rpx;
}
.p-img {
width: 412rpx;
height: 412rpx;
margin-bottom: 16rpx;
border-radius: 12rpx;
background-color: #f5f5f5;
}
.price {
color: #C52733;
text-align: center;
margin-bottom: 16rpx;
.unit {
font-size: 28rpx;
font-weight: bold;
}
.num {
font-size: 36rpx;
font-weight: bold;
}
}
.actions {
display: flex;
justify-content: center;
.btn-buy {
width: 160rpx;
height: 56rpx;
margin: 0 12rpx;
line-height: 56rpx;
text-align: center;
background: #C52733;
color: #fff;
font-size: 24rpx;
border-radius: 28rpx;
}
.btn-gift {
width: 160rpx;
height: 56rpx;
margin: 0 12rpx;
line-height: 52rpx;
text-align: center;
background: #fff;
color: #C52733;
font-size: 24rpx;
border-radius: 28rpx;
border: 2rpx solid #C52733;
box-sizing: border-box;
}
}
}
}
.order-list-wrap {
width: 100%;
padding: 24rpx 0;
box-sizing: border-box;
.order-item-wrap + .order-item-wrap {
margin: 24rpx 0 0 0;
}
}