feat: 格式化代码
This commit is contained in:
parent
9de068e7fc
commit
0e51fdcd69
@ -1,14 +1,18 @@
|
||||
<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>
|
||||
<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>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@ -16,14 +20,14 @@ import { defineProps, computed, ref, watch } from "vue";
|
||||
import ChatMarkdown from "./ChatMarkdown.vue";
|
||||
|
||||
const props = defineProps({
|
||||
text: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
isLoading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
text: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
isLoading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
// 用于强制重新渲染的key
|
||||
@ -31,63 +35,62 @@ const textKey = ref(0);
|
||||
|
||||
// 处理文本内容
|
||||
const processedText = computed(() => {
|
||||
if (!props.text) {
|
||||
return "";
|
||||
}
|
||||
if (!props.text) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// 确保文本是字符串类型
|
||||
const textStr = String(props.text);
|
||||
|
||||
// 处理加载状态的文本
|
||||
if (textStr.includes("加载中") || textStr.includes("...")) {
|
||||
return textStr;
|
||||
}
|
||||
// 确保文本是字符串类型
|
||||
const textStr = String(props.text);
|
||||
|
||||
// 处理加载状态的文本
|
||||
if (textStr.includes("加载中") || textStr.includes("...")) {
|
||||
return textStr;
|
||||
}
|
||||
|
||||
return textStr;
|
||||
});
|
||||
|
||||
// 监听text变化,强制重新渲染
|
||||
watch(
|
||||
() => props.text,
|
||||
(newText, oldText) => {
|
||||
if (newText !== oldText) {
|
||||
textKey.value++;
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
() => props.text,
|
||||
(newText, oldText) => {
|
||||
if (newText !== oldText) {
|
||||
textKey.value++;
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 100%; // ✅ 限制最大宽度
|
||||
overflow-x: hidden; // ✅ 防止横向撑开
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 100%; // ✅ 限制最大宽度
|
||||
overflow-x: hidden; // ✅ 防止横向撑开
|
||||
|
||||
.chat-ai {
|
||||
margin: 6px 12px;
|
||||
padding: 0 12px;
|
||||
.chat-ai {
|
||||
margin: 6px 12px;
|
||||
padding: 0 12px;
|
||||
|
||||
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;
|
||||
border: 1px solid;
|
||||
border-color: #ffffff;
|
||||
}
|
||||
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;
|
||||
border: 1px solid;
|
||||
border-color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.loading-img {
|
||||
margin-left: -4px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-left: -4px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@ -1,46 +1,45 @@
|
||||
<template>
|
||||
<view class="chat-mine">
|
||||
<text>{{ text }}</text>
|
||||
<slot></slot>
|
||||
</view>
|
||||
<view class="chat-mine">
|
||||
<text>{{ text }}</text>
|
||||
<slot></slot>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps } from "vue";
|
||||
defineProps({
|
||||
text: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
|
||||
import { defineProps } from "vue";
|
||||
defineProps({
|
||||
text: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.chat-mine {
|
||||
margin: 6px 12px;
|
||||
padding: 8px 16px;
|
||||
|
||||
background-color: #00A6FF;
|
||||
box-shadow: 2px 2px 10px 0px rgba(0,0,0,0.1);
|
||||
border-radius: 20px 4px 20px 20px;
|
||||
border: 1px solid;
|
||||
border-color: #FFFFFF;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 100%; // ✅ 限制最大宽度
|
||||
overflow-x: hidden; // ✅ 防止横向撑开
|
||||
|
||||
text {
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
color: #FFFFFF;
|
||||
line-height: 22px;
|
||||
text-align: justify;
|
||||
font-style: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
.chat-mine {
|
||||
margin: 6px 12px;
|
||||
padding: 8px 16px;
|
||||
|
||||
background-color: #00a6ff;
|
||||
box-shadow: 2px 2px 10px 0px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 20px 4px 20px 20px;
|
||||
border: 1px solid;
|
||||
border-color: #ffffff;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 100%; // ✅ 限制最大宽度
|
||||
overflow-x: hidden; // ✅ 防止横向撑开
|
||||
|
||||
text {
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
color: #ffffff;
|
||||
line-height: 22px;
|
||||
text-align: justify;
|
||||
font-style: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,36 +1,35 @@
|
||||
<template>
|
||||
<view class="chat-other">
|
||||
<text>{{ text }}</text>
|
||||
<slot></slot>
|
||||
</view>
|
||||
<view class="chat-other">
|
||||
<text>{{ text }}</text>
|
||||
<slot></slot>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps } from "vue";
|
||||
defineProps({
|
||||
text: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
import { defineProps } from "vue";
|
||||
defineProps({
|
||||
text: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.chat-other {
|
||||
width: 100%;
|
||||
margin: 6px 0;
|
||||
padding: 0 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 100%; // ✅ 限制最大宽度
|
||||
overflow-x: hidden; // ✅ 防止横向撑开
|
||||
|
||||
text {
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
.chat-other {
|
||||
width: 100%;
|
||||
margin: 6px 0;
|
||||
padding: 0 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 100%; // ✅ 限制最大宽度
|
||||
overflow-x: hidden; // ✅ 防止横向撑开
|
||||
|
||||
text {
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,65 +1,62 @@
|
||||
<template>
|
||||
<view class="input-area-wrapper">
|
||||
<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-else src="/static/input_keyboard_icon.png"></image>
|
||||
</view>
|
||||
<view class="input-area-wrapper">
|
||||
<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-else src="/static/input_keyboard_icon.png"></image>
|
||||
</view>
|
||||
|
||||
<!-- 输入框/语音按钮容器 -->
|
||||
<view class="input-button-container">
|
||||
<textarea
|
||||
ref="textareaRef"
|
||||
v-if="!isVoiceMode"
|
||||
class="textarea"
|
||||
type="text"
|
||||
cursor-spacing="20"
|
||||
confirm-type="send"
|
||||
v-model="inputMessage"
|
||||
auto-height
|
||||
:confirm-hold="true"
|
||||
:placeholder="placeholder"
|
||||
:show-confirm-bar="false"
|
||||
:hold-keyboard="holdKeyboard"
|
||||
:adjust-position="true"
|
||||
:disable-default-padding="true"
|
||||
maxlength="300"
|
||||
@confirm="sendMessage"
|
||||
@focus="handleFocus"
|
||||
@blur="handleBlur"
|
||||
@touchstart="handleTouchStart"
|
||||
@touchend="handleTouchEnd"
|
||||
/>
|
||||
<!-- 输入框/语音按钮容器 -->
|
||||
<view class="input-button-container">
|
||||
<textarea
|
||||
ref="textareaRef"
|
||||
v-if="!isVoiceMode"
|
||||
class="textarea"
|
||||
type="text"
|
||||
cursor-spacing="20"
|
||||
confirm-type="send"
|
||||
v-model="inputMessage"
|
||||
auto-height
|
||||
:confirm-hold="true"
|
||||
:placeholder="placeholder"
|
||||
:show-confirm-bar="false"
|
||||
:hold-keyboard="holdKeyboard"
|
||||
:adjust-position="true"
|
||||
:disable-default-padding="true"
|
||||
maxlength="300"
|
||||
@confirm="sendMessage"
|
||||
@focus="handleFocus"
|
||||
@blur="handleBlur"
|
||||
@touchstart="handleTouchStart"
|
||||
@touchend="handleTouchEnd"
|
||||
/>
|
||||
|
||||
<view
|
||||
v-if="isVoiceMode"
|
||||
class="hold-to-talk-button"
|
||||
@longpress="handleVoiceTouchStart"
|
||||
@touchend="handleVoiceTouchEnd"
|
||||
@touchcancel="handleVoiceTouchEnd"
|
||||
>
|
||||
按住 说话
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="input-container-send">
|
||||
<view class="input-container-send-btn" @click="sendMessage">
|
||||
<image
|
||||
v-if="props.isSessionActive"
|
||||
src="/static/input_stop_icon.png"
|
||||
></image>
|
||||
<image v-else src="/static/input_send_icon.png"></image>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
v-if="isVoiceMode"
|
||||
class="hold-to-talk-button"
|
||||
@longpress="handleVoiceTouchStart"
|
||||
@touchend="handleVoiceTouchEnd"
|
||||
@touchcancel="handleVoiceTouchEnd"
|
||||
>
|
||||
按住 说话
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 录音按钮 -->
|
||||
<RecordingWaveBtn v-if="visibleWaveBtn" ref="recordingWaveBtnRef" />
|
||||
<view class="input-container-send">
|
||||
<view class="input-container-send-btn" @click="sendMessage">
|
||||
<image
|
||||
v-if="props.isSessionActive"
|
||||
src="/static/input_stop_icon.png"
|
||||
></image>
|
||||
<image v-else src="/static/input_send_icon.png"></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 录音按钮 -->
|
||||
<RecordingWaveBtn v-if="visibleWaveBtn" ref="recordingWaveBtnRef" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@ -70,18 +67,18 @@ const plugin = requirePlugin("WechatSI");
|
||||
const manager = plugin.getRecordRecognitionManager();
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: String,
|
||||
holdKeyboard: Boolean,
|
||||
isSessionActive: Boolean,
|
||||
stopRequest: Function,
|
||||
modelValue: String,
|
||||
holdKeyboard: Boolean,
|
||||
isSessionActive: Boolean,
|
||||
stopRequest: Function,
|
||||
});
|
||||
const emit = defineEmits([
|
||||
"update:modelValue",
|
||||
"send",
|
||||
"noHideKeyboard",
|
||||
"keyboardShow",
|
||||
"keyboardHide",
|
||||
"sendVoice",
|
||||
"update:modelValue",
|
||||
"send",
|
||||
"noHideKeyboard",
|
||||
"keyboardShow",
|
||||
"keyboardHide",
|
||||
"sendVoice",
|
||||
]);
|
||||
|
||||
const textareaRef = ref(null);
|
||||
@ -95,137 +92,137 @@ const visibleWaveBtn = ref(false);
|
||||
|
||||
// 保持和父组件同步
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
inputMessage.value = val;
|
||||
}
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
inputMessage.value = val;
|
||||
}
|
||||
);
|
||||
|
||||
// 当子组件的 inputMessage 变化时,通知父组件(但要避免循环更新)
|
||||
watch(inputMessage, (val) => {
|
||||
// 只有当值真正不同时才emit,避免循环更新
|
||||
if (val !== props.modelValue) {
|
||||
emit("update:modelValue", val);
|
||||
}
|
||||
// 只有当值真正不同时才emit,避免循环更新
|
||||
if (val !== props.modelValue) {
|
||||
emit("update:modelValue", val);
|
||||
}
|
||||
});
|
||||
|
||||
// 切换语音/文本模式
|
||||
const toggleVoiceMode = () => {
|
||||
isVoiceMode.value = !isVoiceMode.value;
|
||||
isVoiceMode.value = !isVoiceMode.value;
|
||||
};
|
||||
|
||||
// 处理语音按钮长按开始
|
||||
const handleVoiceTouchStart = () => {
|
||||
manager.start({ lang: "zh_CN" });
|
||||
manager.start({ lang: "zh_CN" });
|
||||
|
||||
visibleWaveBtn.value = true;
|
||||
visibleWaveBtn.value = true;
|
||||
|
||||
// 启动音频条动画
|
||||
nextTick(() => {
|
||||
if (recordingWaveBtnRef.value) {
|
||||
recordingWaveBtnRef.value.startAnimation();
|
||||
}
|
||||
});
|
||||
// 启动音频条动画
|
||||
nextTick(() => {
|
||||
if (recordingWaveBtnRef.value) {
|
||||
recordingWaveBtnRef.value.startAnimation();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 处理语音按钮长按结束
|
||||
const handleVoiceTouchEnd = () => {
|
||||
manager.stop();
|
||||
manager.stop();
|
||||
|
||||
// 停止音频条动画
|
||||
if (recordingWaveBtnRef.value) {
|
||||
recordingWaveBtnRef.value.stopAnimation();
|
||||
}
|
||||
// 停止音频条动画
|
||||
if (recordingWaveBtnRef.value) {
|
||||
recordingWaveBtnRef.value.stopAnimation();
|
||||
}
|
||||
|
||||
visibleWaveBtn.value = false;
|
||||
visibleWaveBtn.value = false;
|
||||
};
|
||||
|
||||
// 处理发送原语音
|
||||
const initRecord = () => {
|
||||
manager.onRecognize = (res) => {
|
||||
let text = res.result;
|
||||
inputMessage.value = text;
|
||||
};
|
||||
// 识别结束事件
|
||||
manager.onStop = (res) => {
|
||||
console.log(res, 37);
|
||||
let text = res.result;
|
||||
manager.onRecognize = (res) => {
|
||||
let text = res.result;
|
||||
inputMessage.value = text;
|
||||
};
|
||||
// 识别结束事件
|
||||
manager.onStop = (res) => {
|
||||
console.log(res, 37);
|
||||
let text = res.result;
|
||||
|
||||
if (text == "") {
|
||||
console.log("没有说话");
|
||||
return;
|
||||
}
|
||||
if (text == "") {
|
||||
console.log("没有说话");
|
||||
return;
|
||||
}
|
||||
|
||||
inputMessage.value = text;
|
||||
// 在语音识别完成后发送消息
|
||||
emit("send", text);
|
||||
};
|
||||
inputMessage.value = text;
|
||||
// 在语音识别完成后发送消息
|
||||
emit("send", text);
|
||||
};
|
||||
};
|
||||
|
||||
// 监听键盘高度变化
|
||||
onMounted(() => {
|
||||
// 监听键盘弹起
|
||||
uni.onKeyboardHeightChange((res) => {
|
||||
keyboardHeight.value = res.height;
|
||||
if (res.height) {
|
||||
emit("keyboardShow", res.height);
|
||||
} else {
|
||||
emit("keyboardHide");
|
||||
}
|
||||
});
|
||||
// 监听键盘弹起
|
||||
uni.onKeyboardHeightChange((res) => {
|
||||
keyboardHeight.value = res.height;
|
||||
if (res.height) {
|
||||
emit("keyboardShow", res.height);
|
||||
} else {
|
||||
emit("keyboardHide");
|
||||
}
|
||||
});
|
||||
|
||||
initRecord();
|
||||
initRecord();
|
||||
});
|
||||
|
||||
const sendMessage = () => {
|
||||
if (props.isSessionActive) {
|
||||
// 如果会话进行中,调用停止请求函数
|
||||
if (props.stopRequest) {
|
||||
props.stopRequest();
|
||||
}
|
||||
} else {
|
||||
// 否则发送新消息
|
||||
if (!inputMessage.value.trim()) return;
|
||||
emit("send", inputMessage.value);
|
||||
|
||||
// 发送后保持焦点(可选)
|
||||
if (props.holdKeyboard && textareaRef.value) {
|
||||
nextTick(() => {
|
||||
textareaRef.value.focus();
|
||||
});
|
||||
}
|
||||
if (props.isSessionActive) {
|
||||
// 如果会话进行中,调用停止请求函数
|
||||
if (props.stopRequest) {
|
||||
props.stopRequest();
|
||||
}
|
||||
} else {
|
||||
// 否则发送新消息
|
||||
if (!inputMessage.value.trim()) return;
|
||||
emit("send", inputMessage.value);
|
||||
|
||||
// 发送后保持焦点(可选)
|
||||
if (props.holdKeyboard && textareaRef.value) {
|
||||
nextTick(() => {
|
||||
textareaRef.value.focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleFocus = () => {
|
||||
isFocused.value = true;
|
||||
emit("noHideKeyboard");
|
||||
isFocused.value = true;
|
||||
emit("noHideKeyboard");
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
isFocused.value = false;
|
||||
isFocused.value = false;
|
||||
};
|
||||
|
||||
const handleTouchStart = () => {
|
||||
emit("noHideKeyboard");
|
||||
emit("noHideKeyboard");
|
||||
};
|
||||
|
||||
const handleTouchEnd = () => {
|
||||
emit("noHideKeyboard");
|
||||
emit("noHideKeyboard");
|
||||
};
|
||||
|
||||
// 手动聚焦输入框
|
||||
const focusInput = () => {
|
||||
if (textareaRef.value) {
|
||||
textareaRef.value.focus();
|
||||
}
|
||||
if (textareaRef.value) {
|
||||
textareaRef.value.focus();
|
||||
}
|
||||
};
|
||||
|
||||
// 手动失焦输入框
|
||||
const blurInput = () => {
|
||||
if (textareaRef.value) {
|
||||
textareaRef.value.blur();
|
||||
}
|
||||
if (textareaRef.value) {
|
||||
textareaRef.value.blur();
|
||||
}
|
||||
};
|
||||
|
||||
// 暴露方法给父组件
|
||||
@ -234,87 +231,87 @@ defineExpose({ focusInput });
|
||||
|
||||
<style scoped lang="scss">
|
||||
.area-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 22px;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0px 0px 20px 0px rgba(52, 25, 204, 0.05);
|
||||
margin: 0 12px;
|
||||
margin-bottom: 8px;
|
||||
|
||||
.input-container-voice {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 22px;
|
||||
justify-content: center;
|
||||
align-self: flex-end;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
|
||||
image {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.input-button-container {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.hold-to-talk-button {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
color: #333333;
|
||||
font-size: 16px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0px 0px 20px 0px rgba(52, 25, 204, 0.05);
|
||||
margin: 0 12px;
|
||||
margin-bottom: 8px;
|
||||
transition: all 0.2s ease;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.input-container-voice {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
align-self: flex-end;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
.input-container-send {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
align-self: flex-end;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
|
||||
image {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
.input-container-send-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.input-button-container {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
image {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
.textarea {
|
||||
flex: 1;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
max-height: 92px;
|
||||
min-height: 22px;
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
margin: 6px 0;
|
||||
|
||||
&::placeholder {
|
||||
color: #cccccc;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.hold-to-talk-button {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
color: #333333;
|
||||
font-size: 16px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #ffffff;
|
||||
transition: all 0.2s ease;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.input-container-send {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
align-self: flex-end;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
|
||||
.input-container-send-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
image {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
.textarea {
|
||||
flex: 1;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
max-height: 92px;
|
||||
min-height: 22px;
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
margin: 6px 0;
|
||||
|
||||
&::placeholder {
|
||||
color: #cccccc;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,21 +1,18 @@
|
||||
<template>
|
||||
<view>
|
||||
<zero-markdown-view :markdown="text" :aiMode='true'></zero-markdown-view>
|
||||
</view>
|
||||
<view>
|
||||
<zero-markdown-view :markdown="text" :aiMode="true"></zero-markdown-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps } from "vue";
|
||||
|
||||
defineProps({
|
||||
text: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
|
||||
import { defineProps } from "vue";
|
||||
|
||||
defineProps({
|
||||
text: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
||||
@ -1,27 +1,25 @@
|
||||
<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-title">
|
||||
<image :src="item.icon"></image>
|
||||
<text>{{ item.title }}</text>
|
||||
</view>
|
||||
<text class="quick-access-item-content">{{
|
||||
item.content
|
||||
}}</text>
|
||||
</view>
|
||||
<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-title">
|
||||
<image :src="item.icon"></image>
|
||||
<text>{{ item.title }}</text>
|
||||
</view>
|
||||
<text class="quick-access-item-content">{{ item.content }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@ -31,113 +29,113 @@ const itemList = ref([]);
|
||||
const emits = defineEmits(["replySent"]);
|
||||
|
||||
const sendReply = (item) => {
|
||||
emits("replySent", item); // 向父组件传递数据
|
||||
emits("replySent", item); // 向父组件传递数据
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
initData();
|
||||
initData();
|
||||
});
|
||||
|
||||
const initData = () => {
|
||||
itemList.value = [
|
||||
{
|
||||
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_order.png",
|
||||
title: "订单/工单",
|
||||
content: "我的订单/工单",
|
||||
type: "MyOrder",
|
||||
},
|
||||
{
|
||||
icon: "/static/quick/quick_icon_call.png",
|
||||
title: "呼叫服务",
|
||||
content: "加水、客房服务等",
|
||||
type: "Command.createWorkOrder",
|
||||
},
|
||||
];
|
||||
itemList.value = [
|
||||
{
|
||||
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_order.png",
|
||||
title: "订单/工单",
|
||||
content: "我的订单/工单",
|
||||
type: "MyOrder",
|
||||
},
|
||||
{
|
||||
icon: "/static/quick/quick_icon_call.png",
|
||||
title: "呼叫服务",
|
||||
content: "加水、客房服务等",
|
||||
type: "Command.createWorkOrder",
|
||||
},
|
||||
];
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.quick-access {
|
||||
width: 100%;
|
||||
width: 100%;
|
||||
|
||||
&-scroll {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
&-scroll {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.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);
|
||||
padding: 12px;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
|
||||
position: relative;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.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);
|
||||
padding: 12px;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
|
||||
position: relative;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.quick-access-item-bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 0;
|
||||
border-radius: 8px;
|
||||
width: 128px;
|
||||
height: 56px;
|
||||
}
|
||||
|
||||
.quick-access-item-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
z-index: 1;
|
||||
|
||||
image {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
text {
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
color: #201f32;
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.quick-access-item-content {
|
||||
z-index: 1;
|
||||
margin-top: 4px;
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 10px;
|
||||
color: #678cad;
|
||||
line-height: 18px;
|
||||
}
|
||||
&:last-child {
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.quick-access-item-bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 0;
|
||||
border-radius: 8px;
|
||||
width: 128px;
|
||||
height: 56px;
|
||||
}
|
||||
|
||||
.quick-access-item-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
z-index: 1;
|
||||
|
||||
image {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
text {
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
color: #201f32;
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.quick-access-item-content {
|
||||
z-index: 1;
|
||||
margin-top: 4px;
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 10px;
|
||||
color: #678cad;
|
||||
line-height: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,20 +1,18 @@
|
||||
<template>
|
||||
<view class="top-bg">
|
||||
</view>
|
||||
<view class="top-bg"> </view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
</script>
|
||||
<script></script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.top-bg {
|
||||
width: 100%;
|
||||
height: 270px;
|
||||
overflow: hidden;
|
||||
background: linear-gradient( 180deg, #42ADF9 0%, #6CD1FF 51%, #E9F3F7 99%);
|
||||
.top-bg-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
.top-bg {
|
||||
width: 100%;
|
||||
height: 270px;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(180deg, #42adf9 0%, #6cd1ff 51%, #e9f3f7 99%);
|
||||
.top-bg-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,48 +1,46 @@
|
||||
<template>
|
||||
<view class="nav-bar">
|
||||
<view class="nav-item" @click="showDrawer('showLeft')">
|
||||
<uni-icons type="bars" size="24" color="#333"></uni-icons>
|
||||
</view>
|
||||
|
||||
<uni-drawer ref="showLeft" mode="left" :width="320">
|
||||
<DrawerHome @closeDrawer="closeDrawer('showLeft')" />
|
||||
</uni-drawer>
|
||||
|
||||
</view>
|
||||
<view class="nav-bar">
|
||||
<view class="nav-item" @click="showDrawer('showLeft')">
|
||||
<uni-icons type="bars" size="24" color="#333"></uni-icons>
|
||||
</view>
|
||||
|
||||
<uni-drawer ref="showLeft" mode="left" :width="320">
|
||||
<DrawerHome @closeDrawer="closeDrawer('showLeft')" />
|
||||
</uni-drawer>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineEmits, ref } from 'vue'
|
||||
import DrawerHome from "@/pages/drawer/DrawerHome.vue";
|
||||
|
||||
const showLeft = ref(false)
|
||||
|
||||
// 打开窗口
|
||||
const showDrawer = (e) => {
|
||||
showLeft.value.open()
|
||||
}
|
||||
// 关闭窗口
|
||||
const closeDrawer = (e) => {
|
||||
showLeft.value.close()
|
||||
}
|
||||
|
||||
import { defineEmits, ref } from "vue";
|
||||
import DrawerHome from "@/pages/drawer/DrawerHome.vue";
|
||||
|
||||
const showLeft = ref(false);
|
||||
|
||||
// 打开窗口
|
||||
const showDrawer = (e) => {
|
||||
showLeft.value.open();
|
||||
};
|
||||
// 关闭窗口
|
||||
const closeDrawer = (e) => {
|
||||
showLeft.value.close();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 44px;
|
||||
padding: 0 15px;
|
||||
|
||||
.nav-item {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.nav-item-icon {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 44px;
|
||||
padding: 0 15px;
|
||||
|
||||
.nav-item {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.nav-item-icon {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,18 +1,15 @@
|
||||
<template>
|
||||
<view class="top-bg-content">
|
||||
<view class="top-item">
|
||||
<!-- :style="backgroundStyle" -->
|
||||
<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" />
|
||||
<view class="top-bg-content">
|
||||
<view class="top-item">
|
||||
<!-- :style="backgroundStyle" -->
|
||||
<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" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@ -20,56 +17,56 @@ import { defineProps, computed, ref } from "vue";
|
||||
import ChatCardAI from "./ChatCardAI.vue";
|
||||
|
||||
const props = defineProps({
|
||||
initPageImages: {
|
||||
type: Object,
|
||||
default: {
|
||||
backgroundImageUrl: "",
|
||||
logoImageUrl: "",
|
||||
welcomeImageUrl: "",
|
||||
},
|
||||
},
|
||||
welcomeContent: {
|
||||
type: String,
|
||||
default:
|
||||
"查信息、预定下单、探索玩法、呼叫服务、我通通可以满足,快试试问我问题吧!",
|
||||
initPageImages: {
|
||||
type: Object,
|
||||
default: {
|
||||
backgroundImageUrl: "",
|
||||
logoImageUrl: "",
|
||||
welcomeImageUrl: "",
|
||||
},
|
||||
},
|
||||
welcomeContent: {
|
||||
type: String,
|
||||
default:
|
||||
"查信息、预定下单、探索玩法、呼叫服务、我通通可以满足,快试试问我问题吧!",
|
||||
},
|
||||
});
|
||||
|
||||
// 计算背景样式
|
||||
const backgroundStyle = computed(() => {
|
||||
return {
|
||||
backgroundImage: `url(${props.initPageImages.backgroundImageUrl})`,
|
||||
backgroundSize: "cover",
|
||||
backgroundPosition: "center",
|
||||
backgroundRepeat: "no-repeat",
|
||||
};
|
||||
return {
|
||||
backgroundImage: `url(${props.initPageImages.backgroundImageUrl})`,
|
||||
backgroundSize: "cover",
|
||||
backgroundPosition: "center",
|
||||
backgroundRepeat: "no-repeat",
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.top-bg-content {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.top-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 32px;
|
||||
margin-bottom: -6px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 32px;
|
||||
margin-bottom: -6px;
|
||||
}
|
||||
|
||||
.top-item-left {
|
||||
width: 118px;
|
||||
height: 52px;
|
||||
width: 118px;
|
||||
height: 52px;
|
||||
}
|
||||
|
||||
.top-item-right {
|
||||
width: 130px;
|
||||
height: 130px;
|
||||
width: 130px;
|
||||
height: 130px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,153 +1,155 @@
|
||||
.chat-container {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: #E9F3F7;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden !important;
|
||||
position: relative;
|
||||
/* 确保在键盘弹起时布局正确 */
|
||||
box-sizing: border-box;
|
||||
|
||||
.chat-container-bg {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 0;
|
||||
height: 270px;
|
||||
background: linear-gradient( 180deg, #42ADF9 0%, #6CD1FF 51%, #E9F3F7 99%);
|
||||
}
|
||||
|
||||
.chat-content {
|
||||
width: 100vw;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.chat-container-top-bannar {
|
||||
width: 100vw;
|
||||
flex-shrink: 0;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.area-msg-list {
|
||||
width: 100vw;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
min-height: 0;
|
||||
height: 0;
|
||||
padding: 4px 0 0;
|
||||
overscroll-behavior: contain; /* 阻止滚动穿透 */
|
||||
-webkit-overflow-scrolling: touch;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.message-item-ai {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.message-item-mine {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.message-item-other {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer-area {
|
||||
width: 100vw;
|
||||
flex-shrink: 0;
|
||||
padding: 4px 0 20px 0; /* 直接设置20px底部安全距离 */
|
||||
background-color: #E9F3F7;
|
||||
touch-action: pan-x;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
min-height: fit-content;
|
||||
/* 安卓键盘适配 - 使用相对定位配合adjustPan */
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
transition: padding-bottom 0.3s ease;
|
||||
/* 确保输入区域始终可见 */
|
||||
// transform: translateZ(0);
|
||||
// -webkit-transform: translateZ(0);
|
||||
}
|
||||
|
||||
.area-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 22px;
|
||||
background-color: #FFFFFF;
|
||||
box-shadow: 0px 0px 20px 0px rgba(52,25,204,0.05);
|
||||
margin: 0 12px;
|
||||
|
||||
.input-container-voice {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
flex-shrink: 0;
|
||||
align-self: flex-end;
|
||||
|
||||
image {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.input-container-send {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
flex-shrink: 0;
|
||||
align-self: flex-end;
|
||||
|
||||
image {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
.textarea {
|
||||
flex: 1;
|
||||
max-height: 92px;
|
||||
min-height: 22px;
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
margin-bottom: 2px;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: #e9f3f7;
|
||||
|
||||
// 打字机光标闪烁动画
|
||||
.typing-cursor {
|
||||
display: inline-block;
|
||||
color: #42ADF9;
|
||||
font-weight: bold;
|
||||
font-size: 1.2em;
|
||||
animation: blink 1s infinite;
|
||||
margin-left: 2px;
|
||||
}
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden !important;
|
||||
position: relative;
|
||||
/* 确保在键盘弹起时布局正确 */
|
||||
box-sizing: border-box;
|
||||
|
||||
@keyframes blink {
|
||||
0%, 50% {
|
||||
opacity: 1;
|
||||
}
|
||||
51%, 100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
.chat-container-bg {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 0;
|
||||
height: 270px;
|
||||
background: linear-gradient(180deg, #42adf9 0%, #6cd1ff 51%, #e9f3f7 99%);
|
||||
}
|
||||
|
||||
.chat-content {
|
||||
width: 100vw;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.chat-container-top-bannar {
|
||||
width: 100vw;
|
||||
flex-shrink: 0;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.area-msg-list {
|
||||
width: 100vw;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
min-height: 0;
|
||||
height: 0;
|
||||
padding: 4px 0 0;
|
||||
overscroll-behavior: contain; /* 阻止滚动穿透 */
|
||||
-webkit-overflow-scrolling: touch;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.message-item-ai {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.message-item-mine {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.message-item-other {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer-area {
|
||||
width: 100vw;
|
||||
flex-shrink: 0;
|
||||
padding: 4px 0 20px 0; /* 直接设置20px底部安全距离 */
|
||||
background-color: #e9f3f7;
|
||||
touch-action: pan-x;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
min-height: fit-content;
|
||||
/* 安卓键盘适配 - 使用相对定位配合adjustPan */
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
transition: padding-bottom 0.3s ease;
|
||||
/* 确保输入区域始终可见 */
|
||||
// transform: translateZ(0);
|
||||
// -webkit-transform: translateZ(0);
|
||||
}
|
||||
|
||||
.area-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 22px;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0px 0px 20px 0px rgba(52, 25, 204, 0.05);
|
||||
margin: 0 12px;
|
||||
|
||||
.input-container-voice {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
flex-shrink: 0;
|
||||
align-self: flex-end;
|
||||
|
||||
image {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.input-container-send {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
flex-shrink: 0;
|
||||
align-self: flex-end;
|
||||
|
||||
image {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
.textarea {
|
||||
flex: 1;
|
||||
max-height: 92px;
|
||||
min-height: 22px;
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
margin-bottom: 2px;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
// 打字机光标闪烁动画
|
||||
.typing-cursor {
|
||||
display: inline-block;
|
||||
color: #42adf9;
|
||||
font-weight: bold;
|
||||
font-size: 1.2em;
|
||||
animation: blink 1s infinite;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
0%,
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
51%,
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,50 +1,54 @@
|
||||
<template>
|
||||
<view class="drawer-home">
|
||||
<view class="drawer-home-nav">
|
||||
<uni-icons type="closeempty" size="22" color="#333333" class="close-icon" @click="closeDrawer"></uni-icons>
|
||||
<text class="title">我的</text>
|
||||
</view>
|
||||
|
||||
<MineSetting/>
|
||||
|
||||
</view>
|
||||
<view class="drawer-home">
|
||||
<view class="drawer-home-nav">
|
||||
<uni-icons
|
||||
type="closeempty"
|
||||
size="22"
|
||||
color="#333333"
|
||||
class="close-icon"
|
||||
@click="closeDrawer"
|
||||
></uni-icons>
|
||||
<text class="title">我的</text>
|
||||
</view>
|
||||
|
||||
<MineSetting />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import MineSetting from './MineSetting.vue'
|
||||
import { defineEmits } from 'vue'
|
||||
const emits = defineEmits(['closeDrawer'])
|
||||
import MineSetting from "./MineSetting.vue";
|
||||
import { defineEmits } from "vue";
|
||||
const emits = defineEmits(["closeDrawer"]);
|
||||
|
||||
const closeDrawer = () => {
|
||||
emits('closeDrawer')
|
||||
}
|
||||
|
||||
const closeDrawer = () => {
|
||||
emits("closeDrawer");
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.drawer-home {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
background-color: #fff;
|
||||
padding-top: 44px;
|
||||
}
|
||||
|
||||
.drawer-home-nav {
|
||||
position: relative;
|
||||
padding: 12px;
|
||||
display: flex;
|
||||
justify-content: center; /* 文字水平居中 */
|
||||
align-items: center; /* 垂直居中 */
|
||||
|
||||
.title {
|
||||
font-size: 18px;
|
||||
text-align: center;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
position: absolute;
|
||||
left: 12px; /* 距离左边12px */
|
||||
}
|
||||
}
|
||||
</style>
|
||||
.drawer-home {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
background-color: #fff;
|
||||
padding-top: 44px;
|
||||
}
|
||||
|
||||
.drawer-home-nav {
|
||||
position: relative;
|
||||
padding: 12px;
|
||||
display: flex;
|
||||
justify-content: center; /* 文字水平居中 */
|
||||
align-items: center; /* 垂直居中 */
|
||||
|
||||
.title {
|
||||
font-size: 18px;
|
||||
text-align: center;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
position: absolute;
|
||||
left: 12px; /* 距离左边12px */
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -19,10 +19,10 @@
|
||||
|
||||
<!-- 中间功能入口(循环渲染) -->
|
||||
<view class="menu-card">
|
||||
<view
|
||||
class="menu-item"
|
||||
v-for="(item, index) in menuList"
|
||||
:key="index"
|
||||
<view
|
||||
class="menu-item"
|
||||
v-for="(item, index) in menuList"
|
||||
:key="index"
|
||||
@click="handleMenuClick(item)"
|
||||
>
|
||||
<text class="label">{{ item.label }}</text>
|
||||
@ -36,60 +36,64 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { getLoginUserPhone } from '@/request/api/LoginApi'
|
||||
import { ref, onMounted } from "vue";
|
||||
import { getLoginUserPhone } from "@/request/api/LoginApi";
|
||||
|
||||
// 假数据
|
||||
const userInfo = ref({
|
||||
avatar: '/static/default-avatar.png',
|
||||
nickname: '微信用户',
|
||||
phone: ''
|
||||
})
|
||||
avatar: "/static/default-avatar.png",
|
||||
nickname: "微信用户",
|
||||
phone: "",
|
||||
});
|
||||
|
||||
// 功能菜单列表(带 type 区分操作类型)
|
||||
const menuList = ref([
|
||||
// { label: '修改手机号', type: 'navigate', url: '/pages/change-phone/change-phone' },
|
||||
{ label: '账号注销', type: 'navigate', url: '/pages/cancel-account/cancel-account' },
|
||||
{
|
||||
label: "账号注销",
|
||||
type: "navigate",
|
||||
url: "/pages/cancel-account/cancel-account",
|
||||
},
|
||||
// { label: '营业资质&协议', type: 'navigate', url: '/pages/agreement/agreement' },
|
||||
{ label: '联系客服', type: 'action', action: 'contactService' }
|
||||
])
|
||||
{ label: "联系客服", type: "action", action: "contactService" },
|
||||
]);
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
getLoginUserPhoneInfo()
|
||||
})
|
||||
getLoginUserPhoneInfo();
|
||||
});
|
||||
|
||||
const getLoginUserPhoneInfo = async () => {
|
||||
const res = await getLoginUserPhone()
|
||||
const res = await getLoginUserPhone();
|
||||
if (res.code === 0) {
|
||||
userInfo.value.phone = res.data
|
||||
}
|
||||
}
|
||||
userInfo.value.phone = res.data;
|
||||
}
|
||||
};
|
||||
|
||||
// 处理菜单点击
|
||||
const handleMenuClick = (item) => {
|
||||
if (item.type === 'navigate' && item.url) {
|
||||
uni.navigateTo({ url: item.url })
|
||||
} else if (item.type === 'action') {
|
||||
if (item.action === 'contactService') {
|
||||
uni.showToast({ title: '联系客服功能待实现', icon: 'none' })
|
||||
if (item.type === "navigate" && item.url) {
|
||||
uni.navigateTo({ url: item.url });
|
||||
} else if (item.type === "action") {
|
||||
if (item.action === "contactService") {
|
||||
uni.showToast({ title: "联系客服功能待实现", icon: "none" });
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 退出登录
|
||||
const handleLogout = () => {
|
||||
uni.showModal({
|
||||
title: '温馨提示',
|
||||
content: '确定要退出登录吗?',
|
||||
title: "温馨提示",
|
||||
content: "确定要退出登录吗?",
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.clearStorageSync()
|
||||
uni.reLaunch({ url: '/pages/login/index' })
|
||||
uni.clearStorageSync();
|
||||
uni.reLaunch({ url: "/pages/login/index" });
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@ -12,32 +12,31 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, nextTick, defineEmits } from 'vue'
|
||||
import { onMounted } from 'vue'
|
||||
import { SCROLL_TO_BOTTOM } from '@/constant/constant'
|
||||
import { ref, nextTick, defineEmits } from "vue";
|
||||
import { onMounted } from "vue";
|
||||
import { SCROLL_TO_BOTTOM } from "@/constant/constant";
|
||||
const props = defineProps({
|
||||
question: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
default: "",
|
||||
},
|
||||
});
|
||||
|
||||
const tags = ref([])
|
||||
const emits = defineEmits(['replySent']);
|
||||
const tags = ref([]);
|
||||
const emits = defineEmits(["replySent"]);
|
||||
|
||||
const handleClick = (item) => {
|
||||
emits('replySent', item)
|
||||
}
|
||||
emits("replySent", item);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
tags.value = props.question.split(/[&|;]/).filter(tag => tag.trim() !== '')
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
uni.$emit(SCROLL_TO_BOTTOM, true)
|
||||
}, 300)
|
||||
});
|
||||
})
|
||||
|
||||
tags.value = props.question.split(/[&|;]/).filter((tag) => tag.trim() !== "");
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
uni.$emit(SCROLL_TO_BOTTOM, true);
|
||||
}, 300);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@ -59,7 +58,7 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.tag-text {
|
||||
color: #00A6FF; /* 蓝色文字,可根据设计调整 */
|
||||
color: #00a6ff; /* 蓝色文字,可根据设计调整 */
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,82 +1,82 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<swiper class="swiper" circular
|
||||
:indicator-dots="activityList.length > 1"
|
||||
indicator-color="#FFFFFF"
|
||||
indicator-active-color="#00A6FF"
|
||||
:autoplay="autoplay"
|
||||
:interval="interval"
|
||||
:duration="duration">
|
||||
<swiper-item v-for="item in activityList" :key="item.id">
|
||||
<view class="swiper-item" @click="handleClick(item)">
|
||||
<image :src="item.activityCover" mode="aspectFill"></image>
|
||||
<view class="corner-btn">快速预定</view>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
|
||||
</view>
|
||||
<view class="container">
|
||||
<swiper
|
||||
class="swiper"
|
||||
circular
|
||||
:indicator-dots="activityList.length > 1"
|
||||
indicator-color="#FFFFFF"
|
||||
indicator-active-color="#00A6FF"
|
||||
:autoplay="autoplay"
|
||||
:interval="interval"
|
||||
:duration="duration"
|
||||
>
|
||||
<swiper-item v-for="item in activityList" :key="item.id">
|
||||
<view class="swiper-item" @click="handleClick(item)">
|
||||
<image :src="item.activityCover" mode="aspectFill"></image>
|
||||
<view class="corner-btn">快速预定</view>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { SEND_COMMAND_TEXT } from '@/constant/constant'
|
||||
import { ref } from "vue";
|
||||
import { SEND_COMMAND_TEXT } from "@/constant/constant";
|
||||
|
||||
const autoplay = ref(true)
|
||||
const interval = ref(3000)
|
||||
const duration = ref(500)
|
||||
const autoplay = ref(true);
|
||||
const interval = ref(3000);
|
||||
const duration = ref(500);
|
||||
|
||||
const props = defineProps({
|
||||
activityList: {
|
||||
type: Array,
|
||||
default: []
|
||||
}
|
||||
})
|
||||
|
||||
const handleClick = (item) => {
|
||||
uni.$emit(SEND_COMMAND_TEXT, '快速预定')
|
||||
}
|
||||
const props = defineProps({
|
||||
activityList: {
|
||||
type: Array,
|
||||
default: [],
|
||||
},
|
||||
});
|
||||
|
||||
const handleClick = (item) => {
|
||||
uni.$emit(SEND_COMMAND_TEXT, "快速预定");
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.container {
|
||||
margin-bottom: 6px;
|
||||
|
||||
.uni-margin-wrap {
|
||||
width: 100%;
|
||||
}
|
||||
.swiper {
|
||||
height: 120px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.swiper-item {
|
||||
position: relative;
|
||||
display: block;
|
||||
height: 120px;
|
||||
line-height: 120px;
|
||||
text-align: center;
|
||||
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 8px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.corner-btn {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
bottom: 12px;
|
||||
background-color: #ffeb00;
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
.container {
|
||||
margin-bottom: 6px;
|
||||
|
||||
.uni-margin-wrap {
|
||||
width: 100%;
|
||||
}
|
||||
.swiper {
|
||||
height: 120px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.swiper-item {
|
||||
position: relative;
|
||||
display: block;
|
||||
height: 120px;
|
||||
line-height: 120px;
|
||||
text-align: center;
|
||||
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 8px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.corner-btn {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
bottom: 12px;
|
||||
background-color: #ffeb00;
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -22,7 +22,6 @@
|
||||
<text class="calendar-text">日历</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
|
||||
@ -71,4 +71,4 @@ onMounted(() => {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -1,36 +1,33 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<template v-if="toolCall.picture && toolCall.picture.length > 0">
|
||||
<ModuleTitle :title="图片详情" />
|
||||
<ImageSwiper :images="toolCall.picture" />
|
||||
</template>
|
||||
<view class="container">
|
||||
<template v-if="toolCall.picture && toolCall.picture.length > 0">
|
||||
<ModuleTitle :title="图片详情" />
|
||||
<ImageSwiper :images="toolCall.picture" />
|
||||
</template>
|
||||
|
||||
<template v-if="toolCall.commodityList">
|
||||
<DetailCardGoodsContentList :commodityList="toolCall.commodityList" />
|
||||
</template>
|
||||
|
||||
</view>
|
||||
<template v-if="toolCall.commodityList">
|
||||
<DetailCardGoodsContentList :commodityList="toolCall.commodityList" />
|
||||
</template>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps } from 'vue'
|
||||
import ModuleTitle from '@/components/ModuleTitle/index.vue'
|
||||
import ImageSwiper from '@/components/ImageSwiper/index.vue'
|
||||
import DetailCardGoodsContentList from './DetailCardGoodsContentList.vue'
|
||||
|
||||
const props = defineProps({
|
||||
toolCall: {
|
||||
type: Object,
|
||||
default: {}
|
||||
}
|
||||
})
|
||||
import { defineProps } from "vue";
|
||||
import ModuleTitle from "@/components/ModuleTitle/index.vue";
|
||||
import ImageSwiper from "@/components/ImageSwiper/index.vue";
|
||||
import DetailCardGoodsContentList from "./DetailCardGoodsContentList.vue";
|
||||
|
||||
const props = defineProps({
|
||||
toolCall: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.container {
|
||||
width: 100%;
|
||||
padding: 12px 0;
|
||||
width: 100%;
|
||||
padding: 12px 0;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@ -1,43 +1,42 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<view v-for="item in themeDTOList" :key="item.title">
|
||||
<RecommendPostsList :recommendTheme="item" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="container">
|
||||
<view v-for="item in themeDTOList" :key="item.title">
|
||||
<RecommendPostsList :recommendTheme="item" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, nextTick } from 'vue'
|
||||
import { onMounted } from 'vue'
|
||||
import { discoveryCradComponent } from '@/request/api/MainPageDataApi'
|
||||
import RecommendPostsList from '../recommend/RecommendPostsList.vue'
|
||||
import { SCROLL_TO_BOTTOM } from '@/constant/constant'
|
||||
import { ref, nextTick } from "vue";
|
||||
import { onMounted } from "vue";
|
||||
import { discoveryCradComponent } from "@/request/api/MainPageDataApi";
|
||||
import RecommendPostsList from "../recommend/RecommendPostsList.vue";
|
||||
import { SCROLL_TO_BOTTOM } from "@/constant/constant";
|
||||
|
||||
const themeDTOList = ref([])
|
||||
const themeDTOList = ref([]);
|
||||
|
||||
const loadDiscoveryCradComponent = async () => {
|
||||
const res = await discoveryCradComponent()
|
||||
if(res.code === 0 && res.data) {
|
||||
themeDTOList.value = res.data.themeDTOList
|
||||
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
uni.$emit(SCROLL_TO_BOTTOM, true)
|
||||
}, 300)
|
||||
});
|
||||
}
|
||||
}
|
||||
const loadDiscoveryCradComponent = async () => {
|
||||
const res = await discoveryCradComponent();
|
||||
if (res.code === 0 && res.data) {
|
||||
themeDTOList.value = res.data.themeDTOList;
|
||||
|
||||
onMounted(() => {
|
||||
loadDiscoveryCradComponent()
|
||||
})
|
||||
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
uni.$emit(SCROLL_TO_BOTTOM, true);
|
||||
}, 300);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadDiscoveryCradComponent();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.container {
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
</style>
|
||||
.container {
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<ModuleTitle :title="recommendTheme.themeName" />
|
||||
<view class="container-scroll">
|
||||
<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>
|
||||
</view>
|
||||
</view>
|
||||
<view class="container">
|
||||
<ModuleTitle :title="recommendTheme.themeName" />
|
||||
<view class="container-scroll">
|
||||
<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>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@ -20,48 +20,48 @@ 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: {},
|
||||
},
|
||||
recommendTheme: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
});
|
||||
|
||||
const sendReply = (item) => {
|
||||
const topic = item.userInputContent || item.topic.replace(/^#/, "");
|
||||
uni.$emit(RECOMMEND_POSTS_TITLE, topic);
|
||||
const topic = item.userInputContent || item.topic.replace(/^#/, "");
|
||||
uni.$emit(RECOMMEND_POSTS_TITLE, topic);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
.container-scroll {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
overflow-x: auto;
|
||||
margin-top: 4px;
|
||||
.container-scroll {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
overflow-x: auto;
|
||||
margin-top: 4px;
|
||||
|
||||
.mk-card-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
width: 188px;
|
||||
height: 154px;
|
||||
background-color: #ffffff;
|
||||
border-radius: 10px;
|
||||
margin-right: 8px;
|
||||
.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;
|
||||
}
|
||||
}
|
||||
image {
|
||||
width: 188px;
|
||||
height: 112px;
|
||||
}
|
||||
text {
|
||||
padding: 12px;
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,29 +1,27 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<view v-for="item in recommendThemeList" :key="item.themeName">
|
||||
<RecommendPostsList :recommendTheme="item" />
|
||||
</view>
|
||||
</view>
|
||||
<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: []
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
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>
|
||||
.container {
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user