Compare commits

..

7 Commits

13 changed files with 1535 additions and 1438 deletions

View File

@ -47,6 +47,11 @@ body,
width: 100vw;
}
/*每个页面公共css */
::-webkit-scrollbar {
display: none;
}
.mb12 {
margin-bottom: 12px;
}

View File

@ -1,7 +1,10 @@
<template>
<view class="container">
<view class="chat-ai">
<view class="loading-container" >
<image v-if="isLoading" class="loading-img" src="/static/msg_loading.svg" />
<ChatMarkdown :key="textKey" :text="processedText" />
</view>
<slot name="content"></slot>
</view>
<slot name="footer"></slot>
@ -17,6 +20,10 @@ const props = defineProps({
type: String,
default: "",
},
isLoading: {
type: Boolean,
default: false,
},
});
// key
@ -62,7 +69,7 @@ watch(
margin: 6px 12px;
padding: 0 12px;
min-width: 80px;
min-width: 90px;
background: rgba(255, 255, 255, 0.4);
box-shadow: 2px 2px 6px 0px rgba(0, 0, 0, 0.1);
border-radius: 4px 20px 20px 20px;
@ -70,4 +77,17 @@ watch(
border-color: #ffffff;
}
}
.loading-container {
display: flex;
align-items: center;
padding: 4px 0;
}
.loading-img {
margin-left: -4px;
width: 32px;
height: 32px;
}
</style>

View File

