Compare commits

..

13 Commits

26 changed files with 698 additions and 127 deletions

View File

@ -1,15 +1,15 @@
.create-service-order { .create-service-order {
margin-left: 12px; // margin-left: 12px;
margin-right: 12px; // margin-right: 12px;
} }
.create-service-wrapper { .create-service-wrapper {
box-sizing: border-box; // box-sizing: border-box;
padding: 12px 12px 16px; padding: 12px 0;
background-color: #eff6fa; // background-color: #eff6fa;
box-shadow: 2px 2px 10px 0px rgba(0, 0, 0, 0.1); // box-shadow: 2px 2px 10px 0px rgba(0, 0, 0, 0.1);
border-radius: 4px 20px 20px 20px; // border-radius: 4px 20px 20px 20px;
border: 1px solid #fff; // border: 1px solid #fff;
} }
.order-header { .order-header {
@ -122,7 +122,7 @@
} }
.footer-help { .footer-help {
margin-top: 8px; margin-bottom: 12px;
display: flex; display: flex;
align-items: center; align-items: center;
font-size: 14px; font-size: 14px;

View File

@ -1,7 +1,7 @@
<template> <template>
<view class="module-header mb12"> <view class="module-header mb12">
<text class="module-title">{{ title }}</text> <text class="module-title">{{ title }}</text>
<image class="underline" src="./images/wave_icon.png" mode="widthFix"/> <image class="underline" src="./images/wave_icon.png" mode="aspectFill"/>
</view> </view>
</template> </template>

View File

@ -1,6 +1,8 @@
.module-header { .module-header {
position: relative; position: relative;
padding: 2px 8px 2px 3px; padding: 6px 2px 2px;
display: inline-block;
} }
.module-title { .module-title {
@ -15,11 +17,11 @@
position: absolute; position: absolute;
bottom: 3px; bottom: 3px;
left: 0; left: 0;
width: 79px; width: 100%;
height: 4px; height: 10px;
z-index: 0; z-index: 0;
} }
.mb12 { .mb12 {
margin-bottom: 12px; margin-bottom: 4px;
} }

2
constant/constant.js Normal file
View File

@ -0,0 +1,2 @@
export const SCROLL_TO_BOTTOM = 'SCROLL_TO_BOTTOM'

View File

@ -11,3 +11,8 @@ export const MessageType = {
} }
export const CompName = {
quickBookingCard: 'quickBookingCard',
createWorkOrderCard: 'createWorkOrderCard',
discoveryCard: 'discoveryCard',
}

View File

@ -1,7 +1,10 @@
<template> <template>
<view class="container">
<view class="chat-ai"> <view class="chat-ai">
<ChatMarkdown :text="text"></ChatMarkdown> <ChatMarkdown v-if="text.length > 0" :text="text"></ChatMarkdown>
<slot></slot> <slot name="content"></slot>
</view>
<slot name="footer"></slot>
</view> </view>
</template> </template>
@ -15,34 +18,26 @@
default: '' default: ''
} }
}) })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.chat-ai { .container {
margin: 6px 12px;
padding: 0 12px;
min-width: 80px;
background: rgba(255,255,255,0.4);
box-shadow: 2px 2px 10px 0px rgba(0,0,0,0.1);
border-radius: 4px 20px 20px 20px;
border: 1px solid;
border-color: #FFFFFF;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
max-width: 100%; // max-width: 100%; //
overflow-x: hidden; // overflow-x: hidden; //
text { .chat-ai {
font-family: PingFang SC, PingFang SC; margin: 6px 12px;
font-weight: 400; padding: 0 12px;
font-size: 14px;
color: #333333; min-width: 80px;
line-height: 22px; background: rgba(255,255,255,0.4);
text-align: justify; box-shadow: 2px 2px 10px 0px rgba(0,0,0,0.1);
font-style: normal; border-radius: 4px 20px 20px 20px;
text-transform: none; border: 1px solid;
border-color: #FFFFFF;
} }
} }
</style> </style>

