微信链接网站怎么做百度官方网站入口
碰到难点
1.wss 心跳机制
实现前端和后端双向绑定 只要后端发送了消息 前端通过全局总线去触发你想要的函数。
全局总线
vue3可以全局总线下一个mitt
新建一个eventBus.js
import mitt from "mitt";
const eventBus = mitt();export default eventBus;
然后wss新建一个useWebSocket.js
import { ref } from "vue";
import eventBus from "../mixins/eventBus";// 连接状态
export const SocketStatus = {Connecting: "正在连接...", // 表示正在连接,这是初始状态。Connected: "连接已建立", // 表示连接已经建立。Disconnecting: "连接正在关闭", // 表示连接正在关闭。Disconnected: "连接已断开", // 表示连接已经关闭。
};const DEFAULT_OPTIONS = {url: "", // WebSocket URLheartBeatData: "", // 心跳数据heartBeatInterval: 60 * 1000, // 心跳间隔,单位 msreconnectInterval: 5 * 1000, // 断线重连间隔,单位 msmaxReconnectAttempts: 10, // 最大重连次数
};const SocketCloseCode = 1000;export default function useWebSocket(options = {}, onMessageCallback) {//onMessageCallback 处理回调函数 确保在收到消息时候调用const state = ref({options: { ...DEFAULT_OPTIONS, ...options },socket: null,heartBeatSendTimer: null, // 心跳发送定时器heartBeatTimeoutTimer: null, // 心跳超时定时器reconnectAttempts: 0,reconnectTimeout: null,});const status = ref(SocketStatus.Disconnected);// 连接 WebSocketconst connect = () => {disconnect(); // 断开之前的连接status.value = SocketStatus.Connecting;state.value.socket = new WebSocket(state.value.options.url);state.value.socket.onopen = (openEvent) => {console.log("socket连接:", openEvent);status.value = SocketStatus.Connected;startHeartBeat(); // 开始心跳};state.value.socket.onmessage = (msgEvent) => {console.log("socket消息:", msgEvent);if (typeof onMessageCallback === "function") {// onMessageCallback(); // 调用传入的回调函数// 广播消息
//在这边可以不用调用传入的函数 可以直接全局调用函数 懒得改了eventBus.emit("socketMessage", msgEvent.data);eventBus.emit("Messageaa");eventBus.emit("Messagebb");} else {console.error("getDate is not a function");}// if (typeof getDate === "function") {// getDate(); // 调用 getDate 函数// } else {// console.error("getDate is not a function");// }startHeartBeat(); // 收到消息时重新开始心跳};state.value.socket.onclose = (closeEvent) => {console.log("socket关闭:", closeEvent);status.value = SocketStatus.Disconnected;// 非正常关闭,尝试重连if (closeEvent.code !== SocketCloseCode) {reconnect();}};state.value.socket.onerror = (errEvent) => {console.log("socket报错:", errEvent);status.value = SocketStatus.Disconnected;reconnect(); // 连接失败,尝试重连};};// 断开 WebSocketconst disconnect = () => {// 如果 WebSocket 实例存在且处于开放或连接中的状态,则关闭连接。if (state.value.socket && (state.value.socket.OPEN || state.value.socket.CONNECTING)) {console.log("socket断开连接");status.value = SocketStatus.Disconnecting;state.value.socket.close(SocketCloseCode, "normal closure");state.value.socket = null;stopHeartBeat(); // 停止心跳stopReconnect(); // 停止重连}};// 开始心跳检测const startHeartBeat = () => {stopHeartBeat(); // 先清除之前的定时器state.value.heartBeatSendTimer = setTimeout(() => {if (status.value === SocketStatus.Connected) {state.value.socket.send(state.value.options.heartBeatData);console.log("socket心跳发送:", state.value.options.heartBeatData);}// 心跳超时state.value.heartBeatTimeoutTimer = setTimeout(() => {console.log("心跳超时,关闭连接");state.value.socket.close(4444, "heart timeout");}, state.value.options.heartBeatInterval);}, state.value.options.heartBeatInterval);};// 停止心跳检测const stopHeartBeat = () => {if (state.value.heartBeatSendTimer) {clearTimeout(state.value.heartBeatSendTimer);state.value.heartBeatSendTimer = null;}if (state.value.heartBeatTimeoutTimer) {clearTimeout(state.value.heartBeatTimeoutTimer);state.value.heartBeatTimeoutTimer = null;}};// 重连机制const reconnect = () => {// reconnect:如果连接状态不是 Connected 或 Connecting,并且重连尝试次数小于最大值,则尝试重连。if (status.value === SocketStatus.Connected || status.value === SocketStatus.Connecting) {return;}stopHeartBeat(); // 停止心跳if (state.value.reconnectAttempts < state.value.options.maxReconnectAttempts) {console.log("socket重连:", state.value.reconnectAttempts);// 重连间隔,5秒起步,下次递增1秒const interval = Math.max(state.value.options.reconnectInterval, state.value.reconnectAttempts * 1000);console.log("间隔时间:", interval);state.value.reconnectTimeout = setTimeout(() => {if (status.value !== SocketStatus.Connected && status.value !== SocketStatus.Connecting) {connect();}}, interval);state.value.reconnectAttempts += 1;} else {status.value = SocketStatus.Disconnected;stopReconnect(); // 停止重连}};// 停止重连const stopReconnect = () => {if (state.value.reconnectTimeout) {clearTimeout(state.value.reconnectTimeout);state.value.reconnectTimeout = null;}};return {connect,disconnect,status,};
}
重点是这步
state.value.socket.onmessage = (msgEvent) => {console.log("socket消息:", msgEvent);if (typeof onMessageCallback === "function") {// onMessageCallback(); // 调用传入的回调函数// 广播消息
//在这边可以不用调用传入的函数 可以直接全局调用函数 懒得改了eventBus.emit("socketMessage", msgEvent.data);eventBus.emit("Messageaa");eventBus.emit("Messagebb");} else {console.error("getDate is not a function");}
然后在自己的组件
import useWebSocket, { SocketStatus } from "@/mixins/useWebSocket";
import eventBus from "@/mixins/eventBus";onMounted(() => {eventBus.on("tenDays", getDate);eventBus.on("socketMessage", getDate); // 监听 WebSocket 消息事件getDate(); // 在组件初始化时调用 getDategetUser(); //为了链接wss});
onUnmounted(() => {eventBus.off("tenDays", getDate);eventBus.off("socketMessage", getDate);disconnect(); // 断开 WebSocket 连接
});
我传函数进去了其实不用的 懒得改了 因为 我要接收到数据 好几个函数一起被触发 所以全局总线比较好
const ID = ref("");
const getUser = () => {userInfo().then((res) => {// console.log("用户res", res);localStorage.setItem("userInof", JSON.stringify(res.data.sysUser));ID.value = res.data.sysUser.id;if (ID.value) {console.log("连接WebSocket");// setLoginCookie(); // 设置登录 Cookie(包括 token)connect(); // 连接 WebSocket} // 组件挂载时连接 WebSocket});
};const { connect, disconnect, status } = useWebSocket({url: computed(() => {// const token = Cookie.get("Authorization") || "";return ID.value? `wss://www.tbaowl.com:9992/ws/mini/websocket/${ID.value}`: "";}), // 替换为实际的 WebSocket URLheartBeatData: "ping", // 心跳数据heartBeatInterval: 30000, // 心跳间隔,30秒reconnectInterval: 5000, // 重连间隔,5秒maxReconnectAttempts: 5, // 最大重连次数},getDate
);
2.关于弹窗红色预警,逻辑。
<template><transition-group name="scroll" tag="div" class="warmTanChuan-container"><divv-if="isRunning && currentItem"class="warmTanChuan"ref="warmTanChuan":key="currentItem.id"><div class="title"><img src="@/assets/images/Frame103(91).png" alt="" /><span v-if="currentItem.deviceType == 1">摄像头警告:</span><span v-if="currentItem.deviceType == 2">灵思传感器警告:</span><span v-if="currentItem.deviceType == 3">大华电气设备警告:</span><span v-if="currentItem.deviceType == 4">消防设备警告:</span><span v-if="currentItem.deviceType == 5">车载设备警告:</span><span v-if="currentItem.deviceType == 6">海康消防设备警告:</span><span v-if="currentItem.deviceType == 7">消防传感器警告:</span><span class="time">{{ time }}s</span></div><div class="content_text"><p>{{ currentItem.incidentDescribe }}</p></div></div></transition-group><div class="test" v-if="isTestVisible && warmList.length > 0"></div>
</template><script setup>
import { ref, onMounted, watch, computed, onUnmounted, nextTick } from "vue";
import { warmEvents } from "@/api/api.js";
import dayjs from "dayjs";
import useWebSocket, { SocketStatus } from "@/mixins/useWebSocket";
import { getSystemData, userInfo } from "@/api/api";
import eventBus from "@/mixins/eventBus";
const warmList = ref([]);
const queue = ref([]); // 用于存储接收到的警告数据的队列const currentIndex = ref(0);
const time = ref(30);
const isRunning = ref(false);
const isTestVisible = ref(true);let audio = new Audio(require("@/assets/warm.mp3"));
audio.hidden = true; // 隐藏音频控件
document.body.appendChild(audio); // 将音频控件添加到页面中let interval;
onMounted(() => {eventBus.on("socketMessage", handleMessage);audio.addEventListener("ended", handleAudioEnded);
});onUnmounted(() => {eventBus.off("socketMessage", handleMessage);clearInterval(interval); // 清除1分钟的定时器clearInterval(countDown); // 清除倒计时定时器audio.pause();document.body.removeChild(audio); // 移除音频控件
});const getDate = () => {const time = new Date();const endTime = dayjs(time).format("YYYY-MM-DD HH:mm:ss");const startTime = dayjs(time).subtract(1, "days").format("YYYY-MM-DD HH:mm:ss");const equipWarnPageDTO = {startTime: startTime,endTime: endTime,};const page = {size: 999,};const obj = Object.assign(equipWarnPageDTO, page);warmEvents(obj).then((res) => {if (res.code == 0) {const records = res.data?.records ?? [];warmList.value = [...warmList.value, ...records]; // 将新数据添加到 warmList 末尾nextTick(() => {if (warmList.value.length > 0) {playAudio();}});}});
};const handleMessage = (msg) => {const data = JSON.parse(msg);queue.value.push(data); // 将新数据添加到队列中if (!isRunning.value && !currentItem.value) {// 如果当前没有正在播放的警告信息,开始播放showNextItem();}
};const ID = ref("");
const getUser = () => {userInfo().then((res) => {localStorage.setItem("userInof", JSON.stringify(res.data.sysUser));ID.value = res.data.sysUser.id;if (ID.value) {connect(); // 连接 WebSocket}});
};const { connect, disconnect, status } = useWebSocket({url: computed(() => {return ID.value? `wss://www.tbaowl.com:9992/ws/mini/websocket/${ID.value}`: "";}),heartBeatData: "ping", // 心跳数据heartBeatInterval: 30000, // 心跳间隔,30秒reconnectInterval: 5000, // 重连间隔,5秒maxReconnectAttempts: 5, // 最大重连次数},handleMessage
);let countDown;
const startCountDown = () => {if (countDown) {clearInterval(countDown); // 清除之前的倒计时定时器}countDown = setInterval(() => {if (isRunning.value) {time.value -= 1;if (time.value <= 0) {clearInterval(countDown);handleAudioEnded();}}}, 1000);
};const showNextItem = () => {if (queue.value.length > 0) {warmList.value.push(queue.value.shift()); // 从队列中取出一条数据并添加到warmListconsole.log("queue.value", queue.value);console.log("warmList.value2222", warmList.value);isRunning.value = true; // 标记为正在播放isTestVisible.value = true;time.value = 30; // 重置时间playAudio();startCountDown(); // 开始倒计时} else {isRunning.value = false; // 如果队列为空,停止播放isTestVisible.value = false;}
};watch(currentIndex, () => {if (isRunning.value) {if (countDown) {clearInterval(countDown);startCountDown();}}
});const currentItem = computed(() => {return warmList.value.length > 0 ? warmList.value[currentIndex.value] : null;
});const playAudio = () => {audio.currentTime = 0;audio.play().catch((error) => {console.error("Error playing audio:", error);});// 设置定时器,10秒后停止音频setTimeout(() => {audio.pause();}, 10000); // 10000毫秒 = 10秒
};
const handleAudioEnded = () => {if (queue.value.length > 0) {currentIndex.value++;showNextItem();} else {isRunning.value = false; // 没有更多数据,停止播放isTestVisible.value = false;audio.pause();document.body.removeChild(audio); // 移除音频控件}
};// const handleAudioEnded = () => {
// if (queue.value.length > 0) {
// // 如果队列中还有未播放的数据,播放下一条
// showNextItem();
// } else {
// // 如果没有更多数据,停止播放
// isRunning.value = false;
// audio.pause();
// document.body.removeChild(audio); // 移除音频控件
// }
// };
</script><style lang="scss" scoped>
.warmTanChuan-container {position: absolute;top: 25%;left: 35%;transform: translate(-50%, -50%);z-index: 999;
}.warmTanChuan {width: 327px;color: #ffffff;.title {height: 40px;line-height: 40px;background: url("@/assets/images/jbbg.png") no-repeat;background-size: 100% 100%;font-weight: bold;padding: 0 10px;font-size: 14px;img {width: 20px;height: 20px;margin: 10px;}.time {float: right;}}.content_text {background-color: #3a0e0b;border: 2px solid #be4b44;padding: 10px;font-size: 14px;position: relative;}
}.scroll-enter-active,
.scroll-leave-active {transition: transform 1s;
}
.scroll-enter {transform: translateY(100%);
}
.scroll-leave-to {transform: translateY(-100%);
}
.test {width: 972px;height: calc(100vh - 440px);background: url("@/assets/images/image2/warm.png") no-repeat;background-size: 100%;position: absolute;top: 96px;left: 50%;transform: translateX(-50%);animation: blink 1s infinite; // 添加闪烁动画
}
// 闪烁动画
@keyframes blink {0%,100% {opacity: 1;}50% {opacity: 0;}
}
</style>
难点:
1.一个是关于弹窗如何控制30s显示然后下一个显示,同时伴有警告声音10s消失?
2.数据如果是一个一个传给你或者是一次性多个传给你,怎么办?
3.闪烁动画怎么做?
先处理第三个问题:首先闪烁动画是一个比较图片的盒子,实现一闪一闪的效果
如下
样式可以这样写
闪烁由数据的长度和isTestVisible共同决定
<div class="test" v-if="isTestVisible && warmList.length > 0"></div>
.test {width: 972px;height: calc(100vh - 440px);background: url("@/assets/images/image2/warm.png") no-repeat;background-size: 100%;position: absolute;top: 96px;left: 50%;transform: translateX(-50%);animation: blink 1s infinite; // 添加闪烁动画
}
// 闪烁动画
@keyframes blink {0%, // 动画开始时100% { // 动画结束时opacity: 1; // 元素完全可见 (不透明)}50% { // 动画进行到一半时opacity: 0; // 元素完全不可见 (透明)}
}
好 现在解决第一个问题。如何让他实现关于弹窗如何控制30s显示然后下一个显示,同时伴有警告声音10s消失。这边就会说明刚才isTestVisible是什么东西了。
首先还是从样式transition-group
来处理这些警告信息的进入和离开动画。
<template><transition-group name="scroll" tag="div" class="warmTanChuan-container"><divv-if="isRunning && currentItem"class="warmTanChuan"ref="warmTanChuan":key="currentItem.id"><div class="title"><img src="@/assets/images/Frame103(91).png" alt="" /><span v-if="currentItem.deviceType == 1">摄像头警告:</span><span v-if="currentItem.deviceType == 2">灵思传感器警告:</span><span v-if="currentItem.deviceType == 3">大华电气设备警告:</span><span v-if="currentItem.deviceType == 4">消防设备警告:</span><span v-if="currentItem.deviceType == 5">车载设备警告:</span><span v-if="currentItem.deviceType == 6">海康消防设备警告:</span><span v-if="currentItem.deviceType == 7">消防传感器警告:</span><span class="time">{{ time }}s</span></div><div class="content_text"><p>{{ currentItem.incidentDescribe }}</p></div></div></transition-group><div class="test" v-if="isTestVisible && warmList.length > 0"></div>
</template>
-
transition-group
:- 名称为
scroll
的transition-group
组件,用于处理列表项的动画。 tag="div"
设置容器元素为div
。- 内部包含一个动态渲染的
div
元素,用于显示当前警告信息。
- 名称为
-
div
:- 根据
isRunning
和currentItem
的值来决定是否显示警告信息。 - 使用
:key
绑定唯一的标识符,以便transition-group
能够正确跟踪元素的变化。
- 根据
-
.warmTanChuan-container
:- 定位样式,使警告信息居中显示。
-
.test
:- 一个用于测试的
div
元素,当isTestVisible
为真时显示,带有闪烁动画。
- 一个用于测试的
第一步肯定是拿到数据 如果没有正在播放的数据才可以播
import { ref, onMounted, watch, computed, onUnmounted, nextTick } from "vue";
import { warmEvents } from "@/api/api.js";
import dayjs from "dayjs";
import useWebSocket, { SocketStatus } from "@/mixins/useWebSocket";
import { getSystemData, userInfo } from "@/api/api";
import eventBus from "@/mixins/eventBus";
const warmList = ref([]);
const queue = ref([]); // 用于存储接收到的警告数据的队列const currentIndex = ref(0);
const time = ref(30);
const isRunning = ref(false);
const isTestVisible = ref(true);let audio = new Audio(require("@/assets/warm.mp3"));
audio.hidden = true; // 隐藏音频控件
document.body.appendChild(audio); // 将音频控件添加到页面中const currentItem = computed(() => {return warmList.value.length > 0 ? warmList.value[currentIndex.value] : null;
});const handleMessage = (msg) => {const data = JSON.parse(msg);queue.value.push(data); // 将新数据添加到队列中if (!isRunning.value && !currentItem.value) {// 如果当前没有正在播放的警告信息,开始播放showNextItem();}
};
下一条给warnList添加数据 同时queue移除
const showNextItem = () => {if (queue.value.length > 0) {warmList.value.push(queue.value.shift()); // 从队列中取出一条数据并添加到warmListconsole.log("queue.value", queue.value);console.log("warmList.value2222", warmList.value);isRunning.value = true; // 标记为正在播放isTestVisible.value = true;time.value = 30; // 重置时间playAudio();//播放声音startCountDown(); // 开始倒计时} else {isRunning.value = false; // 如果队列为空,停止播放isTestVisible.value = false;}
};
播放声音
const playAudio = () => {audio.currentTime = 0;audio.play().catch((error) => {console.error("Error playing audio:", error);});// 设置定时器,10秒后停止音频setTimeout(() => {audio.pause();}, 10000); // 10000毫秒 = 10秒
};
开始倒计时
const startCountDown = () => {if (countDown) {clearInterval(countDown); // 清除之前的倒计时定时器}countDown = setInterval(() => {if (isRunning.value) {time.value -= 1;if (time.value <= 0) {clearInterval(countDown);handleAudioEnded();}}}, 1000);
};
const handleAudioEnded = () => {if (queue.value.length > 0) {currentIndex.value++;showNextItem();} else {isRunning.value = false; // 没有更多数据,停止播放isTestVisible.value = false;audio.pause();document.body.removeChild(audio); // 移除音频控件}
};
watch(currentIndex, () => {if (isRunning.value) {if (countDown) {clearInterval(countDown);startCountDown();}}
});
因为warmList肯定是有数据的所以再加一个条件isTestVisible来控制闪烁动画。
3.地图部分的数据筛选。
效果如图
代码:
<template><div class="DataSelectModal" ref="DataSelectModal" v-if="isModalVisible"><div class="title">预警数据筛选<imgsrc="@/assets/images/Frame103(37).png"alt=""@click="close"class="closeModals"/></div><div class="warmSelectContent"><div class="slect">常用:<!-- <div class="ofenUse" @click="getTime()">总计</div><div class="ofenUse" @click="getTime(1)">过去24小时</div><div class="ofenUse" @click="getTime(7)">过去7天</div><div class="ofenUse" @click="getTime(30)">过去30天</div><div class="ofenUse" @click="getTime(90)">过去90天</div><div class="ofenUse" @click="getTime(180)">过去180天</div><div class="ofenUse" @click="getTime(365)">过去365天</div> --><divv-for="item in buttonData":key="item.id":class="item.id === selectedButton ? 'selected' : 'ofenUse'"@click="handleButtonClick(item)">{{ item.name }}</div></div><div class="slect">条件筛选:<a-select v-model="selectedYear" :key="resetKey" @change="handleChange"><a-select-option v-for="year in yearArr" :key="year" :value="year">{{ year }} 年</a-select-option></a-select><a-selectv-model="selectedSeason"@change="handleChange2":key="resetKey":disabled="!selectedYear"><a-select-optionv-for="season in seasonArr":key="season.value":value="season.value">{{ season.name }}</a-select-option></a-select><a-selectv-model="selectedMonth"@change="handleChange3":key="selectedSeason + selectedYear"><a-select-optionv-for="(month, index) in monthArr":key="month":value="index":disabled="!selectedYear || !selectedSeason">{{ month }}月</a-select-option></a-select></div><div class="slect">时间筛选:<a-range-pickerv-model:value="datea"separator="至"valueFormat="YYYY-MM-DD"@change="dataCheck"placeholder=""><template #suffixIcon><down-outlined /></template></a-range-picker></div></div><div class="immediately" @click="search">立即查询</div></div>
</template><script setup>
import { ref, watch, onMounted, computed, reactive, nextTick } from "vue";
import { DownOutlined } from "@ant-design/icons-vue";
const emit = defineEmits(["update:start-time", "update:end-time"]);
import dayjs from "dayjs";
// const emit = defineEmits(["close"]);
const DataSelectModal = ref(null);
const isModalVisible = ref(false);
const form = reactive({startTime: "",endDate: "",
});
const time = new Date().getFullYear();
const yearArr = ref([]);
const startTime = ref("");
const endTime = ref("");
const selectedMonth = ref("");
const selectedSeason = ref("");
const selectedYear = ref("");import eventBus from "@/mixins/eventBus";
import { message } from "ant-design-vue";const seasonArr = [{value: 1,name: "第一季度",},{value: 2,name: "第二季度",},{value: 3,name: "第三季度",},{value: 4,name: "第四季度",},
];
const monthArr = ref([]);const initializeYears = () => {var i;for (i = 2024; i <= time; i++) {yearArr.value.push(i);}
};
const datea = ref([]);const buttonData = [{ id: 1, name: "总计", time: 0 },{ id: 2, name: "过去24小时", time: 1 },{ id: 3, name: "过去7天", time: 7 },{ id: 4, name: "过去30天", time: 30 },{ id: 5, name: "过去90天", time: 90 },{ id: 6, name: "过去180天", time: 180 },{ id: 7, name: "过去365天", time: 365 },
];
// 当前选中的按钮ID
const selectedButton = ref(0);
const resetKey = ref(0);
const changeButton = ref("");onMounted(() => {nextTick(() => {// selectedButton.value = 1;});initializeYears();// getTime();
});
// 处理按钮点击事件
const handleButtonClick = (date) => {selectedYear.value = null;selectedSeason.value = null;selectedMonth.value = null;datea.value = [];selectedButton.value = date.id;resetKey.value++; // 触发组件重新渲染// const resetKey = selectedYear.value + "_" + selectedSeason.value;nextTick(() => {console.log(" selectedYear.value", selectedYear.value);console.log(" selectedSeason.value", selectedSeason.value);console.log(" selectedMonth.value", selectedMonth.value);});getTime(date.time);changeButton.value = date.name;
};// const date = computed({
// get() {
// // if (!form.startTime || !form.endTime)
// // return [
// // dayjs().subtract(3, "day").format("YYYY-MM-DD HH:mm:ss"),
// // dayjs().format("YYYY-MM-DD HH:mm:ss"),
// // ];
// return [
// dayjs().format("YYYY-MM-DD HH:mm:ss"),
// dayjs().format("YYYY-MM-DD HH:mm:ss"),
// ];
// },
// set(date) {
// console.log("date: ", date);
// selectedButton.value = null;
// startTime.value = date[0];
// endTime.value = date[1];
// },
// });const showModal = () => {isModalVisible.value = true;selectedSeason.value = null;selectedMonth.value = null;datea.value = null;selectedYear.value = null;startTime.value = null;endTime.value = null;// selectedButton.value = 1;changeButton.value = "";
};
const close = () => {isModalVisible.value = false;selectedMonth.value = "";
};const getTime = (data) => {if (data) {startTime.value = dayjs().subtract(data, "day").format("YYYY-MM-DD HH:mm:ss");endTime.value = dayjs().format("YYYY-MM-DD HH:mm:ss");console.log("startDate: ", startTime.value);console.log("endTime: ", endTime.value);} else {startTime.value = null;endTime.value = null;}
};const handleChange = (value) => {console.log("value", value);selectedYear.value = value;selectedSeason.value = null;selectedMonth.value = null;selectedButton.value = null;datea.value = [];updateStartEndTime();changeButton.value = "条件筛选";// console.log("queryParam", queryParam.value.deviceType);
};
const handleChange2 = (value) => {console.log("value", value);selectedSeason.value = value;selectedButton.value = null;selectedMonth.value = null; // 先清空月份monthArr.value = []; // 清空月份数组switch (Number.parseInt(value)) {case 1:monthArr.value = [1, 2, 3];break;case 2:monthArr.value = [4, 5, 6];break;case 3:monthArr.value = [7, 8, 9];break;case 4:monthArr.value = [10, 11, 12];break;}updateStartEndTime();
};const handleChange3 = (value) => {console.log("value", value);selectedMonth.value = value;selectedButton.value = null;updateStartEndTime();// console.log("queryParam", queryParam.value.deviceType);
};const updateStartEndTime = () => {console.log(111);if (selectedYear.value && selectedSeason.value) {// 如果选择了年和季度const seasonStartMonth = monthArr.value[0] - 1;const seasonEndMonth = monthArr.value[2];startTime.value = dayjs(new Date(selectedYear.value, seasonStartMonth, 1)).startOf("month").format("YYYY-MM-DD HH:mm:ss");endTime.value = dayjs(new Date(selectedYear.value, seasonEndMonth, 0)).endOf("month").format("YYYY-MM-DD HH:mm:ss");console.log("startDate: ", startTime.value);console.log("endTime: ", endTime.value);} else if (selectedYear.value &&selectedSeason.value &&selectedMonth.value !== "") {// 如果选择了年、季度和月份// 如果 monthArr.value 是 ['01', '02', '03'] 并且 selectedMonth.value 是 1,那么 month 的值将会是 2。// 如果 monthArr.value 是 [1, 2, 3],并且 selectedMonth.value 是 1,那么 month 的值将会是 2。parseInt(2, 10) 仍然返回 2。const month = parseInt(monthArr.value[selectedMonth.value], 10);startTime.value = dayjs(new Date(selectedYear.value, month - 1, 1)).startOf("month").format("YYYY-MM-DD HH:mm:ss");endTime.value = dayjs(new Date(selectedYear.value, month, 0)).endOf("month").format("YYYY-MM-DD HH:mm:ss");console.log("startDate: ", startTime.value);console.log("endTime: ", endTime.value);} else if (selectedYear.value && selectedSeason.value) {// 如果选择了年和季度const seasonStartMonth = monthArr.value[0] - 1;const seasonEndMonth = monthArr.value[2];startTime.value = dayjs(new Date(selectedYear.value, seasonStartMonth, 1)).startOf("month").format("YYYY-MM-DD HH:mm:ss");endTime.value = dayjs(new Date(selectedYear.value, seasonEndMonth, 0)).endOf("month").format("YYYY-MM-DD HH:mm:ss");console.log("startDate: ", startTime.value);console.log("endTime: ", endTime.value);} else if (selectedYear.value) {// 如果只选择了年startTime.value = dayjs(new Date(selectedYear.value, 0, 1)).startOf("year").format("YYYY-MM-DD HH:mm:ss");endTime.value = dayjs(new Date(selectedYear.value, 11, 31)).endOf("year").format("YYYY-MM-DD HH:mm:ss");console.log("startDate: ", startTime.value);console.log("endTime: ", endTime.value);}
};const search = () => {if (!changeButton.value) {message.error("请选择查询时间范围");returm;} else {emit("update:start-time", startTime.value);emit("update:end-time", endTime.value);emit("changeButton", changeButton.value);console.log("查询时间范围:", startTime.value, endTime.value);close();}
};const dataCheck = (data) => {console.log("Range picker clicked:", data);startTime.value = data[0];endTime.value = data[1];resetKey.value++; // 触发组件重新渲染console.log("startDate: ", startTime.value);console.log("endTime: ", endTime.value);selectedSeason.value = null;selectedMonth.value = null;selectedYear.value = null;selectedButton.value = null;changeButton.value = "时间筛选";// 在这里处理点击事件
};// onChangeTime (data) {
// if (data.length != 0) {
// this.queryParam.startDate = data[0];
// this.queryParam.endDate = data[1];
// } else {
// delete this.queryParam.startDate;
// delete this.queryParam.endDate;
// }
// },defineExpose({// close,showModal,
});
</script><style lang="scss" scoped>
.DataSelectModal {position: relative;font-size: 14px;width: 800px;height: 296px;color: #ffffff;position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);z-index: 999;background: url("@/assets/images/image2/selectbox.png") no-repeat;background-size: 100%;.title {height: 40px;line-height: 40px;padding-left: 20px;font-size: 14px;.closeModals {float: right;width: 24px;height: 24px;margin: 5px;cursor: pointer;}}.warmSelectContent {padding: 20px;.slect {display: flex;align-items: center;margin-bottom: 20px;.ofenUse {font-size: 12px;background: #042931;border-radius: 4px;border: 1px solid #0a8fab;margin-right: 20px;padding: 5px;cursor: pointer;}.selected {font-size: 12px;background: #148aa5;border-radius: 4px;border: 1px solid #0a8fab;margin-right: 20px;padding: 5px;cursor: pointer;}}}.immediately {position: absolute;bottom: 10%;left: 50%;transform: translateX(-50%);width: 104px;height: 44px;line-height: 44px;text-align: center;background: #134451;border-radius: 4px;border: 1px solid #165a6b;cursor: pointer;}:deep(.ant-select-selector) {background: #134451 !important;border: 1px solid #165a6b !important;color: #fff;width: 120px !important;line-height: 35px !important;height: 35px !important;}:deep(.ant-select-selection-item) {line-height: 35px !important;}
}
:deep(.ant-picker-range) {width: 300px !important;
}
</style>
关于下拉框无法置空
这边的难点是关于下拉框是三级联动同时3种筛选选择其中一种的时候,其他两种都必须置为空。难就难在下拉框无法置空踩的坑。后面发现双向绑定还是无法置空 可以重置他们的key
有几个地方需要理解下
关于十进制的用法
// 如果 monthArr.value 是 ['01', '02', '03'] 并且 selectedMonth.value 是 1,那么 month 的值将会是 2。// 如果 monthArr.value 是 [1, 2, 3],并且 selectedMonth.value 是 1,那么 month 的值将会是 2。parseInt(2, 10) 仍然返回 2。const month = parseInt(monthArr.value[selectedMonth.value], 10);
关于一个月的开始和尾巴
// 创建一个日期对象,表示selectedYear.value年seasonStartMonth月的第一天。startTime.value = dayjs(new Date(selectedYear.value, seasonStartMonth, 1)).startOf("month").format("YYYY-MM-DD HH:mm:ss");// 这里0表示该月的最后一天。endTime.value = dayjs(new Date(selectedYear.value, seasonEndMonth, 0)).endOf("month").format("YYYY-MM-DD HH:mm:ss");