@ -3,7 +3,10 @@
<view v-if="!visibleWaveBtn" class="area-input">
<!-- 语音/键盘切换 -->
<view class="input-container-voice" @click="toggleVoiceMode">
<image v-if="!isVoiceMode" src="/static/input_voice_icon.png"></image>
<image
v-if="!isVoiceMode"
src="/static/input_voice_icon.png"
></image>
<image v-else src="/static/input_keyboard_icon.png"></image>
</view>
@ -12,9 +15,9 @@
<textarea
ref="textareaRef"
v-if="!isVoiceMode"
:class="['textarea', ios ? 'ios' : 'android']"
class="textarea"
type="text"
cursor-spacing="65"
cursor-spacing="20"
confirm-type="send"
v-model="inputMessage"
auto-height
@ -90,11 +93,6 @@ const keyboardHeight = ref(0);
const isVoiceMode = ref(false);
const visibleWaveBtn = ref(false);
// iOS
const ios = computed(() => {
return uni.getSystemInfoSync().platform === "ios";
});
//
watch(
() => props.modelValue,
@ -237,7 +235,7 @@ defineExpose({ focusInput });
<style scoped lang="scss">
.area-input {
display: flex;
align-items: flex-end;
align-items: center;
border-radius: 22px;
background-color: #ffffff;
box-shadow: 0px 0px 20px 0px rgba(52, 25, 204, 0.05);
@ -248,6 +246,7 @@ defineExpose({ focusInput });
display: flex;
align-items: center;
justify-content: center;
align-self: flex-end;
width: 44px;
height: 44px;
@ -280,6 +279,7 @@ defineExpose({ focusInput });
display: flex;
align-items: center;
justify-content: center;
align-self: flex-end;
width: 44px;
height: 44px;
@ -302,17 +302,10 @@ defineExpose({ focusInput });
box-sizing: border-box;
width: 100%;
max-height: 92px;
min-height: 44px;
min-height: 22px;
font-size: 16px;
line-height: normal;
&.android {
padding: 12px 0;
}
&.ios {
padding: 6px 0;
}
line-height: 22px;
margin: 6px 0;
&::placeholder {
color: #cccccc;

View File

@ -38,23 +38,30 @@
<template v-if="item.msgType === MessageRole.AI">
<ChatCardAI
class="message-item-ai"
:key="`ai-${item.msgId}-${item.msg ? item.msg.length : 0}`"
:key="`ai-${item.msgId}-${
item.msg ? item.msg.length : 0
}`"
:text="item.msg || ''"
,
:isLoading="item.isLoading"
>
<template #content v-if="item.toolCall">
<QuickBookingComponent
v-if="
item.toolCall.componentName === CompName.quickBookingCard
item.toolCall.componentName ===
CompName.quickBookingCard
"
/>
<DiscoveryCardComponent
v-else-if="
item.toolCall.componentName === CompName.discoveryCard
item.toolCall.componentName ===
CompName.discoveryCard
"
/>
<CreateServiceOrder
v-else-if="
item.toolCall.componentName === CompName.createWorkOrderCard
item.toolCall.componentName ===
CompName.createWorkOrderCard
"
/>
<DetailCardCompontent
@ -77,12 +84,18 @@
</template>
<template v-else-if="item.msgType === MessageRole.ME">
<ChatCardMine class="message-item-mine" :text="item.msg">
<ChatCardMine
class="message-item-mine"
:text="item.msg"
>
</ChatCardMine>
</template>
<template v-else>
<ChatCardOther class="message-item-other" :text="item.msg">
<ChatCardOther
class="message-item-other"
:text="item.msg"
>
<ChatMoreTips
@replySent="handleReply"
:itemList="mainPageDataModel.guideWords"
@ -94,8 +107,12 @@
/>
<RecommendPostsComponent
v-if="mainPageDataModel.recommendTheme.length > 0"
:recommendThemeList="mainPageDataModel.recommendTheme"
v-if="
mainPageDataModel.recommendTheme.length > 0
"
:recommendThemeList="
mainPageDataModel.recommendTheme
"
/>
</ChatCardOther>
</template>
@ -121,7 +138,7 @@
</view>
</template>
<script setup >
<script setup>
import { onMounted, nextTick, onUnmounted, ref, defineEmits } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import {
@ -204,6 +221,7 @@ let loadingTimer = null;
const emits = defineEmits(["openDrawer"]);
const openDrawer = () => emits("openDrawer");
/// =============================
const handleTouchEnd = () => {
clearTimeout(timer.value);
timer.value = setTimeout(() => {
@ -293,6 +311,32 @@ const sendMessageAction = (inputText) => {
setTimeoutScrollToBottom();
};
///
const addNoticeListener = () => {
uni.$on(SCROLL_TO_BOTTOM, () => {
setTimeout(() => {
scrollToBottom();
}, 200);
});
uni.$on(RECOMMEND_POSTS_TITLE, (value) => {
console.log("RECOMMEND_POSTS_TITLE:", value);
if (value && value.length > 0) {
handleReply(value);
}
});
uni.$on(SEND_COMMAND_TEXT, (value) => {
console.log("SEND_COMMAND_TEXT:", value);
if (value && value.length > 0) {
commonType = "Command.quickBooking";
sendMessage(value, true);
setTimeoutScrollToBottom();
}
});
};
/// =============================
onLoad(() => {
uni.getSystemInfo({
success: (res) => {
@ -314,6 +358,38 @@ onMounted(async () => {
}
});
/// =============================
// id
const loadRecentConversation = async () => {
const res = await recentConversation();
if (res.code === 0) {
conversationId.value = res.data.conversationId;
}
};
//
let historyCurrentPageNum = 1;
const loadConversationMsgList = async () => {
const args = {
pageNum: historyCurrentPageNum++,
pageSize: 10,
conversationId: conversationId.value,
};
const res = await conversationMsgList(args);
};
//
const getMainPageData = async () => {
const res = await mainPageData(sceneId.value);
if (res.code === 0) {
mainPageDataModel.value = res.data;
agentId.value = res.data.agentId;
initData();
setTimeoutScrollToBottom();
}
};
/// =============================
// WebSocket
const initWebSocket = () => {
//
@ -375,17 +451,16 @@ const initWebSocket = () => {
// WebSocket
const handleWebSocketMessage = (data) => {
if(loadingTimer) {
if (loadingTimer) {
clearInterval(loadingTimer);
loadingTimer = null;
}
const aiMsgIndex = chatMsgList.value.length - 1;
if (!chatMsgList.value[aiMsgIndex] || aiMsgIndex < 0) {
return;
}
//console.log("WebSocket:", data);
//
if (data.content && typeof data.content !== "string") {
data.content = String(data.content);
@ -417,11 +492,12 @@ const handleWebSocketMessage = (data) => {
}
const msg = chatMsgList.value[aiMsgIndex].msg;
console.log('全量消息内容:', msg)
if (!msg || msg === '加载中.' || msg.startsWith('加载中')) {
chatMsgList.value[aiMsgIndex].msg = '未获取到内容,请重试';
if(data.toolCall) {
chatMsgList.value[aiMsgIndex].msg = '';
console.log("全量消息内容:", msg);
if (!msg || msg === "加载中." || msg.startsWith("加载中")) {
chatMsgList.value[aiMsgIndex].msg = "未获取到内容,请重试";
chatMsgList.value[aiMsgIndex].isLoading = false;
if (data.toolCall) {
chatMsgList.value[aiMsgIndex].msg = "";
}
}
@ -471,6 +547,7 @@ const initTypewriterManager = () => {
onContentUpdate: (content) => {
const aiMsgIndex = chatMsgList.value.length - 1;
if (aiMsgIndex >= 0 && chatMsgList.value[aiMsgIndex]) {
chatMsgList.value[aiMsgIndex].isLoading = false;
chatMsgList.value[aiMsgIndex].msg = content;
}
},
@ -478,6 +555,7 @@ const initTypewriterManager = () => {
onTypingComplete: (finalContent) => {
const aiMsgIndex = chatMsgList.value.length - 1;
if (aiMsgIndex >= 0 && chatMsgList.value[aiMsgIndex]) {
chatMsgList.value[aiMsgIndex].isLoading = false;
chatMsgList.value[aiMsgIndex].msg = finalContent;
}
//
@ -493,60 +571,6 @@ const resetMessageState = () => {
}
};
const addNoticeListener = () => {
uni.$on(SCROLL_TO_BOTTOM, () => {
setTimeout(() => {
scrollToBottom();
}, 200);
});
uni.$on(RECOMMEND_POSTS_TITLE, (value) => {
console.log("RECOMMEND_POSTS_TITLE:", value);
if (value && value.length > 0) {
handleReply(value);
}
});
uni.$on(SEND_COMMAND_TEXT, (value) => {
console.log("SEND_COMMAND_TEXT:", value);
if (value && value.length > 0) {
commonType = "Command.quickBooking";
sendMessage(value, true);
setTimeoutScrollToBottom();
}
});
};
// id
const loadRecentConversation = async () => {
const res = await recentConversation();
if (res.code === 0) {
conversationId.value = res.data.conversationId;
}
};
//
let historyCurrentPageNum = 1;
const loadConversationMsgList = async () => {
const args = {
pageNum: historyCurrentPageNum++,
pageSize: 10,
conversationId: conversationId.value,
};
const res = await conversationMsgList(args);
};
//
const getMainPageData = async () => {
const res = await mainPageData(sceneId.value);
if (res.code === 0) {
mainPageDataModel.value = res.data;
agentId.value = res.data.agentId;
initData();
setTimeoutScrollToBottom();
}
};
//
const initData = () => {
const msg = {
@ -656,6 +680,11 @@ const stopRequest = () => {
// (messageType=2)
sendWebSocketMessage(2, "stop_request", { silent: true });
if (loadingTimer) {
clearInterval(loadingTimer);
loadingTimer = null;
}
//
if (typewriterManager) {
typewriterManager.stopTypewriter();

View File

@ -50,12 +50,6 @@ const sendReply = (text) => {
-webkit-overflow-scrolling: touch;
padding-bottom: 12px;
box-sizing: border-box;
/* 隐藏滚动条 */
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}
.more-tips-item {

View File

@ -1,65 +1,75 @@
<template>
<view class="quick-access">
<view class="quick-access-scroll">
<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>
<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>
<view class="quick-access-item-title">
<image :src="item.icon"></image>
<text>{{ item.title }}</text>
</view>
<text class="quick-access-item-content">{{ item.content }}</text>
<text class="quick-access-item-content">{{
item.content
}}</text>
</view>
</view>
</view>
</template>
<script setup>
import { onMounted, ref } from 'vue';
const itemList = ref([])
import { onMounted, ref } from "vue";
const itemList = ref([]);
const emits = defineEmits(['replySent']);
const emits = defineEmits(["replySent"]);
const sendReply = (item) => {
emits('replySent', item); //
}
const sendReply = (item) => {
emits("replySent", item); //
};
onMounted(() => {
initData()
})
onMounted(() => {
initData();
});
const initData = () => {
const initData = () => {
itemList.value = [
{
icon: '/static/quick/quick_icon_yuding.png',
title: '快速预定',
content: '预定门票、房间、餐食',
type: 'Command.quickBooking'
icon: "/static/quick/quick_icon_yuding.png",
title: "快速预定",
content: "预定门票、房间、餐食",
type: "Command.quickBooking",
},
{
icon: '/static/quick/quick_icon_find.png',
title: '探索发现',
content: '探索玩法、出片佳地',
type: 'Command.discovery'
icon: "/static/quick/quick_icon_find.png",
title: "探索发现",
content: "探索玩法、出片佳地",
type: "Command.discovery",
},
{
icon: '/static/quick/quick_icon_order.png',
title: '订单/工单',
content: '我的订单/工单',
type: 'MyOrder'
icon: "/static/quick/quick_icon_order.png",
title: "订单/工单",
content: "我的订单/工单",
type: "MyOrder",
},
{
icon: '/static/quick/quick_icon_call.png',
title: '呼叫服务',
content: '加水、客房服务等',
type: 'Command.createWorkOrder'
}
]
}
icon: "/static/quick/quick_icon_call.png",
title: "呼叫服务",
content: "加水、客房服务等",
type: "Command.createWorkOrder",
},
];
};
</script>
<style lang="scss" scoped>
.quick-access {
.quick-access {
width: 100%;
&-scroll {
@ -68,19 +78,13 @@
overflow-x: auto;
white-space: nowrap;
-webkit-overflow-scrolling: touch;
/* 隐藏滚动条 */
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}
.quick-access-item {
flex: 0 0 104px;
border-radius: 8px;
margin: 4px 4px 8px 4px;
box-shadow: 0 2px 5px 0px rgba(0,0,0,0.1);
box-shadow: 0 2px 5px 0px rgba(0, 0, 0, 0.1);
padding: 12px;
display: inline-flex;
flex-direction: column;
@ -120,7 +124,7 @@
font-family: PingFang SC, PingFang SC;
font-weight: 500;
font-size: 12px;
color: #201F32;
color: #201f32;
line-height: 16px;
}
}
@ -131,10 +135,9 @@
font-family: PingFang SC, PingFang SC;
font-weight: 400;
font-size: 10px;
color: #678CAD;
color: #678cad;
line-height: 18px;
}
}
}
}
</style>

View File

@ -1,70 +1,75 @@
<template>
<view class="top-bg-content">
<view class="top-item" >
<view class="top-item">
<!-- :style="backgroundStyle" -->
<image class="top-item-left" :src="initPageImages.welcomeImageUrl"></image>
<image class="top-item-right" :src="initPageImages.logoImageUrl"></image>
<image
class="top-item-left"
:src="initPageImages.welcomeImageUrl"
></image>
<image
class="top-item-right"
:src="initPageImages.logoImageUrl"
></image>
</view>
<ChatCardAI v-if="welcomeContent.length" :text='welcomeContent'/>
<ChatCardAI v-if="welcomeContent.length" :text="welcomeContent" />
</view>
</template>
<script setup>
import { defineProps, computed, ref } from "vue";
import ChatCardAI from './ChatCardAI.vue';
import { defineProps, computed, ref } from "vue";
import ChatCardAI from "./ChatCardAI.vue";
const props = defineProps({
const props = defineProps({
initPageImages: {
type: Object,
default: {
backgroundImageUrl: '',
logoImageUrl: '',
welcomeImageUrl: ''
}
backgroundImageUrl: "",
logoImageUrl: "",
welcomeImageUrl: "",
},
},
welcomeContent: {
type: String,
default: '查信息、预定下单、探索玩法、呼叫服务、我通通可以满足,快试试问我问题吧!'
}
})
default:
"查信息、预定下单、探索玩法、呼叫服务、我通通可以满足,快试试问我问题吧!",
},
});
//
const backgroundStyle = computed(() => {
//
const backgroundStyle = computed(() => {
return {
backgroundImage: `url(${props.initPageImages.backgroundImageUrl})`,
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat'
}
})
backgroundSize: "cover",
backgroundPosition: "center",
backgroundRepeat: "no-repeat",
};
});
</script>
<style lang="scss" scoped>
.top-bg-content {
.top-bg-content {
display: flex;
justify-content: flex-end;
align-items: stretch;
flex-direction: column;
}
}
.top-item {
.top-item {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 0 32px;
margin-bottom: -8px;
}
margin-bottom: -6px;
}
.top-item-left {
.top-item-left {
width: 118px;
height: 52px;
}
}
.top-item-right {
.top-item-right {
width: 130px;
height: 130px;
margin: -12px 0;
}
}
</style>

View File

@ -49,14 +49,6 @@
display: flex;
flex-direction: column;
.area-msg-list-content {
/* 隐藏滚动条 */
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}
.message-item-ai {
display: flex;
justify-content: flex-start;
@ -141,15 +133,6 @@
}
}
::-webkit-scrollbar {
display: none;
width: 0 !important;
height: 0 !important;
-webkit-appearance: none;
background: transparent;
color: transparent;
}
// 打字机光标闪烁动画
.typing-cursor {
display: inline-block;

View File

@ -2,24 +2,46 @@
<view class="container">
<ModuleTitle :title="commodityDTO.title" />
<view class="container-scroll">
<view v-for="(item, index) in commodityDTO.commodityList" :key="`${item.commodityId}-${index}`">
<view
v-for="(item, index) in commodityDTO.commodityList"
:key="`${item.commodityId}-${index}`"
>
<view class="mk-card-item" @click="placeOrderHandle(item)">
<!-- <view class="card-badge">超值推荐</view> -->
<image class="card-img" :src="item.commodityIcon" mode="aspectFill" />
<image
class="card-img"
:src="item.commodityIcon"
mode="aspectFill"
/>
<view class="card-content">
<view class="card-title-column">
<text class="card-title">{{ item.commodityName }}</text>
<view class="card-tags" v-for="(tag) in item.commodityTradeRuleList" :key="tag">
<text class="card-title">{{
item.commodityName
}}</text>
<view
class="card-tags"
v-for="tag in item.commodityTradeRuleList"
:key="tag"
>
<text class="card-tag">{{ tag }}</text>
</view>
</view>
<template v-for="(serviceItem, index) in item.commodityServices" :key="serviceItem.serviceTitle">
<view v-if="index < 3" class="card-desc">· {{ serviceItem.serviceTitle }}</view>
<template
v-for="(
serviceItem, index
) in item.commodityServices"
:key="serviceItem.serviceTitle"
>
<view v-if="index < 3" class="card-desc"
>· {{ serviceItem.serviceTitle }}</view
>
</template>
<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-price">{{
item.commodityPrice
}}</text>
<text class="card-unit">/</text>
</view>
@ -33,27 +55,25 @@
</template>
<script setup>
import ModuleTitle from '@/components/ModuleTitle/index.vue'
import { defineProps } from 'vue'
const props = defineProps({
import ModuleTitle from "@/components/ModuleTitle/index.vue";
import { defineProps } from "vue";
const props = defineProps({
commodityDTO: {
type: Object,
default: {}
}
})
default: {},
},
});
///
const placeOrderHandle = (item) => {
///
const placeOrderHandle = (item) => {
uni.navigateTo({
url: `/pages/goods/index?commodityId=${item.commodityId}`,
})
}
});
};
</script>
<style lang="scss" scoped>
.container {
.container {
.container-scroll {
display: flex;
flex-direction: row;
@ -61,12 +81,6 @@
overflow-y: hidden;
margin: 4px 0;
/* 隐藏滚动条 */
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
.mk-card-item {
position: relative;
@ -195,7 +209,6 @@
line-height: 32px;
}
}
}
}
}
</style>

View File

@ -2,23 +2,45 @@
<view class="container">
<ModuleTitle title="相关商品" />
<view class="container-scroll">
<view v-for="(item, index) in commodityList" :key="`${item.commodityId}-${index}`">
<view
v-for="(item, index) in commodityList"
:key="`${item.commodityId}-${index}`"
>
<view class="mk-card-item" @click="placeOrderHandle(item)">
<image class="card-img" :src="item.commodityPhoto" mode="aspectFill" />
<image
class="card-img"
:src="item.commodityPhoto"
mode="aspectFill"
/>
<view class="card-content">
<view class="card-title-column">
<text class="card-title">{{ item.commodityName }}</text>
<view class="card-tags" v-for="(tag) in item.commodityTradeRuleList" :key="tag">
<text class="card-title">{{
item.commodityName
}}</text>
<view
class="card-tags"
v-for="tag in item.commodityTradeRuleList"
:key="tag"
>
<text class="card-tag">{{ tag }}</text>
</view>
</view>
<template v-for="(serviceItem, index) in item.commodityServices" :key="serviceItem.serviceTitle">
<view v-if="index < 3" class="card-desc">· {{ serviceItem.serviceTitle }}</view>
<template
v-for="(
serviceItem, index
) in item.commodityServices"
:key="serviceItem.serviceTitle"
>
<view v-if="index < 3" class="card-desc"
>· {{ serviceItem.serviceTitle }}</view
>
</template>
<view class="card-bottom-row">
<view class="card-price-row">
<text class="card-price-fu"></text>
<text class="card-price">{{ item.specificationPrice }}</text>
<text class="card-price">{{
item.specificationPrice
}}</text>
<text class="card-unit">/</text>
</view>
<text class="card-btn">下单</text>
@ -31,26 +53,25 @@
</template>
<script setup>
import ModuleTitle from '@/components/ModuleTitle/index.vue'
import { defineProps } from 'vue'
const props = defineProps({
import ModuleTitle from "@/components/ModuleTitle/index.vue";
import { defineProps } from "vue";
const props = defineProps({
commodityList: {
type: Array,
default: []
}
})
default: [],
},
});
///
const placeOrderHandle = (item) => {
///
const placeOrderHandle = (item) => {
uni.navigateTo({
url: `/pages/goods/index?commodityId=${item.commodityId}`,
})
}
});
};
</script>
<style lang="scss" scoped>
.container {
.container {
margin: 6px 0 0;
.container-scroll {
@ -60,12 +81,6 @@
overflow-y: hidden;
margin: 4px 0;
/* 隐藏滚动条 */
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
.mk-card-item {
position: relative;
@ -194,7 +209,6 @@
line-height: 32px;
}
}
}
}
}
</style>

View File

@ -2,7 +2,10 @@
<view class="container">
<ModuleTitle :title="recommendTheme.themeName" />
<view class="container-scroll">
<view v-for="(item, index) in recommendTheme.recommendPostsList" :key="index">
<view
v-for="(item, index) in recommendTheme.recommendPostsList"
:key="index"
>
<view class="mk-card-item" @click="sendReply(item)">
<image :src="item.coverPhoto" mode="widthFix"></image>
<text>{{ item.topic }}</text>
@ -13,38 +16,30 @@
</template>
<script setup>
import ModuleTitle from '@/components/ModuleTitle/index.vue'
import { RECOMMEND_POSTS_TITLE } from '@/constant/constant'
import { defineProps } from 'vue'
const props = defineProps({
import ModuleTitle from "@/components/ModuleTitle/index.vue";
import { RECOMMEND_POSTS_TITLE } from "@/constant/constant";
import { defineProps } from "vue";
const props = defineProps({
recommendTheme: {
type: Object,
default: {}
}
})
default: {},
},
});
const sendReply = (item) => {
const topic = item.userInputContent || item.topic.replace(/^#/, '');
const sendReply = (item) => {
const topic = item.userInputContent || item.topic.replace(/^#/, "");
uni.$emit(RECOMMEND_POSTS_TITLE, topic);
}
};
</script>
<style lang="scss" scoped>
.container {
.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;
@ -68,5 +63,5 @@
}
}
}
}
}
</style>

View File

@ -44,12 +44,6 @@
overflow-x: auto;
margin-top: 4px;
/* 隐藏滚动条 */
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
.mk-card-item {
flex-shrink: 0; /* 关键:防止 flex 布局压缩子元素宽度 */
width: 142px;

49
static/msg_loading.svg Normal file
View File

@ -0,0 +1,49 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" width="100" height="100" style="shape-rendering: auto; display: block; background: transparent;" xmlns:xlink="http://www.w3.org/1999/xlink"><g><g transform="rotate(0 50 50)">
<rect fill="#00a6ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
<animate repeatCount="indefinite" begin="-0.9166666666666666s" dur="1s" keyTimes="0;1" values="1;0" attributeName="opacity"></animate>
</rect>
</g><g transform="rotate(30 50 50)">
<rect fill="#00a6ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
<animate repeatCount="indefinite" begin="-0.8333333333333334s" dur="1s" keyTimes="0;1" values="1;0" attributeName="opacity"></animate>
</rect>
</g><g transform="rotate(60 50 50)">
<rect fill="#00a6ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
<animate repeatCount="indefinite" begin="-0.75s" dur="1s" keyTimes="0;1" values="1;0" attributeName="opacity"></animate>
</rect>
</g><g transform="rotate(90 50 50)">
<rect fill="#00a6ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
<animate repeatCount="indefinite" begin="-0.6666666666666666s" dur="1s" keyTimes="0;1" values="1;0" attributeName="opacity"></animate>
</rect>
</g><g transform="rotate(120 50 50)">
<rect fill="#00a6ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
<animate repeatCount="indefinite" begin="-0.5833333333333334s" dur="1s" keyTimes="0;1" values="1;0" attributeName="opacity"></animate>
</rect>
</g><g transform="rotate(150 50 50)">
<rect fill="#00a6ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
<animate repeatCount="indefinite" begin="-0.5s" dur="1s" keyTimes="0;1" values="1;0" attributeName="opacity"></animate>
</rect>
</g><g transform="rotate(180 50 50)">
<rect fill="#00a6ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
<animate repeatCount="indefinite" begin="-0.4166666666666667s" dur="1s" keyTimes="0;1" values="1;0" attributeName="opacity"></animate>
</rect>
</g><g transform="rotate(210 50 50)">
<rect fill="#00a6ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
<animate repeatCount="indefinite" begin="-0.3333333333333333s" dur="1s" keyTimes="0;1" values="1;0" attributeName="opacity"></animate>
</rect>
</g><g transform="rotate(240 50 50)">
<rect fill="#00a6ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
<animate repeatCount="indefinite" begin="-0.25s" dur="1s" keyTimes="0;1" values="1;0" attributeName="opacity"></animate>
</rect>
</g><g transform="rotate(270 50 50)">
<rect fill="#00a6ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
<animate repeatCount="indefinite" begin="-0.16666666666666666s" dur="1s" keyTimes="0;1" values="1;0" attributeName="opacity"></animate>
</rect>
</g><g transform="rotate(300 50 50)">
<rect fill="#00a6ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
<animate repeatCount="indefinite" begin="-0.08333333333333333s" dur="1s" keyTimes="0;1" values="1;0" attributeName="opacity"></animate>
</rect>
</g><g transform="rotate(330 50 50)">
<rect fill="#00a6ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
<animate repeatCount="indefinite" begin="0s" dur="1s" keyTimes="0;1" values="1;0" attributeName="opacity"></animate>
</rect>
</g><g></g></g><!-- [ldio] generated by https://loading.io --></svg>

After

Width:  |  Height:  |  Size: 3.3 KiB