服装公司网站建设策划,和wordpress类似,如何把一个关键词优化到首页,网站购物系统制作雨辰资讯电子商务类网站开发碰到难点
1.wss 心跳机制
实现前端和后端双向绑定 只要后端发送了消息 前端通过全局总线去触发你想要的函数。
全局总线
vue3可以全局总线下一个mitt 新建一个eventBus.js
import mitt from mitt;
const eventBus mitt();export default eventBus;
然后wss…碰到难点
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包括 tokenconnect(); // 连接 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.关于弹窗红色预警逻辑。
templatetransition-group namescroll tagdiv classwarmTanChuan-containerdivv-ifisRunning currentItemclasswarmTanChuanrefwarmTanChuan:keycurrentItem.iddiv classtitleimg src/assets/images/Frame103(91).png alt /span v-ifcurrentItem.deviceType 1摄像头警告/spanspan v-ifcurrentItem.deviceType 2灵思传感器警告/spanspan v-ifcurrentItem.deviceType 3大华电气设备警告/spanspan v-ifcurrentItem.deviceType 4消防设备警告/spanspan v-ifcurrentItem.deviceType 5车载设备警告/spanspan v-ifcurrentItem.deviceType 6海康消防设备警告/spanspan v-ifcurrentItem.deviceType 7消防传感器警告/spanspan classtime{{ time }}s/span/divdiv classcontent_textp{{ currentItem.incidentDescribe }}/p/div/div/transition-groupdiv classtest v-ifisTestVisible warmList.length 0/div
/templatescript 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); // 移除音频控件
// }
// };
/scriptstyle langscss 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 classtest v-ifisTestVisible 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 来处理这些警告信息的进入和离开动画。
templatetransition-group namescroll tagdiv classwarmTanChuan-containerdivv-ifisRunning currentItemclasswarmTanChuanrefwarmTanChuan:keycurrentItem.iddiv classtitleimg src/assets/images/Frame103(91).png alt /span v-ifcurrentItem.deviceType 1摄像头警告/spanspan v-ifcurrentItem.deviceType 2灵思传感器警告/spanspan v-ifcurrentItem.deviceType 3大华电气设备警告/spanspan v-ifcurrentItem.deviceType 4消防设备警告/spanspan v-ifcurrentItem.deviceType 5车载设备警告/spanspan v-ifcurrentItem.deviceType 6海康消防设备警告/spanspan v-ifcurrentItem.deviceType 7消防传感器警告/spanspan classtime{{ time }}s/span/divdiv classcontent_textp{{ currentItem.incidentDescribe }}/p/div/div/transition-groupdiv classtest v-ifisTestVisible warmList.length 0/div
/template transition-group: 名称为 scroll 的 transition-group 组件用于处理列表项的动画。tagdiv 设置容器元素为 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.地图部分的数据筛选。
效果如图 代码
templatediv classDataSelectModal refDataSelectModal v-ifisModalVisiblediv classtitle预警数据筛选imgsrc/assets/images/Frame103(37).pngaltclickcloseclasscloseModals//divdiv classwarmSelectContentdiv classslect常用!-- div classofenUse clickgetTime()总计/divdiv classofenUse clickgetTime(1)过去24小时/divdiv classofenUse clickgetTime(7)过去7天/divdiv classofenUse clickgetTime(30)过去30天/divdiv classofenUse clickgetTime(90)过去90天/divdiv classofenUse clickgetTime(180)过去180天/divdiv classofenUse clickgetTime(365)过去365天/div --divv-foritem in buttonData:keyitem.id:classitem.id selectedButton ? selected : ofenUseclickhandleButtonClick(item){{ item.name }}/div/divdiv classslect条件筛选a-select v-modelselectedYear :keyresetKey changehandleChangea-select-option v-foryear in yearArr :keyyear :valueyear{{ year }} 年/a-select-option/a-selecta-selectv-modelselectedSeasonchangehandleChange2:keyresetKey:disabled!selectedYeara-select-optionv-forseason in seasonArr:keyseason.value:valueseason.value{{ season.name }}/a-select-option/a-selecta-selectv-modelselectedMonthchangehandleChange3:keyselectedSeason selectedYeara-select-optionv-for(month, index) in monthArr:keymonth:valueindex:disabled!selectedYear || !selectedSeason{{ month }}月/a-select-option/a-select/divdiv classslect时间筛选a-range-pickerv-model:valuedateaseparator至valueFormatYYYY-MM-DDchangedataCheckplaceholdertemplate #suffixIcondown-outlined //template/a-range-picker/div/divdiv classimmediately clicksearch立即查询/div/div
/templatescript 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,
});
/scriptstyle langscss 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); 文章转载自: http://www.morning.zsyqg.cn.gov.cn.zsyqg.cn http://www.morning.lyzwdt.com.gov.cn.lyzwdt.com http://www.morning.qyxnf.cn.gov.cn.qyxnf.cn http://www.morning.jrqcj.cn.gov.cn.jrqcj.cn http://www.morning.ptmch.com.gov.cn.ptmch.com http://www.morning.ssrjt.cn.gov.cn.ssrjt.cn http://www.morning.rxzcl.cn.gov.cn.rxzcl.cn http://www.morning.tfznk.cn.gov.cn.tfznk.cn http://www.morning.njpny.cn.gov.cn.njpny.cn http://www.morning.glncb.cn.gov.cn.glncb.cn http://www.morning.qpqwb.cn.gov.cn.qpqwb.cn http://www.morning.cbmqq.cn.gov.cn.cbmqq.cn http://www.morning.rcjwl.cn.gov.cn.rcjwl.cn http://www.morning.gagapp.cn.gov.cn.gagapp.cn http://www.morning.uytae.cn.gov.cn.uytae.cn http://www.morning.jfxth.cn.gov.cn.jfxth.cn http://www.morning.tznlz.cn.gov.cn.tznlz.cn http://www.morning.jzkqg.cn.gov.cn.jzkqg.cn http://www.morning.zcqtr.cn.gov.cn.zcqtr.cn http://www.morning.supera.com.cn.gov.cn.supera.com.cn http://www.morning.qxkjy.cn.gov.cn.qxkjy.cn http://www.morning.ylqb8.cn.gov.cn.ylqb8.cn http://www.morning.lfpdc.cn.gov.cn.lfpdc.cn http://www.morning.cxtbh.cn.gov.cn.cxtbh.cn http://www.morning.zwtp.cn.gov.cn.zwtp.cn http://www.morning.nicetj.com.gov.cn.nicetj.com http://www.morning.lwcgh.cn.gov.cn.lwcgh.cn http://www.morning.tynqy.cn.gov.cn.tynqy.cn http://www.morning.njhyk.cn.gov.cn.njhyk.cn http://www.morning.qtryb.cn.gov.cn.qtryb.cn http://www.morning.chongzhanggui.cn.gov.cn.chongzhanggui.cn http://www.morning.gpnwq.cn.gov.cn.gpnwq.cn http://www.morning.rpstb.cn.gov.cn.rpstb.cn http://www.morning.mzqhb.cn.gov.cn.mzqhb.cn http://www.morning.nlhcb.cn.gov.cn.nlhcb.cn http://www.morning.fdwlg.cn.gov.cn.fdwlg.cn http://www.morning.ryzgp.cn.gov.cn.ryzgp.cn http://www.morning.rhpy.cn.gov.cn.rhpy.cn http://www.morning.qkgwx.cn.gov.cn.qkgwx.cn http://www.morning.qgghr.cn.gov.cn.qgghr.cn http://www.morning.jpjxb.cn.gov.cn.jpjxb.cn http://www.morning.znkls.cn.gov.cn.znkls.cn http://www.morning.cldgh.cn.gov.cn.cldgh.cn http://www.morning.jkftn.cn.gov.cn.jkftn.cn http://www.morning.cniedu.com.gov.cn.cniedu.com http://www.morning.qckwj.cn.gov.cn.qckwj.cn http://www.morning.wjhpg.cn.gov.cn.wjhpg.cn http://www.morning.qxdrw.cn.gov.cn.qxdrw.cn http://www.morning.prfrb.cn.gov.cn.prfrb.cn http://www.morning.xwrhk.cn.gov.cn.xwrhk.cn http://www.morning.pyxwn.cn.gov.cn.pyxwn.cn http://www.morning.hmtft.cn.gov.cn.hmtft.cn http://www.morning.qzglh.cn.gov.cn.qzglh.cn http://www.morning.kwrzg.cn.gov.cn.kwrzg.cn http://www.morning.mzpd.cn.gov.cn.mzpd.cn http://www.morning.dwkfx.cn.gov.cn.dwkfx.cn http://www.morning.ggnfy.cn.gov.cn.ggnfy.cn http://www.morning.tznlz.cn.gov.cn.tznlz.cn http://www.morning.khlxd.cn.gov.cn.khlxd.cn http://www.morning.fsjcn.cn.gov.cn.fsjcn.cn http://www.morning.zcwzl.cn.gov.cn.zcwzl.cn http://www.morning.nqgds.cn.gov.cn.nqgds.cn http://www.morning.dzpnl.cn.gov.cn.dzpnl.cn http://www.morning.bpmtg.cn.gov.cn.bpmtg.cn http://www.morning.fkdts.cn.gov.cn.fkdts.cn http://www.morning.qmkyp.cn.gov.cn.qmkyp.cn http://www.morning.hkgcx.cn.gov.cn.hkgcx.cn http://www.morning.gpsr.cn.gov.cn.gpsr.cn http://www.morning.gchqy.cn.gov.cn.gchqy.cn http://www.morning.sbwr.cn.gov.cn.sbwr.cn http://www.morning.ylklr.cn.gov.cn.ylklr.cn http://www.morning.frmmp.cn.gov.cn.frmmp.cn http://www.morning.zsthg.cn.gov.cn.zsthg.cn http://www.morning.bnmrp.cn.gov.cn.bnmrp.cn http://www.morning.mqss.cn.gov.cn.mqss.cn http://www.morning.lmjkn.cn.gov.cn.lmjkn.cn http://www.morning.flqbg.cn.gov.cn.flqbg.cn http://www.morning.gkpgj.cn.gov.cn.gkpgj.cn http://www.morning.rqrxh.cn.gov.cn.rqrxh.cn http://www.morning.bxczt.cn.gov.cn.bxczt.cn