Compare commits
No commits in common. "515ae4e78c55ead98bdb1348df93ea0765f7fb53" and "f57fbd29aa2e5dd57c2c1a8eb07ee11fd21a8e37" have entirely different histories.
515ae4e78c
...
f57fbd29aa
@ -2,11 +2,12 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
padding: 6px 2px 2px;
|
padding: 6px 2px 2px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.module-title {
|
.module-title {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-family: PingFang SC, PingFang SC;
|
font-family: DingTalk JinBuTi, DingTalk JinBuTi;
|
||||||
color: #000;
|
color: #000;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
@ -23,4 +24,4 @@
|
|||||||
|
|
||||||
.mb12 {
|
.mb12 {
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
@ -1,37 +1,29 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="container">
|
<view class="container">
|
||||||
<view class="chat-ai">
|
<view class="chat-ai">
|
||||||
<view class="loading-container">
|
<view class="loading-container" >
|
||||||
<image
|
<image v-if="isLoading" class="loading-img" src="/static/msg_loading.svg" />
|
||||||
v-if="isLoading"
|
<ChatMarkdown :key="textKey" :text="processedText" />
|
||||||
class="loading-img"
|
</view>
|
||||||
src="/static/msg_loading.svg"
|
<slot name="content"></slot>
|
||||||
/>
|
</view>
|
||||||
<!-- <loading v-if="isLoading" /> -->
|
<slot name="footer"></slot>
|
||||||
<ChatMarkdown :key="textKey" :text="processedText" />
|
|
||||||
<DotLoading v-if="isLoading" />
|
|
||||||
</view>
|
|
||||||
<slot name="content"></slot>
|
|
||||||
</view>
|
</view>
|
||||||
<slot name="footer"></slot>
|
|
||||||
</view>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps, computed, ref, watch } from "vue";
|
import { defineProps, computed, ref, watch } from "vue";
|
||||||
import ChatMarkdown from "./ChatMarkdown.vue";
|
import ChatMarkdown from "./ChatMarkdown.vue";
|
||||||
import loading from "@/pages/loading/loading.vue";
|
|
||||||
import DotLoading from "@/pages/loading/DotLoading.vue";
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
text: {
|
text: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "",
|
default: "",
|
||||||
},
|
},
|
||||||
isLoading: {
|
isLoading: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 用于强制重新渲染的key
|
// 用于强制重新渲染的key
|
||||||
@ -39,63 +31,63 @@ const textKey = ref(0);
|
|||||||
|
|
||||||
// 处理文本内容
|
// 处理文本内容
|
||||||
const processedText = computed(() => {
|
const processedText = computed(() => {
|
||||||
if (!props.text) {
|
if (!props.text) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确保文本是字符串类型
|
// 确保文本是字符串类型
|
||||||
const textStr = String(props.text);
|
const textStr = String(props.text);
|
||||||
|
|
||||||
|
// 处理加载状态的文本
|
||||||
|
if (textStr.includes("加载中") || textStr.includes("...")) {
|
||||||
|
return textStr;
|
||||||
|
}
|
||||||
|
|
||||||
// 处理加载状态的文本
|
|
||||||
if (textStr.includes("加载中") || textStr.includes("...")) {
|
|
||||||
return textStr;
|
return textStr;
|
||||||
}
|
|
||||||
|
|
||||||
return textStr;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 监听text变化,强制重新渲染
|
// 监听text变化,强制重新渲染
|
||||||
watch(
|
watch(
|
||||||
() => props.text,
|
() => props.text,
|
||||||
(newText, oldText) => {
|
(newText, oldText) => {
|
||||||
if (newText !== oldText) {
|
if (newText !== oldText) {
|
||||||
textKey.value++;
|
textKey.value++;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.container {
|
.container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
max-width: 100%; // ✅ 限制最大宽度
|
|
||||||
overflow-x: hidden; // ✅ 防止横向撑开
|
|
||||||
|
|
||||||
.chat-ai {
|
|
||||||
margin: 6px 12px;
|
|
||||||
padding: 0 12px;
|
|
||||||
max-width: 100%; // ✅ 限制最大宽度
|
max-width: 100%; // ✅ 限制最大宽度
|
||||||
min-width: 100px;
|
overflow-x: hidden; // ✅ 防止横向撑开
|
||||||
background: rgba(255, 255, 255, 0.4);
|
|
||||||
box-shadow: 2px 2px 6px 0px rgba(0, 0, 0, 0.1);
|
.chat-ai {
|
||||||
border-radius: 4px 20px 20px 20px;
|
margin: 6px 12px;
|
||||||
border: 1px solid;
|
padding: 0 12px;
|
||||||
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 {
|
.loading-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 4px 0;
|
padding: 4px 0;
|
||||||
max-width: 100%; // ✅ 限制最大宽度
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-img {
|
.loading-img {
|
||||||
margin-left: -4px;
|
margin-left: -4px;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,45 +1,46 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="chat-mine">
|
<view class="chat-mine">
|
||||||
<text>{{ text }}</text>
|
<text>{{ text }}</text>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps } from "vue";
|
import { defineProps } from "vue";
|
||||||
defineProps({
|
defineProps({
|
||||||
text: {
|
text: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "",
|
default: ''
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.chat-mine {
|
.chat-mine {
|
||||||
margin: 6px 12px;
|
margin: 6px 12px;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
|
|
||||||
background-color: #00a6ff;
|
background-color: #00A6FF;
|
||||||
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: 20px 4px 20px 20px;
|
border-radius: 20px 4px 20px 20px;
|
||||||
border: 1px solid;
|
border: 1px solid;
|
||||||
border-color: #ffffff;
|
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 {
|
text {
|
||||||
font-family: PingFang SC, PingFang SC;
|
font-family: PingFang SC, PingFang SC;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 15px;
|
font-size: 14px;
|
||||||
color: #ffffff;
|
color: #FFFFFF;
|
||||||
line-height: 22px;
|
line-height: 22px;
|
||||||
text-align: justify;
|
text-align: justify;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -1,35 +1,36 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="chat-other">
|
<view class="chat-other">
|
||||||
<text>{{ text }}</text>
|
<text>{{ text }}</text>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps } from "vue";
|
import { defineProps } from "vue";
|
||||||
defineProps({
|
defineProps({
|
||||||
text: {
|
text: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "",
|
default: ''
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.chat-other {
|
.chat-other {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 6px 0;
|
margin: 6px 0;
|
||||||
padding: 0 12px;
|
padding: 0 12px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
max-width: 100%; // ✅ 限制最大宽度
|
max-width: 100%; // ✅ 限制最大宽度
|
||||||
overflow-x: hidden; // ✅ 防止横向撑开
|
overflow-x: hidden; // ✅ 防止横向撑开
|
||||||
|
|
||||||
text {
|
text {
|
||||||
font-family: PingFang SC, PingFang SC;
|
font-family: PingFang SC, PingFang SC;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #333333;
|
color: #333333;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
}
|
||||||
|
</style>
|
||||||
@ -1,62 +1,65 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="input-area-wrapper">
|
<view class="input-area-wrapper">
|
||||||
<view v-if="!visibleWaveBtn" class="area-input">
|
<view v-if="!visibleWaveBtn" class="area-input">
|
||||||
<!-- 语音/键盘切换 -->
|
<!-- 语音/键盘切换 -->
|
||||||
<view class="input-container-voice" @click="toggleVoiceMode">
|
<view class="input-container-voice" @click="toggleVoiceMode">
|
||||||
<image v-if="!isVoiceMode" src="/static/input_voice_icon.png"></image>
|
<image
|
||||||
<image v-else src="/static/input_keyboard_icon.png"></image>
|
v-if="!isVoiceMode"
|
||||||
</view>
|
src="/static/input_voice_icon.png"
|
||||||
|
></image>
|
||||||
|
<image v-else src="/static/input_keyboard_icon.png"></image>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 输入框/语音按钮容器 -->
|
<!-- 输入框/语音按钮容器 -->
|
||||||
<view class="input-button-container">
|
<view class="input-button-container">
|
||||||
<textarea
|
<textarea
|
||||||
ref="textareaRef"
|
ref="textareaRef"
|
||||||
v-if="!isVoiceMode"
|
v-if="!isVoiceMode"
|
||||||
class="textarea"
|
class="textarea"
|
||||||
type="text"
|
type="text"
|
||||||
cursor-spacing="20"
|
cursor-spacing="20"
|
||||||
confirm-type="send"
|
confirm-type="send"
|
||||||
v-model="inputMessage"
|
v-model="inputMessage"
|
||||||
auto-height
|
auto-height
|
||||||
:confirm-hold="true"
|
:confirm-hold="true"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
:show-confirm-bar="false"
|
:show-confirm-bar="false"
|
||||||
:hold-keyboard="holdKeyboard"
|
:hold-keyboard="holdKeyboard"
|
||||||
:adjust-position="true"
|
:adjust-position="true"
|
||||||
:disable-default-padding="true"
|
:disable-default-padding="true"
|
||||||
maxlength="300"
|
maxlength="300"
|
||||||
@confirm="sendMessage"
|
@confirm="sendMessage"
|
||||||
@focus="handleFocus"
|
@focus="handleFocus"
|
||||||
@blur="handleBlur"
|
@blur="handleBlur"
|
||||||
@touchstart="handleTouchStart"
|
@touchstart="handleTouchStart"
|
||||||
@touchend="handleTouchEnd"
|
@touchend="handleTouchEnd"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<view
|
<view
|
||||||
v-if="isVoiceMode"
|
v-if="isVoiceMode"
|
||||||
class="hold-to-talk-button"
|
class="hold-to-talk-button"
|
||||||
@longpress="handleVoiceTouchStart"
|
@longpress="handleVoiceTouchStart"
|
||||||
@touchend="handleVoiceTouchEnd"
|
@touchend="handleVoiceTouchEnd"
|
||||||
@touchcancel="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>
|
</view>
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="input-container-send">
|
<!-- 录音按钮 -->
|
||||||
<view class="input-container-send-btn" @click="sendMessage">
|
<RecordingWaveBtn v-if="visibleWaveBtn" ref="recordingWaveBtnRef" />
|
||||||
<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>
|
</view>
|
||||||
|
|
||||||
<!-- 录音按钮 -->
|
|
||||||
<RecordingWaveBtn v-if="visibleWaveBtn" ref="recordingWaveBtnRef" />
|
|
||||||
</view>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@ -67,18 +70,18 @@ const plugin = requirePlugin("WechatSI");
|
|||||||
const manager = plugin.getRecordRecognitionManager();
|
const manager = plugin.getRecordRecognitionManager();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: String,
|
modelValue: String,
|
||||||
holdKeyboard: Boolean,
|
holdKeyboard: Boolean,
|
||||||
isSessionActive: Boolean,
|
isSessionActive: Boolean,
|
||||||
stopRequest: Function,
|
stopRequest: Function,
|
||||||
});
|
});
|
||||||
const emit = defineEmits([
|
const emit = defineEmits([
|
||||||
"update:modelValue",
|
"update:modelValue",
|
||||||
"send",
|
"send",
|
||||||
"noHideKeyboard",
|
"noHideKeyboard",
|
||||||
"keyboardShow",
|
"keyboardShow",
|
||||||
"keyboardHide",
|
"keyboardHide",
|
||||||
"sendVoice",
|
"sendVoice",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const textareaRef = ref(null);
|
const textareaRef = ref(null);
|
||||||
@ -92,137 +95,137 @@ const visibleWaveBtn = ref(false);
|
|||||||
|
|
||||||
// 保持和父组件同步
|
// 保持和父组件同步
|
||||||
watch(
|
watch(
|
||||||
() => props.modelValue,
|
() => props.modelValue,
|
||||||
(val) => {
|
(val) => {
|
||||||
inputMessage.value = val;
|
inputMessage.value = val;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// 当子组件的 inputMessage 变化时,通知父组件(但要避免循环更新)
|
// 当子组件的 inputMessage 变化时,通知父组件(但要避免循环更新)
|
||||||
watch(inputMessage, (val) => {
|
watch(inputMessage, (val) => {
|
||||||
// 只有当值真正不同时才emit,避免循环更新
|
// 只有当值真正不同时才emit,避免循环更新
|
||||||
if (val !== props.modelValue) {
|
if (val !== props.modelValue) {
|
||||||
emit("update:modelValue", val);
|
emit("update:modelValue", val);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 切换语音/文本模式
|
// 切换语音/文本模式
|
||||||
const toggleVoiceMode = () => {
|
const toggleVoiceMode = () => {
|
||||||
isVoiceMode.value = !isVoiceMode.value;
|
isVoiceMode.value = !isVoiceMode.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理语音按钮长按开始
|
// 处理语音按钮长按开始
|
||||||
const handleVoiceTouchStart = () => {
|
const handleVoiceTouchStart = () => {
|
||||||
manager.start({ lang: "zh_CN" });
|
manager.start({ lang: "zh_CN" });
|
||||||
|
|
||||||
visibleWaveBtn.value = true;
|
visibleWaveBtn.value = true;
|
||||||
|
|
||||||
// 启动音频条动画
|
// 启动音频条动画
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (recordingWaveBtnRef.value) {
|
if (recordingWaveBtnRef.value) {
|
||||||
recordingWaveBtnRef.value.startAnimation();
|
recordingWaveBtnRef.value.startAnimation();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理语音按钮长按结束
|
// 处理语音按钮长按结束
|
||||||
const handleVoiceTouchEnd = () => {
|
const handleVoiceTouchEnd = () => {
|
||||||
manager.stop();
|
manager.stop();
|
||||||
|
|
||||||
// 停止音频条动画
|
// 停止音频条动画
|
||||||
if (recordingWaveBtnRef.value) {
|
if (recordingWaveBtnRef.value) {
|
||||||
recordingWaveBtnRef.value.stopAnimation();
|
recordingWaveBtnRef.value.stopAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
visibleWaveBtn.value = false;
|
visibleWaveBtn.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理发送原语音
|
// 处理发送原语音
|
||||||
const initRecord = () => {
|
const initRecord = () => {
|
||||||
manager.onRecognize = (res) => {
|
manager.onRecognize = (res) => {
|
||||||
let text = res.result;
|
let text = res.result;
|
||||||
inputMessage.value = text;
|
inputMessage.value = text;
|
||||||
};
|
};
|
||||||
// 识别结束事件
|
// 识别结束事件
|
||||||
manager.onStop = (res) => {
|
manager.onStop = (res) => {
|
||||||
console.log(res, 37);
|
console.log(res, 37);
|
||||||
let text = res.result;
|
let text = res.result;
|
||||||
|
|
||||||
if (text == "") {
|
if (text == "") {
|
||||||
console.log("没有说话");
|
console.log("没有说话");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
inputMessage.value = text;
|
inputMessage.value = text;
|
||||||
// 在语音识别完成后发送消息
|
// 在语音识别完成后发送消息
|
||||||
emit("send", text);
|
emit("send", text);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// 监听键盘高度变化
|
// 监听键盘高度变化
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 监听键盘弹起
|
// 监听键盘弹起
|
||||||
uni.onKeyboardHeightChange((res) => {
|
uni.onKeyboardHeightChange((res) => {
|
||||||
keyboardHeight.value = res.height;
|
keyboardHeight.value = res.height;
|
||||||
if (res.height) {
|
if (res.height) {
|
||||||
emit("keyboardShow", res.height);
|
emit("keyboardShow", res.height);
|
||||||
} else {
|
} else {
|
||||||
emit("keyboardHide");
|
emit("keyboardHide");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
initRecord();
|
initRecord();
|
||||||
});
|
});
|
||||||
|
|
||||||
const sendMessage = () => {
|
const sendMessage = () => {
|
||||||
if (props.isSessionActive) {
|
if (props.isSessionActive) {
|
||||||
// 如果会话进行中,调用停止请求函数
|
// 如果会话进行中,调用停止请求函数
|
||||||
if (props.stopRequest) {
|
if (props.stopRequest) {
|
||||||
props.stopRequest();
|
props.stopRequest();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 否则发送新消息
|
// 否则发送新消息
|
||||||
if (!inputMessage.value.trim()) return;
|
if (!inputMessage.value.trim()) return;
|
||||||
emit("send", inputMessage.value);
|
emit("send", inputMessage.value);
|
||||||
|
|
||||||
// 发送后保持焦点(可选)
|
// 发送后保持焦点(可选)
|
||||||
if (props.holdKeyboard && textareaRef.value) {
|
if (props.holdKeyboard && textareaRef.value) {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
textareaRef.value.focus();
|
textareaRef.value.focus();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFocus = () => {
|
const handleFocus = () => {
|
||||||
isFocused.value = true;
|
isFocused.value = true;
|
||||||
emit("noHideKeyboard");
|
emit("noHideKeyboard");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBlur = () => {
|
const handleBlur = () => {
|
||||||
isFocused.value = false;
|
isFocused.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTouchStart = () => {
|
const handleTouchStart = () => {
|
||||||
emit("noHideKeyboard");
|
emit("noHideKeyboard");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTouchEnd = () => {
|
const handleTouchEnd = () => {
|
||||||
emit("noHideKeyboard");
|
emit("noHideKeyboard");
|
||||||
};
|
};
|
||||||
|
|
||||||
// 手动聚焦输入框
|
// 手动聚焦输入框
|
||||||
const focusInput = () => {
|
const focusInput = () => {
|
||||||
if (textareaRef.value) {
|
if (textareaRef.value) {
|
||||||
textareaRef.value.focus();
|
textareaRef.value.focus();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 手动失焦输入框
|
// 手动失焦输入框
|
||||||
const blurInput = () => {
|
const blurInput = () => {
|
||||||
if (textareaRef.value) {
|
if (textareaRef.value) {
|
||||||
textareaRef.value.blur();
|
textareaRef.value.blur();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 暴露方法给父组件
|
// 暴露方法给父组件
|
||||||
@ -231,87 +234,87 @@ defineExpose({ focusInput });
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.area-input {
|
.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;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
border-radius: 22px;
|
||||||
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;
|
background-color: #ffffff;
|
||||||
transition: all 0.2s ease;
|
box-shadow: 0px 0px 20px 0px rgba(52, 25, 204, 0.05);
|
||||||
user-select: none;
|
margin: 0 12px;
|
||||||
-webkit-user-select: none;
|
margin-bottom: 8px;
|
||||||
}
|
|
||||||
|
|
||||||
.input-container-send {
|
.input-container-voice {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
width: 44px;
|
width: 44px;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
|
|
||||||
.input-container-send-btn {
|
image {
|
||||||
display: flex;
|
width: 22px;
|
||||||
align-items: center;
|
height: 22px;
|
||||||
justify-content: center;
|
}
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
image {
|
.input-button-container {
|
||||||
width: 28px;
|
flex: 1;
|
||||||
height: 28px;
|
position: relative;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.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 {
|
.hold-to-talk-button {
|
||||||
outline: none;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,18 +1,21 @@
|
|||||||
<template>
|
<template>
|
||||||
<view>
|
<view>
|
||||||
<zero-markdown-view :markdown="text" :aiMode="true"></zero-markdown-view>
|
<zero-markdown-view :markdown="text" :aiMode='true'></zero-markdown-view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps } from "vue";
|
import { defineProps } from "vue";
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
text: {
|
text: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "",
|
default: ''
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@ -1,25 +1,27 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="quick-access">
|
<view class="quick-access">
|
||||||
<view class="quick-access-scroll">
|
<view class="quick-access-scroll">
|
||||||
<view
|
<view
|
||||||
class="quick-access-item"
|
class="quick-access-item"
|
||||||
v-for="(item, index) in itemList"
|
v-for="(item, index) in itemList"
|
||||||
:key="index"
|
:key="index"
|
||||||
@click="sendReply(item)"
|
@click="sendReply(item)"
|
||||||
>
|
>
|
||||||
<image
|
<image
|
||||||
class="quick-access-item-bg"
|
class="quick-access-item-bg"
|
||||||
src="/static/quick/quick_icon_bg.png"
|
src="/static/quick/quick_icon_bg.png"
|
||||||
mode="aspectFill"
|
mode="aspectFill"
|
||||||
></image>
|
></image>
|
||||||
<view class="quick-access-item-title">
|
<view class="quick-access-item-title">
|
||||||
<image :src="item.icon"></image>
|
<image :src="item.icon"></image>
|
||||||
<text>{{ item.title }}</text>
|
<text>{{ item.title }}</text>
|
||||||
|
</view>
|
||||||
|
<text class="quick-access-item-content">{{
|
||||||
|
item.content
|
||||||
|
}}</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<text class="quick-access-item-content">{{ item.content }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@ -29,113 +31,113 @@ const itemList = ref([]);
|
|||||||
const emits = defineEmits(["replySent"]);
|
const emits = defineEmits(["replySent"]);
|
||||||
|
|
||||||
const sendReply = (item) => {
|
const sendReply = (item) => {
|
||||||
emits("replySent", item); // 向父组件传递数据
|
emits("replySent", item); // 向父组件传递数据
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initData();
|
initData();
|
||||||
});
|
});
|
||||||
|
|
||||||
const initData = () => {
|
const initData = () => {
|
||||||
itemList.value = [
|
itemList.value = [
|
||||||
{
|
{
|
||||||
icon: "/static/quick/quick_icon_yuding.png",
|
icon: "/static/quick/quick_icon_yuding.png",
|
||||||
title: "快速预定",
|
title: "快速预定",
|
||||||
content: "预定门票、房间、餐食",
|
content: "预定门票、房间、餐食",
|
||||||
type: "Command.quickBooking",
|
type: "Command.quickBooking",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "/static/quick/quick_icon_find.png",
|
icon: "/static/quick/quick_icon_find.png",
|
||||||
title: "探索发现",
|
title: "探索发现",
|
||||||
content: "探索玩法、出片佳地",
|
content: "探索玩法、出片佳地",
|
||||||
type: "Command.discovery",
|
type: "Command.discovery",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "/static/quick/quick_icon_order.png",
|
icon: "/static/quick/quick_icon_order.png",
|
||||||
title: "订单/工单",
|
title: "订单/工单",
|
||||||
content: "我的订单/工单",
|
content: "我的订单/工单",
|
||||||
type: "MyOrder",
|
type: "MyOrder",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "/static/quick/quick_icon_call.png",
|
icon: "/static/quick/quick_icon_call.png",
|
||||||
title: "呼叫服务",
|
title: "呼叫服务",
|
||||||
content: "加水、客房服务等",
|
content: "加水、客房服务等",
|
||||||
type: "Command.createWorkOrder",
|
type: "Command.createWorkOrder",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.quick-access {
|
.quick-access {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
&-scroll {
|
&-scroll {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
-webkit-overflow-scrolling: touch;
|
-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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:last-child {
|
.quick-access-item {
|
||||||
margin-right: 12px;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.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>
|
</style>
|
||||||
|
|||||||
@ -1,18 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="top-bg"> </view>
|
<view class="top-bg">
|
||||||
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script></script>
|
<script>
|
||||||
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.top-bg {
|
.top-bg {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 270px;
|
height: 270px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: linear-gradient(180deg, #42adf9 0%, #6cd1ff 51%, #e9f3f7 99%);
|
background: linear-gradient( 180deg, #42ADF9 0%, #6CD1FF 51%, #E9F3F7 99%);
|
||||||
.top-bg-img {
|
.top-bg-img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -1,46 +1,48 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="nav-bar">
|
<view class="nav-bar">
|
||||||
<view class="nav-item" @click="showDrawer('showLeft')">
|
<view class="nav-item" @click="showDrawer('showLeft')">
|
||||||
<uni-icons type="bars" size="24" color="#333"></uni-icons>
|
<uni-icons type="bars" size="24" color="#333"></uni-icons>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<uni-drawer ref="showLeft" mode="left" :width="320">
|
<uni-drawer ref="showLeft" mode="left" :width="320">
|
||||||
<DrawerHome @closeDrawer="closeDrawer('showLeft')" />
|
<DrawerHome @closeDrawer="closeDrawer('showLeft')" />
|
||||||
</uni-drawer>
|
</uni-drawer>
|
||||||
</view>
|
|
||||||
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { defineEmits, ref } from "vue";
|
import { defineEmits, ref } from 'vue'
|
||||||
import DrawerHome from "@/pages/drawer/DrawerHome.vue";
|
import DrawerHome from "@/pages/drawer/DrawerHome.vue";
|
||||||
|
|
||||||
const showLeft = ref(false);
|
const showLeft = ref(false)
|
||||||
|
|
||||||
// 打开窗口
|
// 打开窗口
|
||||||
const showDrawer = (e) => {
|
const showDrawer = (e) => {
|
||||||
showLeft.value.open();
|
showLeft.value.open()
|
||||||
};
|
}
|
||||||
// 关闭窗口
|
// 关闭窗口
|
||||||
const closeDrawer = (e) => {
|
const closeDrawer = (e) => {
|
||||||
showLeft.value.close();
|
showLeft.value.close()
|
||||||
};
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.nav-bar {
|
.nav-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
padding: 0 15px;
|
padding: 0 15px;
|
||||||
|
|
||||||
.nav-item {
|
.nav-item {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
.nav-item-icon {
|
.nav-item-icon {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -1,15 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="top-bg-content">
|
<view class="top-bg-content">
|
||||||
<view class="top-item">
|
<view class="top-item">
|
||||||
<!-- :style="backgroundStyle" -->
|
<!-- :style="backgroundStyle" -->
|
||||||
<image
|
<image
|
||||||
class="top-item-left"
|
class="top-item-left"
|
||||||
:src="initPageImages.welcomeImageUrl"
|
:src="initPageImages.welcomeImageUrl"
|
||||||
></image>
|
></image>
|
||||||
<image class="top-item-right" :src="initPageImages.logoImageUrl"></image>
|
<image
|
||||||
|
class="top-item-right"
|
||||||
|
:src="initPageImages.logoImageUrl"
|
||||||
|
></image>
|
||||||
|
</view>
|
||||||
|
<ChatCardAI v-if="welcomeContent.length" :text="welcomeContent" />
|
||||||
</view>
|
</view>
|
||||||
<ChatCardAI v-if="welcomeContent.length" :text="welcomeContent" />
|
|
||||||
</view>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@ -17,56 +20,56 @@ import { defineProps, computed, ref } from "vue";
|
|||||||
import ChatCardAI from "./ChatCardAI.vue";
|
import ChatCardAI from "./ChatCardAI.vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
initPageImages: {
|
initPageImages: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: {
|
default: {
|
||||||
backgroundImageUrl: "",
|
backgroundImageUrl: "",
|
||||||
logoImageUrl: "",
|
logoImageUrl: "",
|
||||||
welcomeImageUrl: "",
|
welcomeImageUrl: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
welcomeContent: {
|
||||||
|
type: String,
|
||||||
|
default:
|
||||||
|
"查信息、预定下单、探索玩法、呼叫服务、我通通可以满足,快试试问我问题吧!",
|
||||||
},
|
},
|
||||||
},
|
|
||||||
welcomeContent: {
|
|
||||||
type: String,
|
|
||||||
default:
|
|
||||||
"查信息、预定下单、探索玩法、呼叫服务、我通通可以满足,快试试问我问题吧!",
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 计算背景样式
|
// 计算背景样式
|
||||||
const backgroundStyle = computed(() => {
|
const backgroundStyle = computed(() => {
|
||||||
return {
|
return {
|
||||||
backgroundImage: `url(${props.initPageImages.backgroundImageUrl})`,
|
backgroundImage: `url(${props.initPageImages.backgroundImageUrl})`,
|
||||||
backgroundSize: "cover",
|
backgroundSize: "cover",
|
||||||
backgroundPosition: "center",
|
backgroundPosition: "center",
|
||||||
backgroundRepeat: "no-repeat",
|
backgroundRepeat: "no-repeat",
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.top-bg-content {
|
.top-bg-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-item {
|
.top-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 32px;
|
padding: 0 32px;
|
||||||
margin-bottom: -6px;
|
margin-bottom: -6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-item-left {
|
.top-item-left {
|
||||||
width: 118px;
|
width: 118px;
|
||||||
height: 52px;
|
height: 52px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-item-right {
|
.top-item-right {
|
||||||
width: 130px;
|
width: 130px;
|
||||||
height: 130px;
|
height: 130px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,155 +1,153 @@
|
|||||||
.chat-container {
|
.chat-container {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
background-color: #e9f3f7;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
display: flex;
|
// 打字机光标闪烁动画
|
||||||
flex-direction: column;
|
.typing-cursor {
|
||||||
overflow: hidden !important;
|
display: inline-block;
|
||||||
position: relative;
|
color: #42ADF9;
|
||||||
/* 确保在键盘弹起时布局正确 */
|
font-weight: bold;
|
||||||
box-sizing: border-box;
|
font-size: 1.2em;
|
||||||
|
animation: blink 1s infinite;
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
.chat-container-bg {
|
@keyframes blink {
|
||||||
position: fixed;
|
0%, 50% {
|
||||||
top: 0;
|
opacity: 1;
|
||||||
left: 0;
|
}
|
||||||
right: 0;
|
51%, 100% {
|
||||||
z-index: 0;
|
opacity: 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,54 +1,50 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="drawer-home">
|
<view class="drawer-home">
|
||||||
<view class="drawer-home-nav">
|
<view class="drawer-home-nav">
|
||||||
<uni-icons
|
<uni-icons type="closeempty" size="22" color="#333333" class="close-icon" @click="closeDrawer"></uni-icons>
|
||||||
type="closeempty"
|
<text class="title">我的</text>
|
||||||
size="22"
|
</view>
|
||||||
color="#333333"
|
|
||||||
class="close-icon"
|
<MineSetting/>
|
||||||
@click="closeDrawer"
|
|
||||||
></uni-icons>
|
</view>
|
||||||
<text class="title">我的</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<MineSetting />
|
|
||||||
</view>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import MineSetting from "./MineSetting.vue";
|
import MineSetting from './MineSetting.vue'
|
||||||
import { defineEmits } from "vue";
|
import { defineEmits } from 'vue'
|
||||||
const emits = defineEmits(["closeDrawer"]);
|
const emits = defineEmits(['closeDrawer'])
|
||||||
|
|
||||||
const closeDrawer = () => {
|
const closeDrawer = () => {
|
||||||
emits("closeDrawer");
|
emits('closeDrawer')
|
||||||
};
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.drawer-home {
|
.drawer-home {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
padding-top: 44px;
|
padding-top: 44px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.drawer-home-nav {
|
.drawer-home-nav {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center; /* 文字水平居中 */
|
justify-content: center; /* 文字水平居中 */
|
||||||
align-items: center; /* 垂直居中 */
|
align-items: center; /* 垂直居中 */
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #333333;
|
color: #333333;
|
||||||
}
|
}
|
||||||
|
|
||||||
.close-icon {
|
.close-icon {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 12px; /* 距离左边12px */
|
left: 12px; /* 距离左边12px */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -19,10 +19,10 @@
|
|||||||
|
|
||||||
<!-- 中间功能入口(循环渲染) -->
|
<!-- 中间功能入口(循环渲染) -->
|
||||||
<view class="menu-card">
|
<view class="menu-card">
|
||||||
<view
|
<view
|
||||||
class="menu-item"
|
class="menu-item"
|
||||||
v-for="(item, index) in menuList"
|
v-for="(item, index) in menuList"
|
||||||
:key="index"
|
:key="index"
|
||||||
@click="handleMenuClick(item)"
|
@click="handleMenuClick(item)"
|
||||||
>
|
>
|
||||||
<text class="label">{{ item.label }}</text>
|
<text class="label">{{ item.label }}</text>
|
||||||
@ -36,64 +36,60 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from "vue";
|
import { ref, onMounted } from 'vue'
|
||||||
import { getLoginUserPhone } from "@/request/api/LoginApi";
|
import { getLoginUserPhone } from '@/request/api/LoginApi'
|
||||||
|
|
||||||
// 假数据
|
// 假数据
|
||||||
const userInfo = ref({
|
const userInfo = ref({
|
||||||
avatar: "/static/default-avatar.png",
|
avatar: '/static/default-avatar.png',
|
||||||
nickname: "微信用户",
|
nickname: '微信用户',
|
||||||
phone: "",
|
phone: ''
|
||||||
});
|
})
|
||||||
|
|
||||||
// 功能菜单列表(带 type 区分操作类型)
|
// 功能菜单列表(带 type 区分操作类型)
|
||||||
const menuList = ref([
|
const menuList = ref([
|
||||||
// { label: '修改手机号', type: 'navigate', url: '/pages/change-phone/change-phone' },
|
// { 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: 'navigate', url: '/pages/agreement/agreement' },
|
||||||
{ label: "联系客服", type: "action", action: "contactService" },
|
{ label: '联系客服', type: 'action', action: 'contactService' }
|
||||||
]);
|
])
|
||||||
|
|
||||||
// 生命周期钩子
|
// 生命周期钩子
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getLoginUserPhoneInfo();
|
getLoginUserPhoneInfo()
|
||||||
});
|
})
|
||||||
|
|
||||||
const getLoginUserPhoneInfo = async () => {
|
const getLoginUserPhoneInfo = async () => {
|
||||||
const res = await getLoginUserPhone();
|
const res = await getLoginUserPhone()
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
userInfo.value.phone = res.data;
|
userInfo.value.phone = res.data
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// 处理菜单点击
|
// 处理菜单点击
|
||||||
const handleMenuClick = (item) => {
|
const handleMenuClick = (item) => {
|
||||||
if (item.type === "navigate" && item.url) {
|
if (item.type === 'navigate' && item.url) {
|
||||||
uni.navigateTo({ url: item.url });
|
uni.navigateTo({ url: item.url })
|
||||||
} else if (item.type === "action") {
|
} else if (item.type === 'action') {
|
||||||
if (item.action === "contactService") {
|
if (item.action === 'contactService') {
|
||||||
uni.showToast({ title: "联系客服功能待实现", icon: "none" });
|
uni.showToast({ title: '联系客服功能待实现', icon: 'none' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// 退出登录
|
// 退出登录
|
||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
title: "温馨提示",
|
title: '温馨提示',
|
||||||
content: "确定要退出登录吗?",
|
content: '确定要退出登录吗?',
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
if (res.confirm) {
|
if (res.confirm) {
|
||||||
uni.clearStorageSync();
|
uni.clearStorageSync()
|
||||||
uni.reLaunch({ url: "/pages/login/index" });
|
uni.reLaunch({ url: '/pages/login/index' })
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
<ModuleTitle title="购买须知" />
|
<ModuleTitle title="购买须知" />
|
||||||
|
|
||||||
<zero-markdown-view :markdown="goodsData.commodityTip" :aiMode='true' />
|
<zero-markdown-view :markdown="goodsData.commodityTip" />
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
|
|||||||
@ -1,47 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view class="wave">
|
|
||||||
<view class="dot"></view>
|
|
||||||
<view class="dot"></view>
|
|
||||||
<view class="dot"></view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script></script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.wave {
|
|
||||||
position: relative;
|
|
||||||
text-align: center;
|
|
||||||
width: 30px;
|
|
||||||
|
|
||||||
.dot {
|
|
||||||
display: inline-block;
|
|
||||||
width: 3px;
|
|
||||||
height: 3px;
|
|
||||||
border-radius: 50%;
|
|
||||||
margin-right: 3px;
|
|
||||||
background: #333333;
|
|
||||||
animation: wave 1.3s linear infinite;
|
|
||||||
|
|
||||||
&:nth-child(2) {
|
|
||||||
animation-delay: -1.1s;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:nth-child(3) {
|
|
||||||
animation-delay: -0.9s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes wave {
|
|
||||||
0%,
|
|
||||||
60%,
|
|
||||||
100% {
|
|
||||||
transform: initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
30% {
|
|
||||||
transform: translateY(-5px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view class="ai">
|
|
||||||
<view class="container">
|
|
||||||
<view class="c c4"></view>
|
|
||||||
<view class="c c1"></view>
|
|
||||||
<view class="c c2"></view>
|
|
||||||
<view class="c c3"></view>
|
|
||||||
|
|
||||||
<view class="rings"></view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="glass"></view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
@import "styles/loading.scss";
|
|
||||||
</style>
|
|
||||||
@ -1,281 +0,0 @@
|
|||||||
@property --a {
|
|
||||||
syntax: "<angle>";
|
|
||||||
inherits: true;
|
|
||||||
initial-value: 0deg;
|
|
||||||
}
|
|
||||||
|
|
||||||
@property --l {
|
|
||||||
syntax: "<number>";
|
|
||||||
inherits: true;
|
|
||||||
initial-value: 0;
|
|
||||||
}
|
|
||||||
@property --x {
|
|
||||||
syntax: "<length>";
|
|
||||||
inherits: false;
|
|
||||||
initial-value: 0;
|
|
||||||
}
|
|
||||||
@property --y {
|
|
||||||
syntax: "<length>";
|
|
||||||
inherits: false;
|
|
||||||
initial-value: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@property --o {
|
|
||||||
syntax: "<number>";
|
|
||||||
inherits: false;
|
|
||||||
initial-value: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ai {
|
|
||||||
--s: 32px;
|
|
||||||
// --p: calc(var(--s) / 4);
|
|
||||||
width: var(--s);
|
|
||||||
aspect-ratio: 1;
|
|
||||||
--bg-color: color-mix(in srgb, #7b7bf4, transparent 90%);
|
|
||||||
background: radial-gradient(
|
|
||||||
60% 75% at center,
|
|
||||||
var(--bg-color) 50%,
|
|
||||||
transparent 50%
|
|
||||||
),
|
|
||||||
radial-gradient(75% 60% at center, var(--bg-color) 50%, transparent 50%);
|
|
||||||
padding: var(--p);
|
|
||||||
margin-right: 6px;
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
position: relative;
|
|
||||||
border-radius: 50%;
|
|
||||||
transform: scale(1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
$count: 4;
|
|
||||||
:root {
|
|
||||||
--count: #{$count};
|
|
||||||
--radius: 6vmin;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes ai {
|
|
||||||
from {
|
|
||||||
--a: 360deg;
|
|
||||||
--l: 0.35;
|
|
||||||
--o: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
30% {
|
|
||||||
--l: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
70% {
|
|
||||||
--o: 0.4;
|
|
||||||
--l: 0.05;
|
|
||||||
}
|
|
||||||
|
|
||||||
98% {
|
|
||||||
--o: 0.7;
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
--a: 0deg;
|
|
||||||
--l: 0.35;
|
|
||||||
--o: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.c {
|
|
||||||
opacity: 0.9;
|
|
||||||
position: absolute;
|
|
||||||
width: 10vmin;
|
|
||||||
aspect-ratio: 1;
|
|
||||||
border-radius: 50%;
|
|
||||||
--offset-per-item: calc(360deg / var(--count));
|
|
||||||
--current-angle-offset: calc(var(--offset-per-item) * var(--i) + var(--a));
|
|
||||||
translate: calc(
|
|
||||||
cos(var(--current-angle-offset)) * var(--radius) + var(--x, 0)
|
|
||||||
)
|
|
||||||
calc(sin(var(--current-angle-offset)) * var(--radius) * -1);
|
|
||||||
scale: calc(0.6 + var(--l));
|
|
||||||
animation: ai 5.5s cubic-bezier(0.45, -0.35, 0.16, 1.5) infinite;
|
|
||||||
transition: opacity 0.3s linear;
|
|
||||||
opacity: var(--o, 1);
|
|
||||||
@for $i from 0 through $count {
|
|
||||||
&:nth-child(#{$i + 1}) {
|
|
||||||
--i: #{$i};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.c1 {
|
|
||||||
background: #79e3ee;
|
|
||||||
background: radial-gradient(50% 50% at center, #79e3ee, #e7e7fb);
|
|
||||||
background: radial-gradient(50% 50% at center, #c979ee, #74bcd6);
|
|
||||||
|
|
||||||
--x: 1vmin;
|
|
||||||
width: 16vmin;
|
|
||||||
animation-timing-function: cubic-bezier(0.12, 0.32, 0.68, 0.24);
|
|
||||||
}
|
|
||||||
|
|
||||||
.c2 {
|
|
||||||
background: radial-gradient(50% 50% at center, #ef788c, #e7e7fb);
|
|
||||||
width: 15vmin;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c3 {
|
|
||||||
background: radial-gradient(50% 50% at center, #eb7fc6, transparent);
|
|
||||||
width: 5vmin;
|
|
||||||
opacity: 0.6;
|
|
||||||
--x: -1vmin;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c4 {
|
|
||||||
background: #6d67c8;
|
|
||||||
animation-timing-function: cubic-bezier(0.39, -0.03, 0.75, 0.47);
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
overflow: hidden;
|
|
||||||
background: #b6a9f8;
|
|
||||||
width: 100%;
|
|
||||||
border-radius: 50%;
|
|
||||||
aspect-ratio: 1;
|
|
||||||
position: relative;
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.glass {
|
|
||||||
overflow: hidden;
|
|
||||||
position: absolute;
|
|
||||||
--w: 0.5vmin;
|
|
||||||
inset: calc(var(--s) - var(--s));
|
|
||||||
border-radius: 50%;
|
|
||||||
backdrop-filter: blur(1.3vmin);
|
|
||||||
box-shadow: 0 0 8vmin color-mix(in srgb, black, transparent 70%);
|
|
||||||
background: radial-gradient(
|
|
||||||
10vmin at 70% 30%,
|
|
||||||
rgba(255, 255, 255, 0.2),
|
|
||||||
transparent
|
|
||||||
);
|
|
||||||
|
|
||||||
// // lines
|
|
||||||
// &:after {
|
|
||||||
// content: "";
|
|
||||||
// position: absolute;
|
|
||||||
// inset: 0;
|
|
||||||
// --c: rgba(255, 255, 255, 0.03);
|
|
||||||
// --w: 0.0625rem;
|
|
||||||
// --g: 0.1875rem;
|
|
||||||
|
|
||||||
// background: repeating-linear-gradient(
|
|
||||||
// var(--c),
|
|
||||||
// var(--c),
|
|
||||||
// var(--w),
|
|
||||||
// transparent var(--w),
|
|
||||||
// transparent calc(var(--w) + var(--g))
|
|
||||||
// );
|
|
||||||
// border-radius: inherit;
|
|
||||||
// border: 1vmin rgba(255, 255, 255, 0.1) solid;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
@property --value {
|
|
||||||
syntax: "<angle>";
|
|
||||||
inherits: true;
|
|
||||||
initial-value: 0deg;
|
|
||||||
}
|
|
||||||
|
|
||||||
@property --width-ratio {
|
|
||||||
syntax: "<number>";
|
|
||||||
inherits: true;
|
|
||||||
initial-value: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@property --scale {
|
|
||||||
syntax: "<number>";
|
|
||||||
inherits: true;
|
|
||||||
initial-value: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--width: 1vmin;
|
|
||||||
--duration: 8s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rings {
|
|
||||||
aspect-ratio: 1;
|
|
||||||
border-radius: 50%;
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
perspective: 11rem;
|
|
||||||
opacity: .9;
|
|
||||||
|
|
||||||
&:before,
|
|
||||||
&:after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
background: rgba(255, 0, 0, 1);
|
|
||||||
border-radius: 50%;
|
|
||||||
--width-ratio: 1;
|
|
||||||
border: calc(var(--width) * var(--width-ratio)) solid transparent;
|
|
||||||
mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0);
|
|
||||||
background: linear-gradient(
|
|
||||||
white,
|
|
||||||
blue,
|
|
||||||
magenta,
|
|
||||||
violet,
|
|
||||||
lightyellow) border-box;
|
|
||||||
mask-composite: exclude;
|
|
||||||
animation: ring var(--duration) ease-in-out infinite;
|
|
||||||
--start: 180deg;
|
|
||||||
--value: var(--start);
|
|
||||||
--scale: 1;
|
|
||||||
transform: rotateY(var(--value)) rotateX(var(--value)) rotateZ(var(--value))
|
|
||||||
scale(var(--scale));
|
|
||||||
}
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
--start: 180deg;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
--start: 90deg;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .rings {
|
|
||||||
&:before {
|
|
||||||
--start: 360deg;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
--start: 270deg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes ring {
|
|
||||||
from {
|
|
||||||
--value: var(--start);
|
|
||||||
--scale: 1;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
--scale: 1.2;
|
|
||||||
--width-ratio: 1.5;
|
|
||||||
}
|
|
||||||
70% {
|
|
||||||
--scale: 1;
|
|
||||||
--value: calc(var(--start) + 180deg);
|
|
||||||
--width-ratio: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
80% {
|
|
||||||
--scale: 1.2;
|
|
||||||
--width-ratio: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
--value: calc(var(--start) + 360deg);
|
|
||||||
--scale: 1;
|
|
||||||
--width-ratio: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@ -12,31 +12,32 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, nextTick, defineEmits } from "vue";
|
import { ref, nextTick, defineEmits } from 'vue'
|
||||||
import { onMounted } from "vue";
|
import { onMounted } from 'vue'
|
||||||
import { SCROLL_TO_BOTTOM } from "@/constant/constant";
|
import { SCROLL_TO_BOTTOM } from '@/constant/constant'
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
question: {
|
question: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "",
|
default: ''
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const tags = ref([]);
|
const tags = ref([])
|
||||||
const emits = defineEmits(["replySent"]);
|
const emits = defineEmits(['replySent']);
|
||||||
|
|
||||||
const handleClick = (item) => {
|
const handleClick = (item) => {
|
||||||
emits("replySent", item);
|
emits('replySent', item)
|
||||||
};
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
tags.value = props.question.split(/[&|;]/).filter((tag) => tag.trim() !== "");
|
tags.value = props.question.split(/[&|;]/).filter(tag => tag.trim() !== '')
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
uni.$emit(SCROLL_TO_BOTTOM, true);
|
uni.$emit(SCROLL_TO_BOTTOM, true)
|
||||||
}, 300);
|
}, 300)
|
||||||
});
|
});
|
||||||
});
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@ -58,7 +59,7 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tag-text {
|
.tag-text {
|
||||||
color: #00a6ff; /* 蓝色文字,可根据设计调整 */
|
color: #00A6FF; /* 蓝色文字,可根据设计调整 */
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,82 +1,82 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="container">
|
<view class="container">
|
||||||
<swiper
|
<swiper class="swiper" circular
|
||||||
class="swiper"
|
:indicator-dots="activityList.length > 1"
|
||||||
circular
|
indicator-color="#FFFFFF"
|
||||||
:indicator-dots="activityList.length > 1"
|
indicator-active-color="#00A6FF"
|
||||||
indicator-color="#FFFFFF"
|
:autoplay="autoplay"
|
||||||
indicator-active-color="#00A6FF"
|
:interval="interval"
|
||||||
:autoplay="autoplay"
|
:duration="duration">
|
||||||
:interval="interval"
|
<swiper-item v-for="item in activityList" :key="item.id">
|
||||||
:duration="duration"
|
<view class="swiper-item" @click="handleClick(item)">
|
||||||
>
|
<image :src="item.activityCover" mode="aspectFill"></image>
|
||||||
<swiper-item v-for="item in activityList" :key="item.id">
|
<view class="corner-btn">快速预定</view>
|
||||||
<view class="swiper-item" @click="handleClick(item)">
|
</view>
|
||||||
<image :src="item.activityCover" mode="aspectFill"></image>
|
</swiper-item>
|
||||||
<view class="corner-btn">快速预定</view>
|
</swiper>
|
||||||
</view>
|
|
||||||
</swiper-item>
|
</view>
|
||||||
</swiper>
|
|
||||||
</view>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from "vue";
|
import { ref } from 'vue'
|
||||||
import { SEND_COMMAND_TEXT } from "@/constant/constant";
|
import { SEND_COMMAND_TEXT } from '@/constant/constant'
|
||||||
|
|
||||||
const autoplay = ref(true);
|
const autoplay = ref(true)
|
||||||
const interval = ref(3000);
|
const interval = ref(3000)
|
||||||
const duration = ref(500);
|
const duration = ref(500)
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
activityList: {
|
activityList: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: [],
|
default: []
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
|
const handleClick = (item) => {
|
||||||
|
uni.$emit(SEND_COMMAND_TEXT, '快速预定')
|
||||||
|
}
|
||||||
|
|
||||||
const handleClick = (item) => {
|
|
||||||
uni.$emit(SEND_COMMAND_TEXT, "快速预定");
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.container {
|
.container {
|
||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
|
|
||||||
.uni-margin-wrap {
|
.uni-margin-wrap {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.swiper {
|
.swiper {
|
||||||
height: 120px;
|
height: 120px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
.swiper-item {
|
.swiper-item {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: block;
|
display: block;
|
||||||
height: 120px;
|
height: 120px;
|
||||||
line-height: 120px;
|
line-height: 120px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
image {
|
image {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.corner-btn {
|
.corner-btn {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 12px;
|
right: 12px;
|
||||||
bottom: 12px;
|
bottom: 12px;
|
||||||
background-color: #ffeb00;
|
background-color: #ffeb00;
|
||||||
color: #333;
|
color: #333;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
padding: 4px 12px;
|
padding: 4px 12px;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
}
|
||||||
|
</style>
|
||||||
@ -22,6 +22,7 @@
|
|||||||
<text class="calendar-text">日历</text>
|
<text class="calendar-text">日历</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@ -71,4 +71,4 @@ onMounted(() => {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -1,203 +1,214 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="container">
|
<view class="container">
|
||||||
<ModuleTitle :title="commodityDTO.title" />
|
<ModuleTitle :title="commodityDTO.title" />
|
||||||
<view class="container-scroll">
|
<view class="container-scroll">
|
||||||
<view
|
<view
|
||||||
v-for="(item, index) in commodityDTO.commodityList"
|
v-for="(item, index) in commodityDTO.commodityList"
|
||||||
:key="`${item.commodityId}-${index}`"
|
: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" />
|
|
||||||
<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-tag">{{ tag }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<template
|
|
||||||
v-for="(serviceItem, index) in item.commodityServices"
|
|
||||||
:key="serviceItem.serviceTitle"
|
|
||||||
>
|
>
|
||||||
<view v-if="index < 3" class="card-desc"
|
<view class="mk-card-item" @click="placeOrderHandle(item)">
|
||||||
>· {{ serviceItem.serviceTitle }}</view
|
<!-- <view class="card-badge">超值推荐</view> -->
|
||||||
>
|
<image
|
||||||
</template>
|
class="card-img"
|
||||||
<view class="card-bottom-row">
|
:src="item.commodityIcon"
|
||||||
<view class="card-price-row">
|
mode="aspectFill"
|
||||||
<text class="card-price-fu">¥</text>
|
/>
|
||||||
<text class="card-price">{{ item.commodityPrice }}</text>
|
<view class="card-content">
|
||||||
<text class="card-unit">/人</text>
|
<view class="card-title-column">
|
||||||
</view>
|
<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>
|
||||||
|
<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>
|
<text class="card-btn">下单</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import ModuleTitle from "@/components/ModuleTitle/index.vue";
|
import ModuleTitle from "@/components/ModuleTitle/index.vue";
|
||||||
import { defineProps } from "vue";
|
import { defineProps } from "vue";
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
commodityDTO: {
|
commodityDTO: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: {},
|
default: {},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
/// 去下单
|
/// 去下单
|
||||||
const placeOrderHandle = (item) => {
|
const placeOrderHandle = (item) => {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: `/pages/goods/index?commodityId=${item.commodityId}`,
|
url: `/pages/goods/index?commodityId=${item.commodityId}`,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.container {
|
.container {
|
||||||
.container-scroll {
|
.container-scroll {
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
overflow-x: auto;
|
|
||||||
overflow-y: hidden;
|
|
||||||
margin: 4px 0;
|
|
||||||
|
|
||||||
.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;
|
|
||||||
object-fit: cover; /* 确保图片不变形,保持比例裁剪 */
|
|
||||||
flex-shrink: 0; /* 防止图片被压缩 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-content {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 10px 12px 0 12px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: start;
|
|
||||||
width: 100%;
|
|
||||||
flex: 1; /* 让内容区域占据剩余空间 */
|
|
||||||
overflow: hidden; /* 防止内容溢出 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-title-column {
|
|
||||||
display: flex;
|
|
||||||
align-items: start;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-title {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #222;
|
|
||||||
width: 100%;
|
|
||||||
/* 限制标题最多显示两行 */
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
-webkit-line-clamp: 2;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
line-height: 1.4;
|
|
||||||
max-height: 2.8em; /* 2行的高度 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-tags {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: start;
|
overflow-x: auto;
|
||||||
padding: 6px 0;
|
overflow-y: hidden;
|
||||||
}
|
margin: 4px 0;
|
||||||
|
|
||||||
.card-tag {
|
.mk-card-item {
|
||||||
color: #ff6600;
|
position: relative;
|
||||||
font-size: 10px;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 0 6px;
|
|
||||||
margin-left: 2px;
|
|
||||||
border: 1px solid #ff6600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-desc {
|
display: flex;
|
||||||
font-size: 13px;
|
flex-direction: column;
|
||||||
color: #888;
|
align-items: start;
|
||||||
margin-top: 2px;
|
width: 188px;
|
||||||
}
|
// height: 244px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin-right: 8px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
|
||||||
.card-bottom-row {
|
.card-badge {
|
||||||
display: flex;
|
position: absolute;
|
||||||
align-items: center;
|
top: 8px;
|
||||||
justify-content: space-between;
|
left: 8px;
|
||||||
margin-top: 8px;
|
background: #ffe7b2;
|
||||||
width: 100%;
|
color: #b97a00;
|
||||||
}
|
font-size: 12px;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
.card-price-row {
|
.card-img {
|
||||||
.card-price-fu {
|
width: 188px;
|
||||||
color: #ff6600;
|
height: 114px;
|
||||||
font-size: 11px;
|
border-radius: 10px;
|
||||||
font-weight: normal;
|
object-fit: cover; /* 确保图片不变形,保持比例裁剪 */
|
||||||
|
flex-shrink: 0; /* 防止图片被压缩 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 10px 12px 0 12px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: start;
|
||||||
|
width: 100%;
|
||||||
|
flex: 1; /* 让内容区域占据剩余空间 */
|
||||||
|
overflow: hidden; /* 防止内容溢出 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title-column {
|
||||||
|
display: flex;
|
||||||
|
align-items: start;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #222;
|
||||||
|
width: 100%;
|
||||||
|
/* 限制标题最多显示两行 */
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
line-height: 1.4;
|
||||||
|
max-height: 2.8em; /* 2行的高度 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-tags {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: start;
|
||||||
|
padding: 6px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-tag {
|
||||||
|
background: #f5f5f5;
|
||||||
|
color: #ff6600;
|
||||||
|
font-size: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0 6px;
|
||||||
|
margin-left: 2px;
|
||||||
|
border: 1px solid #ff6600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.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>
|
</style>
|
||||||
|
|||||||
@ -1,33 +1,36 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="container">
|
<view class="container">
|
||||||
<template v-if="toolCall.picture && toolCall.picture.length > 0">
|
<template v-if="toolCall.picture && toolCall.picture.length > 0">
|
||||||
<ModuleTitle :title="图片详情" />
|
<ModuleTitle :title="图片详情" />
|
||||||
<ImageSwiper :images="toolCall.picture" />
|
<ImageSwiper :images="toolCall.picture" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="toolCall.commodityList">
|
<template v-if="toolCall.commodityList">
|
||||||
<DetailCardGoodsContentList :commodityList="toolCall.commodityList" />
|
<DetailCardGoodsContentList :commodityList="toolCall.commodityList" />
|
||||||
</template>
|
</template>
|
||||||
</view>
|
|
||||||
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps } from "vue";
|
import { defineProps } from 'vue'
|
||||||
import ModuleTitle from "@/components/ModuleTitle/index.vue";
|
import ModuleTitle from '@/components/ModuleTitle/index.vue'
|
||||||
import ImageSwiper from "@/components/ImageSwiper/index.vue";
|
import ImageSwiper from '@/components/ImageSwiper/index.vue'
|
||||||
import DetailCardGoodsContentList from "./DetailCardGoodsContentList.vue";
|
import DetailCardGoodsContentList from './DetailCardGoodsContentList.vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
toolCall: {
|
||||||
|
type: Object,
|
||||||
|
default: {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
toolCall: {
|
|
||||||
type: Object,
|
|
||||||
default: {},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.container {
|
.container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 12px 0;
|
padding: 12px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,207 +1,214 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="container">
|
<view class="container">
|
||||||
<ModuleTitle title="相关商品" />
|
<ModuleTitle title="相关商品" />
|
||||||
<view class="container-scroll">
|
<view class="container-scroll">
|
||||||
<view
|
<view
|
||||||
v-for="(item, index) in commodityList"
|
v-for="(item, index) in commodityList"
|
||||||
:key="`${item.commodityId}-${index}`"
|
:key="`${item.commodityId}-${index}`"
|
||||||
>
|
|
||||||
<view class="mk-card-item" @click="placeOrderHandle(item)">
|
|
||||||
<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-tag">{{ tag }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<template
|
|
||||||
v-for="(serviceItem, index) in item.commodityServices"
|
|
||||||
:key="serviceItem.serviceTitle"
|
|
||||||
>
|
>
|
||||||
<view v-if="index < 3" class="card-desc"
|
<view class="mk-card-item" @click="placeOrderHandle(item)">
|
||||||
>· {{ serviceItem.serviceTitle }}</view
|
<image
|
||||||
>
|
class="card-img"
|
||||||
</template>
|
:src="item.commodityPhoto"
|
||||||
<view class="card-bottom-row">
|
mode="aspectFill"
|
||||||
<view class="card-price-row">
|
/>
|
||||||
<text class="card-price-fu">¥</text>
|
<view class="card-content">
|
||||||
<text class="card-price">{{ item.specificationPrice }}</text>
|
<view class="card-title-column">
|
||||||
<text class="card-unit">/人</text>
|
<text class="card-title">{{
|
||||||
</view>
|
item.commodityName
|
||||||
<text class="card-btn">下单</text>
|
}}</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>
|
||||||
|
<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-unit">/人</text>
|
||||||
|
</view>
|
||||||
|
<text class="card-btn">下单</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import ModuleTitle from "@/components/ModuleTitle/index.vue";
|
import ModuleTitle from "@/components/ModuleTitle/index.vue";
|
||||||
import { defineProps } from "vue";
|
import { defineProps } from "vue";
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
commodityList: {
|
commodityList: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: [],
|
default: [],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
/// 去下单
|
/// 去下单
|
||||||
const placeOrderHandle = (item) => {
|
const placeOrderHandle = (item) => {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: `/pages/goods/index?commodityId=${item.commodityId}`,
|
url: `/pages/goods/index?commodityId=${item.commodityId}`,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.container {
|
.container {
|
||||||
margin: 6px 0 0;
|
margin: 6px 0 0;
|
||||||
|
|
||||||
.container-scroll {
|
.container-scroll {
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
overflow-x: auto;
|
|
||||||
overflow-y: hidden;
|
|
||||||
margin: 4px 0;
|
|
||||||
|
|
||||||
.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;
|
|
||||||
object-fit: cover; /* 确保图片不变形,保持比例裁剪 */
|
|
||||||
flex-shrink: 0; /* 防止图片被压缩 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-content {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 10px 12px 0 12px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: start;
|
|
||||||
width: 100%;
|
|
||||||
flex: 1; /* 让内容区域占据剩余空间 */
|
|
||||||
overflow: hidden; /* 防止内容溢出 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-title-column {
|
|
||||||
display: flex;
|
|
||||||
align-items: start;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-title {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #222;
|
|
||||||
width: 100%;
|
|
||||||
/* 限制标题最多显示两行 */
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
-webkit-line-clamp: 2;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
line-height: 1.4;
|
|
||||||
max-height: 2.8em; /* 2行的高度 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-tags {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: start;
|
overflow-x: auto;
|
||||||
padding: 6px 0;
|
overflow-y: hidden;
|
||||||
}
|
margin: 4px 0;
|
||||||
|
|
||||||
.card-tag {
|
.mk-card-item {
|
||||||
color: #ff6600;
|
position: relative;
|
||||||
font-size: 10px;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 0 6px;
|
|
||||||
margin-left: 2px;
|
|
||||||
border: 1px solid #ff6600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-desc {
|
display: flex;
|
||||||
font-size: 13px;
|
flex-direction: column;
|
||||||
color: #888;
|
align-items: start;
|
||||||
margin-top: 2px;
|
width: 188px;
|
||||||
}
|
// height: 244px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin-right: 8px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
|
||||||
.card-bottom-row {
|
.card-badge {
|
||||||
display: flex;
|
position: absolute;
|
||||||
align-items: center;
|
top: 8px;
|
||||||
justify-content: space-between;
|
left: 8px;
|
||||||
margin-top: 8px;
|
background: #ffe7b2;
|
||||||
width: 100%;
|
color: #b97a00;
|
||||||
}
|
font-size: 12px;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
.card-price-row {
|
.card-img {
|
||||||
.card-price-fu {
|
width: 188px;
|
||||||
color: #ff6600;
|
height: 114px;
|
||||||
font-size: 11px;
|
border-radius: 10px;
|
||||||
font-weight: normal;
|
object-fit: cover; /* 确保图片不变形,保持比例裁剪 */
|
||||||
|
flex-shrink: 0; /* 防止图片被压缩 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 10px 12px 0 12px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: start;
|
||||||
|
width: 100%;
|
||||||
|
flex: 1; /* 让内容区域占据剩余空间 */
|
||||||
|
overflow: hidden; /* 防止内容溢出 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title-column {
|
||||||
|
display: flex;
|
||||||
|
align-items: start;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #222;
|
||||||
|
width: 100%;
|
||||||
|
/* 限制标题最多显示两行 */
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
line-height: 1.4;
|
||||||
|
max-height: 2.8em; /* 2行的高度 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-tags {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: start;
|
||||||
|
padding: 6px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-tag {
|
||||||
|
background: #f5f5f5;
|
||||||
|
color: #ff6600;
|
||||||
|
font-size: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0 6px;
|
||||||
|
margin-left: 2px;
|
||||||
|
border: 1px solid #ff6600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.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>
|
</style>
|
||||||
|
|||||||
@ -1,42 +1,43 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="container">
|
<view class="container">
|
||||||
<view v-for="item in themeDTOList" :key="item.title">
|
<view v-for="item in themeDTOList" :key="item.title">
|
||||||
<RecommendPostsList :recommendTheme="item" />
|
<RecommendPostsList :recommendTheme="item" />
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, nextTick } from "vue";
|
import { ref, nextTick } from 'vue'
|
||||||
import { onMounted } from "vue";
|
import { onMounted } from 'vue'
|
||||||
import { discoveryCradComponent } from "@/request/api/MainPageDataApi";
|
import { discoveryCradComponent } from '@/request/api/MainPageDataApi'
|
||||||
import RecommendPostsList from "../recommend/RecommendPostsList.vue";
|
import RecommendPostsList from '../recommend/RecommendPostsList.vue'
|
||||||
import { SCROLL_TO_BOTTOM } from "@/constant/constant";
|
import { SCROLL_TO_BOTTOM } from '@/constant/constant'
|
||||||
|
|
||||||
const themeDTOList = ref([]);
|
const themeDTOList = ref([])
|
||||||
|
|
||||||
const loadDiscoveryCradComponent = async () => {
|
const loadDiscoveryCradComponent = async () => {
|
||||||
const res = await discoveryCradComponent();
|
const res = await discoveryCradComponent()
|
||||||
if (res.code === 0 && res.data) {
|
if(res.code === 0 && res.data) {
|
||||||
themeDTOList.value = res.data.themeDTOList;
|
themeDTOList.value = res.data.themeDTOList
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.$emit(SCROLL_TO_BOTTOM, true)
|
||||||
|
}, 300)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
nextTick(() => {
|
onMounted(() => {
|
||||||
setTimeout(() => {
|
loadDiscoveryCradComponent()
|
||||||
uni.$emit(SCROLL_TO_BOTTOM, true);
|
})
|
||||||
}, 300);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
loadDiscoveryCradComponent();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.container {
|
.container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -1,18 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="container">
|
<view class="container">
|
||||||
<ModuleTitle :title="recommendTheme.themeName" />
|
<ModuleTitle :title="recommendTheme.themeName" />
|
||||||
<view class="container-scroll">
|
<view class="container-scroll">
|
||||||
<view
|
<view
|
||||||
v-for="(item, index) in recommendTheme.recommendPostsList"
|
v-for="(item, index) in recommendTheme.recommendPostsList"
|
||||||
:key="index"
|
:key="index"
|
||||||
>
|
>
|
||||||
<view class="mk-card-item" @click="sendReply(item)">
|
<view class="mk-card-item" @click="sendReply(item)">
|
||||||
<image :src="item.coverPhoto" mode="widthFix"></image>
|
<image :src="item.coverPhoto" mode="widthFix"></image>
|
||||||
<text>{{ item.topic }}</text>
|
<text>{{ item.topic }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@ -20,48 +20,48 @@ import ModuleTitle from "@/components/ModuleTitle/index.vue";
|
|||||||
import { RECOMMEND_POSTS_TITLE } from "@/constant/constant";
|
import { RECOMMEND_POSTS_TITLE } from "@/constant/constant";
|
||||||
import { defineProps } from "vue";
|
import { defineProps } from "vue";
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
recommendTheme: {
|
recommendTheme: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: {},
|
default: {},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const sendReply = (item) => {
|
const sendReply = (item) => {
|
||||||
const topic = item.userInputContent || item.topic.replace(/^#/, "");
|
const topic = item.userInputContent || item.topic.replace(/^#/, "");
|
||||||
uni.$emit(RECOMMEND_POSTS_TITLE, topic);
|
uni.$emit(RECOMMEND_POSTS_TITLE, topic);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.container {
|
.container {
|
||||||
.container-scroll {
|
.container-scroll {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
|
|
||||||
.mk-card-item {
|
.mk-card-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: start;
|
align-items: start;
|
||||||
width: 188px;
|
width: 188px;
|
||||||
height: 154px;
|
height: 154px;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
|
|
||||||
image {
|
image {
|
||||||
width: 188px;
|
width: 188px;
|
||||||
height: 112px;
|
height: 112px;
|
||||||
}
|
}
|
||||||
text {
|
text {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #333333;
|
color: #333333;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,27 +1,29 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="container">
|
<view class="container">
|
||||||
<view v-for="item in recommendThemeList" :key="item.themeName">
|
<view v-for="item in recommendThemeList" :key="item.themeName">
|
||||||
<RecommendPostsList :recommendTheme="item" />
|
<RecommendPostsList :recommendTheme="item" />
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import RecommendPostsList from "./RecommendPostsList.vue";
|
import RecommendPostsList from './RecommendPostsList.vue';
|
||||||
|
|
||||||
import { defineProps } from "vue";
|
import { defineProps } from 'vue'
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
recommendThemeList: {
|
recommendThemeList: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: [],
|
default: []
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.container {
|
.container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -1,50 +1,48 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="container">
|
<view class="container">
|
||||||
<ModuleTitle :title="recommendTheme.themeName" />
|
<ModuleTitle :title="recommendTheme.themeName" />
|
||||||
<view class="container-scroll">
|
<view class="container-scroll">
|
||||||
<view
|
<view v-for="(item, index) in recommendTheme.recommendPostsList" :key="index">
|
||||||
v-for="(item, index) in recommendTheme.recommendPostsList"
|
<view class="mk-card-item" @click="sendReply(item)">
|
||||||
:key="index"
|
<image :src="item.coverPhoto" mode="aspectFill"></image>
|
||||||
>
|
<view class="overlay-gradient">
|
||||||
<view class="mk-card-item" @click="sendReply(item)">
|
<text class="overlay-text">{{ item.topic }}</text>
|
||||||
<image :src="item.coverPhoto" mode="aspectFill"></image>
|
</view>
|
||||||
<view class="overlay-gradient">
|
</view>
|
||||||
<text class="overlay-text">{{ item.topic }}</text>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import ModuleTitle from "@/components/ModuleTitle/index.vue";
|
import ModuleTitle from '@/components/ModuleTitle/index.vue'
|
||||||
import { defineProps } from "vue";
|
import { defineProps } from 'vue'
|
||||||
import { RECOMMEND_POSTS_TITLE } from "@/constant/constant.js";
|
import { RECOMMEND_POSTS_TITLE } from '@/constant/constant.js'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
recommendTheme: {
|
recommendTheme: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: {},
|
default: {}
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
|
const sendReply = (item) => {
|
||||||
|
const topic = item.userInputContent || item.topic.replace(/^#/, '');
|
||||||
|
uni.$emit(RECOMMEND_POSTS_TITLE, topic);
|
||||||
|
}
|
||||||
|
|
||||||
const sendReply = (item) => {
|
|
||||||
const topic = item.userInputContent || item.topic.replace(/^#/, "");
|
|
||||||
uni.$emit(RECOMMEND_POSTS_TITLE, topic);
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.container {
|
.container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container-scroll {
|
.container-scroll {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
margin: 4px 0 6px;
|
margin-top: 4px;
|
||||||
|
|
||||||
.mk-card-item {
|
.mk-card-item {
|
||||||
flex-shrink: 0; /* 关键:防止 flex 布局压缩子元素宽度 */
|
flex-shrink: 0; /* 关键:防止 flex 布局压缩子元素宽度 */
|
||||||
@ -89,4 +87,5 @@ const sendReply = (item) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -2,11 +2,7 @@
|
|||||||
<view class="notice-info mb12">
|
<view class="notice-info mb12">
|
||||||
<view class="notice-title"> 购买须知 </view>
|
<view class="notice-title"> 购买须知 </view>
|
||||||
|
|
||||||
<zero-markdown-view
|
<zero-markdown-view :markdown="orderData.commodityTip" :fontSize="14" />
|
||||||
:markdown="orderData.commodityTip"
|
|
||||||
:fontSize="14"
|
|
||||||
:aiMode="true"
|
|
||||||
/>
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -31,4 +27,4 @@ const props = defineProps({
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "./styles/index.scss";
|
@import "./styles/index.scss";
|
||||||
</style>
|
</style>
|
||||||
@ -36,10 +36,6 @@ export default {
|
|||||||
type: String,
|
type: String,
|
||||||
default: '#2d2d2d'
|
default: '#2d2d2d'
|
||||||
},
|
},
|
||||||
fontFamily: {
|
|
||||||
type: String,
|
|
||||||
default: 'PingFang SC, PingFang SC'
|
|
||||||
},
|
|
||||||
aiMode: {
|
aiMode: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
@ -91,64 +87,59 @@ export default {
|
|||||||
initTagStyle() {
|
initTagStyle() {
|
||||||
const themeColor = this.themeColor
|
const themeColor = this.themeColor
|
||||||
const codeBgColor = this.codeBgColor
|
const codeBgColor = this.codeBgColor
|
||||||
const fontFamily = this.fontFamily
|
|
||||||
let zeroStyle = {
|
let zeroStyle = {
|
||||||
p: `
|
p: `
|
||||||
margin:6px 0;
|
margin:5px 5px;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
line-height:1.65;
|
line-height:1.75;
|
||||||
font-family: ${fontFamily};
|
letter-spacing:0.2em;
|
||||||
|
word-spacing:0.1em;
|
||||||
`,
|
`,
|
||||||
// 一级标题
|
// 一级标题
|
||||||
h1: `
|
h1: `
|
||||||
margin:6px 0;
|
margin:25px 0;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: ${themeColor};
|
color: ${themeColor};
|
||||||
font-family: ${fontFamily};
|
|
||||||
padding:3px 10px 1px;
|
padding:3px 10px 1px;
|
||||||
border-bottom: 2px solid ${themeColor};
|
border-bottom: 2px solid ${themeColor};
|
||||||
border-top-right-radius:3px;
|
border-top-right-radius:3px;
|
||||||
border-top-left-radius:3px;
|
border-top-left-radius:3px;
|
||||||
|
|
||||||
`,
|
`,
|
||||||
// 二级标题
|
// 二级标题
|
||||||
h2: `
|
h2: `
|
||||||
margin:6px 0;
|
margin:40px 0 20px 0;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
text-align:center;
|
text-align:center;
|
||||||
color:${themeColor};
|
color:${themeColor};
|
||||||
font-family: ${fontFamily};
|
|
||||||
font-weight:bolder;
|
font-weight:bolder;
|
||||||
padding-left:10px;
|
padding-left:10px;
|
||||||
// border:1px solid ${themeColor};
|
// border:1px solid ${themeColor};
|
||||||
`,
|
`,
|
||||||
// 三级标题
|
// 三级标题
|
||||||
h3: `
|
h3: `
|
||||||
margin:6px 0;
|
margin:30px 0 10px 0;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
color: ${themeColor};
|
color: ${themeColor};
|
||||||
font-family: ${fontFamily};
|
|
||||||
padding-left:10px;
|
padding-left:10px;
|
||||||
border-left:3px solid ${themeColor};
|
border-left:3px solid ${themeColor};
|
||||||
`,
|
`,
|
||||||
// 引用
|
// 引用
|
||||||
blockquote: `
|
blockquote: `
|
||||||
margin:6px 0;
|
margin:15px 0;
|
||||||
font-size:15px;
|
font-size:15px;
|
||||||
font-family: ${fontFamily};
|
|
||||||
color: #777777;
|
color: #777777;
|
||||||
border-left: 4px solid #dddddd;
|
border-left: 4px solid #dddddd;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
`,
|
`,
|
||||||
// 列表
|
// 列表
|
||||||
ul: `
|
ul: `
|
||||||
margin: 6px 0;
|
margin: 10px 0;
|
||||||
color: #555;
|
color: #555;
|
||||||
`,
|
`,
|
||||||
li: `
|
li: `
|
||||||
margin: 4px 0;
|
margin: 5px 0;
|
||||||
color: #555;
|
color: #555;
|
||||||
`,
|
`,
|
||||||
// 链接
|
// 链接
|
||||||
@ -158,13 +149,11 @@ export default {
|
|||||||
// 加粗
|
// 加粗
|
||||||
strong: `
|
strong: `
|
||||||
font-weight: border;
|
font-weight: border;
|
||||||
color: ${themeColor};
|
color: ${themeColor};
|
||||||
font-family: ${fontFamily};
|
|
||||||
`,
|
`,
|
||||||
// 斜体
|
// 斜体
|
||||||
em: `
|
em: `
|
||||||
color: ${themeColor};
|
color: ${themeColor};
|
||||||
font-family: ${fontFamily};
|
|
||||||
letter-spacing:0.3em;
|
letter-spacing:0.3em;
|
||||||
`,
|
`,
|
||||||
// 分割线
|
// 分割线
|
||||||
@ -205,72 +194,61 @@ export default {
|
|||||||
initTagStyleForAi() {
|
initTagStyleForAi() {
|
||||||
const themeColor = this.themeColor
|
const themeColor = this.themeColor
|
||||||
const codeBgColor = this.codeBgColor
|
const codeBgColor = this.codeBgColor
|
||||||
const fontFamily = this.fontFamily
|
|
||||||
let zeroStyle = {
|
let zeroStyle = {
|
||||||
p: `
|
p: `
|
||||||
margin:6px 0;
|
font-size: 16px;
|
||||||
font-size: 15px;
|
|
||||||
line-height:1.55;
|
|
||||||
font-family: ${fontFamily};
|
|
||||||
`,
|
`,
|
||||||
// 一级标题
|
// 一级标题
|
||||||
h1: `
|
h1: `
|
||||||
margin:4px 0;
|
margin:12px 0 10px 0;
|
||||||
font-size: 20px;
|
font-size: 22px;
|
||||||
color: ${themeColor};
|
color: ${themeColor};
|
||||||
font-family: ${fontFamily};
|
|
||||||
`,
|
`,
|
||||||
// 二级标题
|
// 二级标题
|
||||||
h2: `
|
h2: `
|
||||||
margin:4px 0;
|
margin:10px 0 10px 0;
|
||||||
font-size: 18px;
|
font-size: 20px;
|
||||||
color: ${themeColor};
|
color: ${themeColor};
|
||||||
font-family: ${fontFamily};
|
|
||||||
`,
|
`,
|
||||||
// 三级标题
|
// 三级标题
|
||||||
h3: `
|
h3: `
|
||||||
margin:4x 0;
|
margin:8x 0 8px 0;
|
||||||
font-size: 16px;
|
font-size: 18px;
|
||||||
color: ${themeColor};
|
color: ${themeColor};
|
||||||
font-family: ${fontFamily};
|
|
||||||
`,
|
`,
|
||||||
// 四级标题
|
// 四级标题
|
||||||
h4: `
|
h4: `
|
||||||
margin:4px 0;
|
margin:6px 0 8px 0;
|
||||||
font-size: 15px;
|
font-size: 16px;
|
||||||
color: ${themeColor};
|
color: ${themeColor};
|
||||||
font-family: ${fontFamily};
|
|
||||||
`,
|
`,
|
||||||
// 五级标题
|
// 五级标题
|
||||||
h5: `
|
h5: `
|
||||||
margin:4px 0;
|
margin:6px 0 8px 0;
|
||||||
font-size: 14px;
|
font-size: 16px;
|
||||||
color: ${themeColor};
|
color: ${themeColor};
|
||||||
font-family: ${fontFamily};
|
|
||||||
`,
|
`,
|
||||||
// 六级标题
|
// 六级标题
|
||||||
h6: `
|
h6: `
|
||||||
margin:4px 0;
|
margin:6px 0 8px 0;
|
||||||
font-size: 12px;
|
font-size: 16px;
|
||||||
color: ${themeColor};
|
color: ${themeColor}
|
||||||
font-family: ${fontFamily};
|
|
||||||
`,
|
`,
|
||||||
// 引用
|
// 引用
|
||||||
blockquote: `
|
blockquote: `
|
||||||
margin:4px 0;
|
margin:12px 0;
|
||||||
font-size:14px;
|
font-size:14px;
|
||||||
color: #777777;
|
color: #777777;
|
||||||
border-left: 4px solid #dddddd;
|
border-left: 4px solid #dddddd;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
font-family: ${fontFamily};
|
|
||||||
`,
|
`,
|
||||||
// 列表
|
// 列表
|
||||||
ul: `
|
ul: `
|
||||||
margin: 4px 0;
|
margin: 8px 0;
|
||||||
color: #555;
|
color: #555;
|
||||||
`,
|
`,
|
||||||
li: `
|
li: `
|
||||||
margin: 4px 0;
|
margin: 5px 0;
|
||||||
color: #555;
|
color: #555;
|
||||||
`,
|
`,
|
||||||
// 链接
|
// 链接
|
||||||
@ -281,7 +259,6 @@ export default {
|
|||||||
strong: `
|
strong: `
|
||||||
font-weight: border;
|
font-weight: border;
|
||||||
color: ${themeColor};
|
color: ${themeColor};
|
||||||
font-family: ${fontFamily};
|
|
||||||
`,
|
`,
|
||||||
// 斜体
|
// 斜体
|
||||||
em: `
|
em: `
|
||||||
@ -295,14 +272,14 @@ export default {
|
|||||||
border:none;
|
border:none;
|
||||||
text-align:center;
|
text-align:center;
|
||||||
background-image:linear-gradient(to right,rgba(248,57,41,0),${themeColor},rgba(248,57,41,0));
|
background-image:linear-gradient(to right,rgba(248,57,41,0),${themeColor},rgba(248,57,41,0));
|
||||||
margin:4px 0;
|
margin:8px 0;
|
||||||
`,
|
`,
|
||||||
// 表格
|
// 表格
|
||||||
table: `
|
table: `
|
||||||
border-spacing:0;
|
border-spacing:0;
|
||||||
overflow:auto;
|
overflow:auto;
|
||||||
min-width:100%;
|
min-width:100%;
|
||||||
margin:4px 0;
|
margin:8px 0;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
`,
|
`,
|
||||||
th: `
|
th: `
|
||||||
@ -319,7 +296,6 @@ export default {
|
|||||||
background: ${codeBgColor};
|
background: ${codeBgColor};
|
||||||
font-size:12px;
|
font-size:12px;
|
||||||
position: relative;
|
position: relative;
|
||||||
font-family: ${fontFamily};
|
|
||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
this.tagStyle = zeroStyle
|
this.tagStyle = zeroStyle
|
||||||
@ -333,4 +309,4 @@ export default {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -7,7 +7,7 @@ class TypewriterManager {
|
|||||||
// 配置选项
|
// 配置选项
|
||||||
this.options = {
|
this.options = {
|
||||||
typingSpeed: 50, // 打字速度(毫秒)
|
typingSpeed: 50, // 打字速度(毫秒)
|
||||||
cursorText: '', // 光标样式
|
cursorText: '<text class="typing-cursor">|</text>', // 光标样式
|
||||||
...options
|
...options
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ class TypewriterManager {
|
|||||||
if (this.isTyping) return;
|
if (this.isTyping) return;
|
||||||
|
|
||||||
this.isTyping = true;
|
this.isTyping = true;
|
||||||
// 不要在启动时重置displayedContent,而是从当前位置继续
|
this.displayedContent = "";
|
||||||
|
|
||||||
this._typeNextChar();
|
this._typeNextChar();
|
||||||
}
|
}
|
||||||
@ -75,7 +75,7 @@ class TypewriterManager {
|
|||||||
this.displayedContent.length + 1
|
this.displayedContent.length + 1
|
||||||
);
|
);
|
||||||
|
|
||||||
const displayContent = this.displayedContent;
|
const displayContent = this.displayedContent + this.options.cursorText;
|
||||||
|
|
||||||
// 调用内容更新回调
|
// 调用内容更新回调
|
||||||
if (this.onContentUpdate) {
|
if (this.onContentUpdate) {
|
||||||
@ -87,12 +87,10 @@ class TypewriterManager {
|
|||||||
this.onCharacterTyped(this.displayedContent);
|
this.onCharacterTyped(this.displayedContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确保最小延迟,避免定时器堆积
|
// 继续下一个字符
|
||||||
const delay = Math.max(this.options.typingSpeed, 30); // 设置最小延迟30ms
|
|
||||||
this.typewriterTimer = setTimeout(() => {
|
this.typewriterTimer = setTimeout(() => {
|
||||||
this._typeNextChar();
|
this._typeNextChar();
|
||||||
}, delay);
|
}, this.options.typingSpeed);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// 打字完成,移除光标
|
// 打字完成,移除光标
|
||||||
if (this.onContentUpdate) {
|
if (this.onContentUpdate) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user