View File

@ -27,6 +27,14 @@
<view class="area-msg-list-content" v-for="item in chatMsgList" :key="item.msgId" :id="item.msgId"> <view class="area-msg-list-content" v-for="item in chatMsgList" :key="item.msgId" :id="item.msgId">
<template v-if="item.msgType === MessageRole.AI"> <template v-if="item.msgType === MessageRole.AI">
<ChatCardAI class="message-item-ai" :text="item.msg"> <ChatCardAI class="message-item-ai" :text="item.msg">
<template #content v-if="item.toolCall">
<QuickBookingComponent v-if="item.toolCall.componentName === CompName.quickBookingCard"/>
<DiscoveryCardComponent v-else-if="item.toolCall.componentName === CompName.discoveryCard"/>
<CreateServiceOrder v-else-if="item.toolCall.componentName === CompName.createWorkOrderCard"/>
</template>
<template #footer >
<!-- <text> 这个是底部 </text> -->
</template>
</ChatCardAI> </ChatCardAI>
</template> </template>
@ -34,14 +42,14 @@
<ChatCardMine class="message-item-mine" :text="item.msg"> <ChatCardMine class="message-item-mine" :text="item.msg">
</ChatCardMine> </ChatCardMine>
</template> </template>
<template v-else> <template v-else>
<ChatCardOther class="message-item-other" :text="item.msg"> <ChatCardOther class="message-item-other" :text="item.msg">
<OneFeelMK001 v-if="mainPageDataModel.activityList.length > 0" :activityList="mainPageDataModel.activityList"/> <ChatMoreTips @replySent="handleReply" :itemList="mainPageDataModel.guideWords"/>
<template v-if="mainPageDataModel.recommendTheme.length > 0" v-for="(item, index) in mainPageDataModel.recommendTheme" :key="item.themeName"> <ActivityListComponent v-if="mainPageDataModel.activityList.length > 0" :activityList="mainPageDataModel.activityList"/>
<OneFeelMK002 :recommendTheme="item"/>
</template>
<RecommendPostsComponent v-if="mainPageDataModel.recommendTheme.length > 0" :recommendThemeList="mainPageDataModel.recommendTheme" />
</ChatCardOther> </ChatCardOther>
</template> </template>
</view> </view>
@ -50,10 +58,9 @@
<!-- 输入框区域 --> <!-- 输入框区域 -->
<view class="footer-area"> <view class="footer-area">
<ChatMoreTips @replySent="handleReply" :itemList="mainPageDataModel.guideWords"></ChatMoreTips> <ChatQuickAccess @replySent="handleReplyInstruct"/>
<ChatQuickAccess @replySent="handleReplyInstruct"></ChatQuickAccess>
<ChatInputArea <ChatInputArea
v-model:inputMessage="inputMessage" v-model="inputMessage"
:holdKeyboard="holdKeyboard" :holdKeyboard="holdKeyboard"
@send="sendMessageAction" @send="sendMessageAction"
@noHideKeyboard="handleNoHideKeyboard" @noHideKeyboard="handleNoHideKeyboard"
@ -68,6 +75,9 @@
import { ref } from 'vue' import { ref } from 'vue'
import { defineEmits } from 'vue' import { defineEmits } from 'vue'
import { onLoad } from '@dcloudio/uni-app'; import { onLoad } from '@dcloudio/uni-app';
import { SCROLL_TO_BOTTOM } from '../../constant/constant'
import { MessageRole, MessageType, CompName } from '../../model/ChatModel';
import ChatTopWelcome from './ChatTopWelcome.vue'; import ChatTopWelcome from './ChatTopWelcome.vue';
import ChatTopBgImg from './ChatTopBgImg.vue'; import ChatTopBgImg from './ChatTopBgImg.vue';
import ChatTopNavBar from './ChatTopNavBar.vue'; import ChatTopNavBar from './ChatTopNavBar.vue';
@ -79,14 +89,18 @@
import ChatInputArea from './ChatInputArea.vue' import ChatInputArea from './ChatInputArea.vue'
import CommandWrapper from '@/components/CommandWrapper/index.vue' import CommandWrapper from '@/components/CommandWrapper/index.vue'
import { MessageRole, MessageType } from '../../model/ChatModel'; import QuickBookingComponent from '../module/booking/QuickBookingComponent.vue'
import DiscoveryCardComponent from '../module/discovery/DiscoveryCardComponent.vue';
import ActivityListComponent from '../module/banner/ActivityListComponent.vue';
import RecommendPostsComponent from '../module/recommend/RecommendPostsComponent.vue';
import CreateServiceOrder from '@/components/CreateServiceOrder/index.vue'
import { agentChatStream } from '@/request/api/AgentChatStream';
import { mainPageData } from '@/request/api/MainPageDataApi';
import { conversationMsgList, recentConversation } from '@/request/api/ConversationApi';
import OneFeelMK001 from '../module/OneFeelMK001.vue';
import OneFeelMK002 from '../module/OneFeelMK002.vue';
import { agentChatStream } from '../../request/api/AgentChatStream';
import { mainPageData } from '../../request/api/MainPageData';
import { conversationMsgList } from '../../request/api/ConversationMsgList';
import { recentConversation } from '../../request/api/RecentConversation';
/// ///
const statusBarHeight = ref(20); const statusBarHeight = ref(20);
@ -116,6 +130,8 @@
// //
let isSessionActive = false; let isSessionActive = false;
///
let commonType = ''
// //
@ -167,8 +183,13 @@
}; };
/// ///
const handleReplyInstruct = (text) => { const handleReplyInstruct = (item) => {
sendMessage(text, true) if(item.type === 'MyOrder') {
///
return
}
commonType = item.type
sendMessage(item.title, true)
setTimeoutScrollToBottom() setTimeoutScrollToBottom()
} }
@ -196,8 +217,17 @@
getMainPageData() getMainPageData()
await loadRecentConversation() await loadRecentConversation()
loadConversationMsgList() loadConversationMsgList()
addNoticeListener()
}) })
const addNoticeListener = () => {
uni.$on(SCROLL_TO_BOTTOM, (value) => {
setTimeout(() => {
scrollToBottom()
}, 200)
})
}
/// id /// id
const loadRecentConversation = async() => { const loadRecentConversation = async() => {
const res = await recentConversation() const res = await recentConversation()
@ -271,7 +301,7 @@
conversationId: conversationId.value, conversationId: conversationId.value,
agentId: agentId.value, agentId: agentId.value,
messageType: isInstruct ? 1 : 0, messageType: isInstruct ? 1 : 0,
messageContent: message messageContent: isInstruct ? commonType : message
} }
// AI // AI
@ -305,9 +335,10 @@
// //
agentChatStream(args, (chunk) => { agentChatStream(args, (chunk) => {
console.log('分段内容:', chunk) console.log('分段内容:', chunk)
if (chunk.error) { if (chunk && chunk.error) {
chatMsgList.value[aiMsgIndex].msg = '请求错误,请重试'; chatMsgList.value[aiMsgIndex].msg = '请求错误,请重试';
clearInterval(finishInterval); clearInterval(loadingTimer);
loadingTimer = null;
isTyping = false; isTyping = false;
typeWriterTimer = null; typeWriterTimer = null;
isSessionActive = false; // isSessionActive = false; //
@ -334,10 +365,30 @@
if (chunk && chunk.finish) { if (chunk && chunk.finish) {
// //
const finishInterval = setInterval(() => { const finishInterval = setInterval(() => {
console.log('aiMsgBuffer.length:', aiMsgBuffer.length)
if (aiMsgBuffer.length === 0) { if (aiMsgBuffer.length === 0) {
clearInterval(finishInterval); clearInterval(finishInterval);
clearInterval(loadingTimer);
loadingTimer = null;
isTyping = false; isTyping = false;
isSessionActive = false; // typeWriterTimer = null;
// '.'
const msg = chatMsgList.value[aiMsgIndex].msg;
console.log('msg:', msg)
if (!msg || msg === '加载中.' || msg.startsWith('加载中')) {
chatMsgList.value[aiMsgIndex].msg = '未获取到内容,请重试';
if(chunk.toolCall) {
chatMsgList.value[aiMsgIndex].msg = '';
}
}
if(chunk.toolCall) {
console.log('chunk.toolCall:', chunk.toolCall)
chatMsgList.value[aiMsgIndex].toolCall = chunk.toolCall
}
isSessionActive = false;
scrollToBottom(); scrollToBottom();
} }
}, 50); }, 50);

View File

@ -36,6 +36,7 @@
<style lang="scss" scoped> <style lang="scss" scoped>
.more-tips { .more-tips {
width: 100%; width: 100%;
margin-bottom: 12px;
&-scroll { &-scroll {
display: flex; display: flex;
@ -61,13 +62,13 @@
flex-direction: column; flex-direction: column;
min-width: 46px; min-width: 46px;
&:first-child { // &:first-child {
margin-left: 12px; // margin-left: 12px;
} // }
&:last-child { // &:last-child {
margin-right: 12px; // margin-right: 12px;
} // }
.more-tips-item-title { .more-tips-item-title {
font-family: PingFang SC, PingFang SC; font-family: PingFang SC, PingFang SC;

View File

@ -1,7 +1,7 @@
<template> <template>
<view class="quick-access"> <view class="quick-access">
<view class="quick-access-scroll"> <view class="quick-access-scroll">
<view class="quick-access-item" v-for="(item, index) in itemList" :key="index" @click="sendReply(item.title)"> <view class="quick-access-item" v-for="(item, index) in itemList" :key="index" @click="sendReply(item)">
<image class="quick-access-item-bg" src="/static/quick/quick_icon_bg.png" mode="aspectFill"></image> <image class="quick-access-item-bg" src="/static/quick/quick_icon_bg.png" mode="aspectFill"></image>
<view class="quick-access-item-title"> <view class="quick-access-item-title">
<image :src="item.icon"></image> <image :src="item.icon"></image>
@ -19,8 +19,8 @@
const emits = defineEmits(['replySent']); const emits = defineEmits(['replySent']);
const sendReply = (text) => { const sendReply = (item) => {
emits('replySent', text); // emits('replySent', item); //
} }
onMounted(() => { onMounted(() => {
@ -33,21 +33,25 @@
icon: '/static/quick/quick_icon_yuding.png', icon: '/static/quick/quick_icon_yuding.png',
title: '预定门票', title: '预定门票',
content: '快速预定天沐温泉门票', content: '快速预定天沐温泉门票',
type: 'Command.quickBooking'
}, },
{ {
icon: '/static/quick/quick_icon_find.png', icon: '/static/quick/quick_icon_find.png',
title: '探索发现', title: '探索发现',
content: '亲子、团建等更多玩法', content: '亲子、团建等更多玩法',
type: 'Command.discovery'
}, },
{ {
icon: '/static/quick/quick_icon_call.png', icon: '/static/quick/quick_icon_call.png',
title: '呼叫服务', title: '呼叫服务',
content: '加床、订麻将机...', content: '加床、订麻将机...',
type: 'Command.createWorkOrder'
}, },
{ {
icon: '/static/quick/quick_icon_order.png', icon: '/static/quick/quick_icon_order.png',
title: '我的订单', title: '我的订单',
content: '快速查看订单', content: '快速查看订单',
type: 'MyOrder'
} }
] ]
} }

View File

@ -4,7 +4,7 @@
<!-- :style="backgroundStyle" --> <!-- :style="backgroundStyle" -->
<view class="top-item1-left"> <view class="top-item1-left">
<image :src="initPageImages.welcomeImageUrl"></image> <image :src="initPageImages.welcomeImageUrl"></image>
<text>{{ currentDate }} 多云 -36 gg </text> <text>{{ currentDate }} 多云 -36 cc </text>
</view> </view>
<view class="top-item1-right"> <view class="top-item1-right">
<image :src="initPageImages.logoImageUrl"></image> <image :src="initPageImages.logoImageUrl"></image>
@ -23,8 +23,8 @@
type: Object, type: Object,
default: { default: {
backgroundImageUrl: '', backgroundImageUrl: '',
logoImageUrl: '/static/hello_banner_icon@2x.png', logoImageUrl: '',
welcomeImageUrl: '/static/hello_banner_bg@2x.png' welcomeImageUrl: ''
} }
}, },
welcomeContent: { welcomeContent: {

View File

@ -0,0 +1,119 @@
<template>
<view class="date-picker">
<view class="date-list">
<view
v-for="(item, index) in dates"
:key="index"
class="date-item"
:class="{ active: index === activeIndex }"
@click="selectDate(index)"
>
<text class="label">{{ item.label }}</text>
<text class="date">{{ item.date }}</text>
</view>
<!-- 日历按钮 -->
<view class="calendar-btn btn-bom" @click="openCalendar">
<image src="/static/booking_calendar.png" mode="widthFix" class="calendar-img" />
<text class="calendar-text">日历</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue';
const emit = defineEmits(['update:date']); //
const activeIndex = ref(2); //
const dates = ref([]);
//
const initDates = () => {
const today = new Date();
const labels = ['前天', '昨天', '今天', '明天', '后天'];
for (let i = -2; i <= 2; i++) {
const d = new Date(today);
d.setDate(today.getDate() + i);
const month = d.getMonth() + 1;
const day = d.getDate();
dates.value.push({
label: labels[i + 2],
date: `${month}/${day}`,
fullDate: `${d.getFullYear()}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`
});
}
};
const selectDate = (index) => {
activeIndex.value = index;
emit('update:date', dates.value[index]); //
};
const openCalendar = () => {
uni.showToast({ title: '打开日历', icon: 'none' });
};
onMounted(() => {
initDates();
});
</script>
<style scoped>
.date-picker {
background: rgba(140, 236, 255, 0.24);
padding: 8rpx 0;
border-radius: 8rpx;
margin-top: 12px;
}
.date-list {
display: flex;
}
.date-item,
.calendar-btn {
flex: 1; /* 等宽 */
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 12rpx 0;
border-radius: 16rpx;
}
.label {
font-size: 24rpx;
color: #999;
}
.date {
font-size: 28rpx;
font-weight: bold;
color: #555;
}
.date-item.active {
background-color: #00A6FF;
}
.date-item.active .label,
.date-item.active .date {
color: #fff;
}
/* 日历按钮 */
.calendar-btn {
background: #fff;
border-radius: 0 16rpx 16rpx 0;
margin: -8rpx 0;
box-shadow: 0 4rpx 8rpx rgba(0, 0, 0, 0.1); /* 阴影 */
}
.calendar-img {
width: 48rpx;
height: 48rpx;
}
.calendar-text {
font-size: 22rpx;
color: #666;
margin-top: 4rpx;
}
</style>

View File

@ -0,0 +1,78 @@
<template>
<view class="container">
<QuickBookingCalender class="calendar" @update:date="onDateSelected" />
<view v-for="item in commodityGroupDTOList" :key="commodityGroupKey(item.title)">
<QuickBookingContentList :commodityDTO="item" />
</view>
</view>
</template>
<script setup>
import QuickBookingCalender from './QuickBookingCalender.vue'
import QuickBookingContentList from './QuickBookingContentList.vue'
import { ref, nextTick } from 'vue'
import { onMounted } from 'vue'
import { quickBookingComponent } from '@/request/api/MainPageDataApi'
import { SCROLL_TO_BOTTOM } from '@/constant/constant'
const selectedDate = ref({});
const commodityGroupDTOList = ref([])
const formattedDate = ref('')
const loadQuickBookingComponent = async () => {
formattedDate.value = formatDate(selectedDate.value.fullDate || new Date());
const res = await quickBookingComponent(formattedDate.value)
if(res.code === 0 && res.data) {
commodityGroupDTOList.value = res.data.commodityGroupDTOList
nextTick(() => {
setTimeout(() => {
uni.$emit(SCROLL_TO_BOTTOM, true)
}, 300)
});
}
}
const onDateSelected = (date) => {
console.log('Selected date:', date);
selectedDate.value = date;
loadQuickBookingComponent();
};
// yyyy-MM-dd
const formatDate = (date) => {
const d = new Date(date);
const year = d.getFullYear();
const month = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
};
const commodityGroupKey = (title) => {
return `${title}${formattedDate.value}`
}
onMounted(() => {
console.log('=============')
loadQuickBookingComponent()
})
</script>
<style scoped lang="scss">
.container {
width: 100%;
flex: 1;
margin-bottom: 12px;
.calendar {
width: 100%;
height: 58px;
margin: 12px 0 6px;
}
}
</style>

View File

@ -0,0 +1,176 @@
<template>
<view class="container">
<ModuleTitle :title="commodityDTO.title" />
<view class="container-scroll">
<view class="mk-card-item" v-for="(item) in commodityDTO.commodityList" :key="item.commodityName">
<view class="card-badge">超值推荐</view>
<image class="card-img" :src="item.commodityIcon" mode="widthFix" />
<view class="card-content">
<view class="card-title-column">
<text class="card-title">{{ item.commodityName }}</text>
<view class="card-tags">
<text class="card-tag">随时可退</text>
<text class="card-tag">民俗表演</text>
</view>
</view>
<view class="card-desc">· 往返观光车票</view>
<view class="card-desc">· 营业时间9:00-22:00</view>
<view class="card-bottom-row">
<view class="card-price-row">
<text class="card-price-fu"></text>
<text class="card-price">{{ item.commodityPrice }}</text>
<text class="card-unit">/</text>
</view>
<text class="card-btn">下单</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import ModuleTitle from '@/components/ModuleTitle/index.vue'
import { defineProps } from 'vue'
const props = defineProps({
commodityDTO: {
type: Object,
default: {}
}
})
</script>
<style lang="scss" scoped>
.container {
.container-scroll {
display: flex;
flex-direction: row;
overflow-x: auto;
overflow-y: hidden;
margin: 4px 0;
/* 隐藏滚动条 */
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
.mk-card-item {
position: relative;
display: flex;
flex-direction: column;
align-items: start;
width: 188px;
height: 244px;
background-color: #ffffff;
border-radius: 10px;
margin-right: 8px;
padding-bottom: 12px;
.card-badge {
position: absolute;
top: 8px;
left: 8px;
background: #ffe7b2;
color: #b97a00;
font-size: 12px;
padding: 2px 8px;
border-radius: 4px;
z-index: 2;
}
.card-img {
width: 188px;
height: 114px;
border-radius: 10px;
}
.card-content {
box-sizing: border-box;
padding: 10px 12px 0 12px;
display: flex;
flex-direction: column;
align-items: start;
width: 100%;
}
.card-title-column {
display: flex;
align-items: start;
flex-direction: column;
}
.card-title {
font-size: 16px;
font-weight: bold;
color: #222;
}
.card-tags {
display: flex;
flex-direction: row;
align-items: start;
padding: 6px 0;
}
.card-tag {
background: #f5f5f5;
color: #ff6600;
font-size: 12px;
border-radius: 4px;
padding: 0 6px;
margin-left: 2px;
}
.card-desc {
font-size: 13px;
color: #888;
margin-top: 2px;
}
.card-bottom-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 8px;
width: 100%;
}
.card-price-row {
.card-price-fu {
color: #ff6600;
font-size: 11px;
font-weight: normal;
}
.card-price {
color: #ff6600;
font-size: 16px;
font-weight: bold;
}
.card-unit {
font-size: 11px;
color: #888;
font-weight: normal;
margin-left: 2px;
}
}
.card-btn {
background: #ff6600;
color: #fff;
font-size: 15px;
border-radius: 20px;
padding: 0 18px;
height: 32px;
line-height: 32px;
}
}
}
}
</style>

View File

@ -0,0 +1,42 @@
<template>
<view class="container">
<view v-for="item in themeDTOList" :key="item.title">
<DiscoveryCradContentList :recommendTheme="item" />
</view>
</view>
</template>
<script setup>
import { ref, nextTick } from 'vue'
import { onMounted } from 'vue'
import { discoveryCradComponent } from '@/request/api/MainPageDataApi'
import { SCROLL_TO_BOTTOM } from '@/constant/constant'
import DiscoveryCradContentList from './DiscoveryCradContentList.vue'
const themeDTOList = ref([])
const loadDiscoveryCradComponent = async () => {
const res = await discoveryCradComponent()
if(res.code === 0 && res.data) {
themeDTOList.value = res.data.themeDTOList
nextTick(() => {
uni.$emit(SCROLL_TO_BOTTOM, true)
});
}
}
onMounted(() => {
console.log('=============')
loadDiscoveryCradComponent()
})
</script>
<style scoped lang="scss">
.container {
width: 100%;
flex: 1;
margin-bottom: 12px;
}
</style>

View File

@ -1,9 +1,6 @@
<template> <template>
<view class="container"> <view class="container">
<view class="mk-title"> <ModuleTitle :title="recommendTheme.themeName" />
<text class="title"> {{ recommendTheme.themeName }}</text>
<image class="wave" src="@/static/wave_icon.png" mode="widthFix"/>
</view>
<view class="container-scroll"> <view class="container-scroll">
<view class="mk-card-item" v-for="(item, index) in recommendTheme.recommendPostsList" :key="index"> <view class="mk-card-item" v-for="(item, index) in recommendTheme.recommendPostsList" :key="index">
<image :src="item.coverPhoto" mode="widthFix"></image> <image :src="item.coverPhoto" mode="widthFix"></image>
@ -14,6 +11,8 @@
</template> </template>
<script setup> <script setup>
import ModuleTitle from '@/components/ModuleTitle/index.vue'
import { defineProps } from 'vue' import { defineProps } from 'vue'
const props = defineProps({ const props = defineProps({
recommendTheme: { recommendTheme: {
@ -26,25 +25,6 @@
<style lang="scss" scoped> <style lang="scss" scoped>
.container { .container {
.mk-title {
padding: 4px 0;
display: inline-block;
position: relative;
.title {
font-weight: 400;
font-size: 16px;
color: #000000;
z-index: 2;
position: relative;
}
.wave {
position: absolute;
bottom: 2px;
left: 0;
width: 100%;
z-index: 1;
}
}
.container-scroll { .container-scroll {
display: flex; display: flex;

View File

@ -0,0 +1,29 @@
<template>
<view class="container">
<view v-for="item in recommendThemeList" :key="item.themeName">
<RecommendPostsList :recommendTheme="item" />
</view>
</view>
</template>
<script setup>
import RecommendPostsList from './RecommendPostsList.vue';
import { defineProps } from 'vue'
const props = defineProps({
recommendThemeList: {
type: Array,
default: []
}
})
</script>
<style scoped lang="scss">
.container {
width: 100%;
flex: 1;
margin-bottom: 12px;
}
</style>

View File

@ -0,0 +1,64 @@
<template>
<view class="container">
<ModuleTitle :title="recommendTheme.themeName" />
<view class="container-scroll">
<view class="mk-card-item" v-for="(item, index) in recommendTheme.recommendPostsList" :key="index">
<image :src="item.coverPhoto" mode="widthFix"></image>
<text>{{ item.topic }}</text>
</view>
</view>
</view>
</template>
<script setup>
import ModuleTitle from '@/components/ModuleTitle/index.vue'
import { defineProps } from 'vue'
const props = defineProps({
recommendTheme: {
type: Object,
default: {}
}
})
</script>
<style lang="scss" scoped>
.container {
.container-scroll {
display: flex;
flex-direction: row;
overflow-x: auto;
margin-top: 4px;
/* 隐藏滚动条 */
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
.mk-card-item {
display: flex;
flex-direction: column;
align-items: start;
width: 188px;
height: 154px;
background-color: #ffffff;
border-radius: 10px;
margin-right: 8px;
image {
width: 188px;
height: 112px;
}
text {
padding: 12px;
text-align: center;
font-weight: 500;
font-size: 12px;
color: #333333;
}
}
}
}
</style>

View File

@ -12,6 +12,7 @@ const API = '/agent/assistant/chat';
function agentChatStream(params, onChunk) { function agentChatStream(params, onChunk) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const token = uni.getStorageSync('token'); const token = uni.getStorageSync('token');
let hasError = false;
console.log("发送请求内容: ", params) console.log("发送请求内容: ", params)
// #ifdef MP-WEIXIN // #ifdef MP-WEIXIN
@ -34,7 +35,7 @@ function agentChatStream(params, onChunk) {
reject(err); reject(err);
}, },
complete(res) { complete(res) {
if(res.statusCode === 500) { if(res.statusCode !== 200) {
console.log("====> ", JSON.stringify(res)) console.log("====> ", JSON.stringify(res))
if (onChunk) { if (onChunk) {
@ -47,9 +48,18 @@ function agentChatStream(params, onChunk) {
requestTask.onHeadersReceived(res => { requestTask.onHeadersReceived(res => {
console.log('onHeadersReceived', res); console.log('onHeadersReceived', res);
const status = res.statusCode || (res.header && res.header.statusCode);
if (status && status !== 200) {
hasError = true;
if (onChunk) {
onChunk({ error: true, message: `服务器错误(${status})`, detail: res });
}
requestTask.abort && requestTask.abort();
}
}); });
requestTask.onChunkReceived(res => { requestTask.onChunkReceived(res => {
if (hasError) return;
const base64 = uni.arrayBufferToBase64(res.data); const base64 = uni.arrayBufferToBase64(res.data);
let data = ''; let data = '';
try { try {

View File

@ -0,0 +1,16 @@
import request from "../base/request";
/// 最近会话
function recentConversation() {
return request.get('/hotelBiz/chat/recentConversation');
}
/// 会话消息列表
function conversationMsgList(args) {
return request.post('/hotelBiz/chat/conversationMessageList', args);
}
export {
recentConversation,
conversationMsgList
};

View File

@ -1,7 +0,0 @@
import request from "../base/request";
function conversationMsgList(args) {
return request.post('/hotelBiz/chat/conversationMessageList', args);
}
export { conversationMsgList }

View File

@ -1,8 +0,0 @@
import request from "../base/request";
function mainPageData(sceneId) {
const args = { sceneId : sceneId}
return request.post('/hotelBiz/mainScene/mainPageData', args);
}
export { mainPageData }

View File

@ -0,0 +1,26 @@
import request from "../base/request";
/// 主页数据
function mainPageData(sceneId) {
const args = { sceneId : sceneId}
return request.post('/hotelBiz/mainScene/mainPageData', args);
}
/// 快速预订组件
function quickBookingComponent(selectedData) {
const args = { selectedData: selectedData }
return request.post('/hotelBiz/mainScene/quickBookingComponent', args);
}
/// 探索发现卡片组件
function discoveryCradComponent() {
return request.get('/hotelBiz/mainScene//discoveryComponent', {});
}
export {
mainPageData,
quickBookingComponent,
discoveryCradComponent
}

View File

@ -1,7 +0,0 @@
import request from "../base/request";
function quickBookingComponent() {
return request.post('/mainScene/quickBookingComponent');
}
export { quickBookingComponent }

View File

@ -1,7 +0,0 @@
import request from "../base/request";
function recentConversation() {
return request.get('/hotelBiz/chat/recentConversation');
}
export{ recentConversation }

BIN
static/booking_calendar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 791 B