Files
xdd-uniapp-new/aiChat/mixins/chatMixinsV2.js
T

913 lines
28 KiB
JavaScript
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.
import {
getSseConfigV1,
getCompletionsV3,
deleteChatItemByConversationId,
// getAnalyzeKeywordsV1,
getAnalyzeKeywordsChangeV3,
getSseCountyAiConfigV1,
getCountyAiCompletionsV1,
getGiftRecommendOptions,
getGiftRecommendPrompt
} from '@/api/chat/index'
import {
getExceptionConfig,
getBottomNavigationConfigList
} from '@/api/public'
import {
getProductDetail,
postCartAdd
} from '@/api/store'
import { VUE_APP_API_URL } from '@/config'
import settings from '@/config/baseSetting.js'
import { formatAiMsgContent } from '../utils/aiChat'
import cookie from '@/utils/store/cookie'
import { handleLoginFailure } from '@/utils'
const plugin = requirePlugin('WechatSI')
const removeMarkdown = require('remove-markdown')
export const chatMixinsV2 = {
data() {
return {
token: cookie.get('login_status'),
userInfo: cookie.get('userInfo'),
webUrl: this.$VUE_APP_RESOURCES_URL,
bottomTools: [],
scrollData: {},
scrollTop: 0,
isScrollToBottom: false,
// 状态栏高度
statusBarHeight: 20,
bottomSafeDistance: 0,
fileHttpStr: Object.freeze(VUE_APP_API_URL + settings.sysPrefix),
keyboardHeight: 0,
optionsFrom: '',
onFocus: false,
loading: false,
chatNumber: '',
infoResData: {},
chatMessageList: [],
audioAyy: [],
isPlayAudio: false,
audioContext: null,
audioPlayId: '',
showGiftBtn: false,
buyLoading: false,
exceptionConfig: {
model_exception: '您提的问题未能理解,云灵思想跑偏啦,重新整理一下问题或者重新提问,让云灵再试试!',
voice_exception: ''
},
giftStepLoading: false,
onlyOneToAddCart: false
}
},
computed: {
chatMessageListLength() {
return this.chatMessageList.length
}
},
onPageScroll(e) {
this.scrollData = e
const scrollTop = e.scrollTop
this.scrollTop = scrollTop
const query = wx.createSelectorQuery().in(this)
query.select('#fixTbabarBody').boundingClientRect(function(rect) {
const contentHeight = rect.height
const windowInfo = wx.getWindowInfo()
const windowHeight = windowInfo.windowHeight
if (scrollTop + windowHeight + 30 >= contentHeight) {
this.isScrollToBottom = true
} else {
this.isScrollToBottom = false
}
}.bind(this)).exec()
},
onUnload() {
if (this.audioContext) {
this.audioContext.stop()
}
wx.getBackgroundAudioManager().stop()
this.closeWsFn()
},
created() {
const _this = this
uni.getSystemInfo({
success: (e) => {
let statusBar = 0
// #ifdef MP-WEIXIN
statusBar = e.statusBarHeight
const custom = uni.getMenuButtonBoundingClientRect()
_this.rightDistance = e.windowWidth - custom.left + 10
// #endif
// 状态栏高度
_this.statusBarHeight = statusBar
_this.bottomSafeDistance = e.safeAreaInsets.bottom
}
})
_this.createdCallbak()
},
methods: {
createdCallbak() {
this.$nextTick(() => {
this.scrollToBottomHandle()
getExceptionConfig().then(res => {
const { success, data } = res
if (success) {
this.exceptionConfig = data || {}
}
})
// 县域AI没有底部快捷操作和送礼配置
if (!this.countyId) {
getBottomNavigationConfigList().then(res => {
const { success, data } = res
if (success) {
this.bottomTools = data || []
this.bottomTools.forEach(item => {
item.display = true
})
}
})
getGiftRecommendOptions().then(res => {
const { success, data } = res
if (success) {
this.giftBuyOptions = data || {}
}
})
}
this.extraReqFn()
})
},
extraReqFn() {
console.log('首页/历史详情页面单独请求接口逻辑')
},
inputFocusHandle(value) {
this.onFocus = value === 1
this.scrollToBottomHandle()
},
historyViewHandle() {
console.log(this.$refs.historyView)
this.$refs.historyView.showView()
},
copyContentHandle(data) {
uni.setClipboardData({
data,
success: () => {
uni.showToast({
title: '已复制'
})
}
})
},
copyMoreInfoHandle(item) {
uni.setClipboardData({
data: JSON.stringify(item.showMoreInfo),
success: () => {
uni.showToast({
title: '已复制'
})
}
})
},
shareHandle() {
wx.showShareMenu({
withShareTicket: true,
menus: ['shareAppMessage', 'shareTimeline'],
success: (res) => {
console.log(res)
},
fail: err => {
console.log(err)
}
})
},
scrollToBottomHandle() {
uni.pageScrollTo({
duration: 100,
selector: '#bottom_postion'
})
this.isScrollToBottom = true
},
// 重新生成
regenerateHandle(item) {
this.audioStopHandle()
deleteChatItemByConversationId(item.conversationId).then(res => {
const { success } = res
if (success) {
this.chatMessageList.splice(this.chatMessageListLength - 1, 1)
this.streamReq({
prompt: item.prompt,
init: 1
})
}
})
},
// 建议提问
suggestionItemClickHandle(question) {
this.streamReq({
prompt: question,
init: 0
})
},
// 大家都在问
topicItemClickHandle(question) {
this.streamReq({
prompt: question,
init: 0
})
},
streamReq(params) {
const _this = this
// 需要校验登录状态
if (!_this.token) {
handleLoginFailure()
return
}
if (this.loading) return
const prompt = params.prompt.substr(0, 500)
if (params.init !== 1) {
_this.chatMessageList.push({
prompt,
type: -1
})
}
_this.$nextTick(() => {
_this.scrollToBottomHandle()
})
_this.chatMessageList.push({
content: '',
nodes: '',
isError: false,
showCursor: false,
conversationId: '',
prompt,
listQuestion: [],
// 特产商品
goodsList: [],
// 礼品商品
giftGoodsList: [],
// 非遗文化
ichList: [],
// 千县名品
qianxianList: [],
// 抽奖
prizeList: [],
// 店铺
shopList: [],
// 地标好物--屏蔽原因:实现此功能时,后端通过AI动态返回的图片不满足需求,后面就不要了
landmarksList: [],
// 订单
orderList: [],
type: -2,
showMoreInfo: {},
currentSwiperIndex: 0,
showChangeBtn: true,
changeIsRotate: false
})
_this.loading = true
_this.showCursor = false
let reqFn = null
let postData = {}
if (_this.countyId) {
reqFn = getSseCountyAiConfigV1
postData = {
'countyId': _this.countyId,
'chatNumber': _this.chatNumber,
prompt
}
} else {
reqFn = getSseConfigV1
postData = {
'chatNumber': _this.chatNumber,
prompt
}
}
reqFn(postData).then(resConfig => {
const conversationId = resConfig.data.conversationId
_this.chatNumber = resConfig.data.chatNumber || ''
_this.chatMessageList[_this.chatMessageListLength - 1].showCursor = true
_this.chatMessageList[_this.chatMessageListLength - 1].conversationId = conversationId
if (conversationId) {
_this.initWebSocket(conversationId, () => {
let completionsReqFn = null
let completionsPostData = {}
if (_this.countyId) {
completionsReqFn = getCountyAiCompletionsV1
completionsPostData = {
conversationId,
countyId: _this.countyId,
ws: true,
isNoNeedPublicErrorNotification: 1
}
} else {
completionsReqFn = getCompletionsV3
completionsPostData = {
conversationId,
ws: true,
isNoNeedPublicErrorNotification: 1
}
}
completionsReqFn(completionsPostData).then(sseRes => {
console.log(sseRes)
// _this.closeWsFn()
}).catch(err => {
console.log(err)
_this.chatErrorSetContent()
})
})
} else {
_this.chatErrorSetContent()
}
}).catch(err => {
console.log(err)
_this.chatErrorSetContent()
})
},
initWebSocket(conversationId, cb) {
const _this = this
console.log('------------------创建连接')
uni.connectSocket({
url: VUE_APP_API_URL.replace('https', 'wss') + settings.sysPrefix + `v1/chat/websocket/${_this.userInfo.unionId}-${conversationId}`,
header: {
'content-type': 'application/json'
},
timeout: 10 * 1000,
success: () => {
console.log('------------WebSocket初始化成功')
},
fail: (err) => {
uni.showModal({
showCancel: false,
content: err
})
}
})
uni.onSocketMessage(res => {
_this.websocketOnmessage(res)
})
uni.onSocketOpen(() => {
cb && cb()
_this.websocketOnopen()
})
uni.onSocketError(err => {
_this.websocketOnerror(err)
})
},
websocketOnmessage(res) {
console.log('-------------监听 WebSocket 接受到服务器的消息事件')
const resData = JSON.parse(res.data)
if (resData) {
const message = resData.message
console.log(message)
if (message && this.chatMessageListLength > 1) {
const mesContent = message.content
const contentType = message.contentType
const finish = message.finish
if (contentType === 'text') {
if (!finish && mesContent) {
this.chatMessageList[this.chatMessageListLength - 1].nodes = formatAiMsgContent(mesContent)
this.chatMessageList[this.chatMessageListLength - 1].content = mesContent
this.chatMessageList[this.chatMessageListLength - 1].listQuestion = message.listQuestion || []
}
}
if (contentType === 'order') {
if (!finish && message.listCards) {
this.chatMessageList[this.chatMessageListLength - 1].orderList = message.listCards || []
}
}
if (contentType === 'shop') {
if (!finish && message.listCards) {
this.chatMessageList[this.chatMessageListLength - 1].shopList = message.listCards || []
}
}
if (contentType === 'gift') {
if (!finish && message.listCards) {
this.chatMessageList[this.chatMessageListLength - 1].giftGoodsList = message.listCards || []
}
}
if (contentType === 'discount') {
if (!finish && message.listCards) {
this.chatMessageList[this.chatMessageListLength - 1].prizeList = message.listCards || []
}
}
if (contentType === 'goods') {
if (!finish && message.listCards) {
this.chatMessageList[this.chatMessageListLength - 1].goodsList = message.listCards || []
}
}
if (contentType === 'famous') {
if (!finish && message.listCards) {
this.chatMessageList[this.chatMessageListLength - 1].qianxianList = message.listCards || []
}
}
if (contentType === 'ich') {
if (!finish && message.listCards) {
this.chatMessageList[this.chatMessageListLength - 1].ichList = message.listCards || []
}
}
if (contentType === 'landmarks') {
if (!finish && message.listCards) {
this.chatMessageList[this.chatMessageListLength - 1].landmarksList = message.listCards || []
}
}
this.$forceUpdate()
this.$nextTick(() => {
this.scrollToBottomHandle()
if (finish) {
this.closeWsFn()
}
})
}
}
},
websocketOnopen() {
console.log('------------------监听 WebSocket 连接打开事件')
// this.websocketSend(JSON.stringify({ msg: '1' }))
},
websocketOnerror(e) {
console.log('-----------------监听 WebSocket 错误事件')
console.log(e)
},
websocketSend(msg) {
// 数据发送
try {
uni.sendSocketMessage({
data: msg,
success: () => {
console.log('-----------发送消息成功了')
}
})
} catch (err) {
console.log('send failed (' + err.code + ')')
}
},
onSwiperChange(index, e) {
this.chatMessageList[index].currentSwiperIndex = e.detail.current
},
analyzeKeywordsChangeHandle(conversationId, item) {
let idx = -1
this.chatMessageList.map((item, index) => {
if (item.conversationId === conversationId) {
idx = index
}
})
if (idx > -1) {
if (this.chatMessageList[idx].changeIsRotate) {
return
}
this.$set(this.chatMessageList[idx], 'changeIsRotate', true)
}
let listKey = ''
// 抽奖和订单查询没有换一换
if (item.goodsList.length > 0) {
listKey = 'goodsList'
}
if (item.giftGoodsList.length > 0) {
listKey = 'giftGoodsList'
}
if (item.ichList.length > 0) {
listKey = 'ichList'
}
if (item.qianxianList.length > 0) {
listKey = 'qianxianList'
}
if (item.shopList.length > 0) {
listKey = 'shopList'
}
getAnalyzeKeywordsChangeV3({
conversationId
}).then(res => {
const { success, data } = res
if (success) {
let changeList = []
this.chatMessageList.map((item) => {
if (item.conversationId === conversationId) {
changeList = data.list || []
}
})
if (idx > -1 && listKey) {
this.$set(this.chatMessageList[idx], listKey, changeList)
this.$forceUpdate()
}
setTimeout(() => {
this.$set(this.chatMessageList[idx], 'changeIsRotate', false)
}, 1000)
}
}).catch(error => {
this.$set(this.chatMessageList[idx], 'changeIsRotate', false)
})
},
productItemClickHandle(item) {
this.audioStopHandle()
this.$yrouter.push({
path: '/pages/shop/GoodsCon/index',
query: {
id: item.id
}
})
},
productItemSupplierClickHandle(item) {
if (!item.storeId) {
return
}
this.audioStopHandle()
this.$yrouter.push({
path: '/pagesInn/inn/innHome',
query: {
id: item.storeId
}
})
},
productItemBuyNowClickHandle(item) {
this.showGiftBtn = false
this.productItemBuyNowOrGiftBuy(item, 1)
},
productItemGiftBuyClickHandle(item) {
this.showGiftBtn = true
this.productItemBuyNowOrGiftBuy(item, 2)
},
productItemBuyNowOrGiftBuy(item, BuyNowOrGiftBuyType) {
this.audioStopHandle()
this.cart_num = 1
this.m_id = item.id
getProductDetail(item.id).then(res => {
const { data } = res
this.storeInfo = {...data.storeInfo}
if(data.storeInfo.stock === 0) {
uni.showToast({
title: "产品库存不足,请选择其他商品",
icon: "none",
duration: 5000
})
return
}
this.isWenwan = data.isWenwan
this.qualifications = data.qualifications || []
// 给 attr 赋值,将请求回来的规格赋值给 attr
if (this.source !== 'pre' && this.source !== 'day' && this.source !== 'kill' && this.source !== 'spe') {
this.attr.productAttr = data.productAttr || []
this.productValueArr = []
for (const key in data.productValue) {
this.productValueArr.push({
attrItemkey: key,
...data.productValue[key]
})
}
// 初始化认为所有的规格都是可以选的
this.attr.productAttr.map(item => {
item.attrValue.map(subItem => {
subItem.canUsed = true
})
})
}
this.attr.defaultSku = data.defaultSku
this.attr.defaultSkuIndex = data.defaultSkuIndex
this.DefaultSelect()
// 是否单一规格
const onlyOne = Object.keys(data.productValue).length === 1
if(onlyOne && this.onlyOneToAddCart) {
this.buyNowOrGiftBuyReq(BuyNowOrGiftBuyType)
return
}
this.attr.cartAttr = !this.isOpen ? true : false
})
},
buyNowOrGiftBuyReq(type) {
const _this = this
if (_this.buyLoading) {
return
}
_this.buyLoading = true
postCartAdd({
productId: _this.m_id,
cartNum: _this.attr.productSelect.cart_num,
new: 1,
uniqueId: _this.attr.productSelect !== undefined ? _this.attr.productSelect.unique : ''
}).then(function (res) {
try {
_this.closeAttrWindow()
const { cartId } = res.data
if (type === 1) {
uni.navigateTo({
url: '/pages/order/OrderSubmission/index?id=' + cartId
})
return
}
if(type === 2) {
uni.navigateTo({
url: '/pkg_user/views/gift/gift?cartId=' + cartId
})
return
}
} catch (error) {
console.log(error)
}
}).catch(error => {
uni.showToast({
title: error.msg,
icon: "none",
duration: 5000
})
}).finally(() => {
_this.buyLoading = false
})
},
closeAttrWindow() {
this.$set(this.attr, 'cartAttr', false)
},
hotelItemClickHandle(item) {
this.audioStopHandle()
this.$yrouter.push({
path: '/pagesInn/inn/innHome',
query: {
id: item.id
}
})
},
ichItemClickHandle(item) {
this.audioStopHandle()
this.$yrouter.push({
path: '/pkg_product/views/heritage/details',
query: {
id: item.id
}
})
},
qianxianItemClickHandle(item) {
this.audioStopHandle()
this.$yrouter.push({
path: '/pkg_product/views/famousQianxianShop',
query: {
id: item.id
}
})
},
prizeItemClickHandle() {
this.audioStopHandle()
this.$yrouter.push({ path: '/pkg-video/views/lottery' })
},
chatErrorSetContent() {
this.chatMessageList[this.chatMessageListLength - 1].isError = true
this.chatMessageList[this.chatMessageListLength - 1].content = this.exceptionConfig.model_exception
this.chatMessageList[this.chatMessageListLength - 1].nodes = this.exceptionConfig.model_exception
this.closeWsFn()
},
closeWsFn() {
this.loading = false
if (this.chatMessageListLength) {
this.chatMessageList[this.chatMessageListLength - 1].showCursor = false
}
uni.closeSocket({
success: () => {
console.log('-----关闭连接')
}
})
this.$nextTick(() => {
this.scrollToBottomHandle()
})
},
audioStopHandle() {
// Fix bug#4049
this.audioAyy = []
if (this.audioContext) {
this.audioContext.stop()
}
this.isPlayAudio = false
wx.getBackgroundAudioManager().stop()
},
ttsHandle(item) {
this.audioAyy = []
if (this.audioContext) {
this.audioContext.stop()
// this.audioContext.destroy()
}
// 如果正在播放,点击当前播放的则认为是暂停
if (this.isPlayAudio && [item.conversationId, item.messageId, item.parentMessageId].includes(this.audioPlayId)) {
this.isPlayAudio = false
this.audioPlayId = ''
return
}
this.isPlayAudio = false
this.audioPlayId = item.conversationId || item.messageId || item.parentMessageId
const flag = 1
if (flag === 1) {
console.log(removeMarkdown(item.content).replace(/[\n\t\s]/g, ''))
uni.showLoading({
title: '合成中...'
})
this.splitAndSynthesize(removeMarkdown(item.content).replace(/[\n\t\s]/g, ''))
} else {
// 调试数据
this.audioAyy = [
'https://ae.weixin.qq.com/cgi-bin/mmasrai-bin/getmedia?filename=1750749075_2f71887cc48d9b3753c39d119862d4bb&filekey=871027724&source=miniapp_plugin',
'https://ae.weixin.qq.com/cgi-bin/mmasrai-bin/getmedia?filename=1750749077_0970508786c5e3edc3a68a5eb9de048c&filekey=871027724&source=miniapp_plugin',
'https://ae.weixin.qq.com/cgi-bin/mmasrai-bin/getmedia?filename=1750749079_e77cec52cc077ba981892887c3df4cdf&filekey=871027724&source=miniapp_plugin',
'https://ae.weixin.qq.com/cgi-bin/mmasrai-bin/getmedia?filename=1750749080_02db4235771f5561cdb73c06d8c67e53&filekey=871027724&source=miniapp_plugin',
'https://ae.weixin.qq.com/cgi-bin/mmasrai-bin/getmedia?filename=1750749082_534e455c5d6ac5e9a4192ecf9952851a&filekey=871027724&source=miniapp_plugin',
'https://ae.weixin.qq.com/cgi-bin/mmasrai-bin/getmedia?filename=1750749083_78c4a2fecad538561f81c48dc4ecd645&filekey=871027724&source=miniapp_plugin'
]
this.playNextChunk()
}
},
splitAndSynthesize(text) {
const _this = this
if (text) {
const textLength = text.length
const chunkSize = 100
const chunk = text.slice(0, textLength > chunkSize ? chunkSize : textLength)
plugin.textToSpeech({
// 语言
lang: 'zh_CN',
tts: true,
// 要转换的文字
content: chunk,
success: function(res) {
console.log("语音文件路径:", res.filename)
_this.audioAyy.push(res.filename)
// 首次就立即播放,增强体验
if (!_this.isPlayAudio) {
_this.playNextChunk()
}
if (textLength > chunkSize) {
// 递归调用,播放一定要按照文本分割顺序
_this.splitAndSynthesize(text.substr(chunkSize, textLength))
} else {
// 最后一条合成,没有正在播放则立即播放
if (!_this.isPlayAudio) {
_this.playNextChunk()
}
}
uni.hideLoading()
},
fail: function(err) {
console.log("转换失败:", err)
uni.hideLoading()
}
})
} else {
if (!_this.isPlayAudio) {
_this.playNextChunk()
}
uni.hideLoading()
}
},
playNextChunk() {
if (this.audioAyy.length > 0 && this.audioPlayId) {
const src = this.audioAyy.shift()
this.isPlayAudio = true
const audioContext = uni.createInnerAudioContext({
// 是否使用 WebAudio 作为底层音频驱动,默认关闭。对于短音频、播放频繁的音频建议开启此选项,开启后将获得更优的性能表现。由于开启此选项后也会带来一定的内存增长,因此对于长音频建议关闭此选项
useWebAudioImplement: false
})
this.audioContext = audioContext
audioContext.src = src
audioContext.onEnded(() => {
audioContext.destroy()
this.playNextChunk()
})
audioContext.play()
console.log('------------src' + src)
} else {
console.log('------------end')
this.isPlayAudio = false
}
},
bottomToolClickHandle(item) {
// 非界面跳转的需要校验登录状态
if (!this.token && item.navType !== 'url') {
handleLoginFailure()
return
}
const idx = this.bottomTools.findIndex((i) => i.giftCardEnabled === 1)
if (item.giftCardEnabled === 1) {
// 新建会话
this.createNewHandle('giftChat')
this.$set(this, 'showGiftSteps', true)
if (idx > -1) {
this.$set(this.bottomTools[idx], 'display', false)
}
return
}
this.$set(this, 'showGiftSteps', false)
if (idx > -1) {
this.$set(this.bottomTools[idx], 'display', true)
}
if (item.navType === 'prompt') {
this.streamReq({
prompt: item.promptContent,
init: 0
})
}
if (item.navType === 'url') {
uni.navigateTo({
url: item.url
})
}
},
viewOrderTrackHandle(order) {
uni.navigateTo({
url: '/pagesOrder/order/OrderDetails/index?id=' + order.orderId
})
},
isGiftOptionSelected(configId, optionId) {
if (!this.giftSelections[configId]) return false
return this.giftSelections[configId].includes(optionId)
},
toggleGiftOption(configId, optionId) {
if (!this.giftSelections[configId]) {
this.$set(this.giftSelections, configId, [])
}
const index = this.giftSelections[configId].indexOf(optionId)
if (index > -1) {
this.giftSelections[configId].splice(index, 1)
} else {
this.giftSelections[configId].push(optionId)
}
},
prevGiftStep() {
if (this.currentGiftStepIndex > 0) {
this.currentGiftStepIndex--
}
},
nextGiftStep() {
const currentStep = this.giftBuyOptions[this.currentGiftStepIndex]
let hasSelection = false
// 校验是否已选择
if (currentStep.type === 'option_card') {
const selectedIds = this.giftSelections[currentStep.id] || []
hasSelection = selectedIds.length > 0
} else if (currentStep.type === 'price_input') {
hasSelection = !!this.giftBudget
}
if (!hasSelection) {
uni.showToast({
title: '请完善信息后再点击下一步',
icon: 'none'
})
return
}
if (this.currentGiftStepIndex < this.giftBuyOptions.length - 1) {
this.currentGiftStepIndex++
} else {
this.finishGiftSteps()
}
},
skipGiftStep() {
if (this.currentGiftStepIndex < this.giftBuyOptions.length - 1) {
this.currentGiftStepIndex++
} else {
this.finishGiftSteps()
}
},
finishGiftSteps() {
if (this.giftStepLoading || this.loading) return
const selectedSteps = []
this.giftBuyOptions.forEach(step => {
if (step.type === 'option_card') {
const selectedIds = this.giftSelections[step.id] || []
if (selectedIds.length > 0) {
const selectedNames = step.options.filter(opt => selectedIds.includes(opt.id)).map(opt => opt.optionName)
selectedSteps.push({
sort: step.sort,
title: step.title,
type: step.type,
options: selectedNames
})
}
} else if (step.type === 'price_input') {
if (this.giftBudget) {
selectedSteps.push({
sort: step.sort,
title: step.title,
type: step.type,
options: [this.giftBudget]
})
}
}
})
console.log(selectedSteps)
uni.showLoading()
this.giftStepLoading = true
getGiftRecommendPrompt(selectedSteps).then(res => {
const { success, data } = res
if (success) {
this.showGiftSteps = false
this.streamReq({
prompt: data.prompt || '我想送礼',
init: 0
})
// 清空所有送礼选项
this.giftSelections = {}
this.giftBudget = ''
this.currentGiftStepIndex = 0
}
}).finally(() => {
uni.hideLoading()
this.giftStepLoading = false
})
}
}
}