From 58a343b3b9cf80c5d778fbe28672f198b366ea0c Mon Sep 17 00:00:00 2001
From: zhangchen <1652267879@qq.com>
Date: 星期六, 26 七月 2025 14:14:36 +0800
Subject: [PATCH] ID1625-定时任务修改完成
---
src/views/mobile/bedsideAuxiliaryScreen/components/ScheduledTask.vue | 560 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 560 insertions(+), 0 deletions(-)
diff --git a/src/views/mobile/bedsideAuxiliaryScreen/components/ScheduledTask.vue b/src/views/mobile/bedsideAuxiliaryScreen/components/ScheduledTask.vue
index e69de29..d931077 100644
--- a/src/views/mobile/bedsideAuxiliaryScreen/components/ScheduledTask.vue
+++ b/src/views/mobile/bedsideAuxiliaryScreen/components/ScheduledTask.vue
@@ -0,0 +1,560 @@
+<template>
+ <div class="scheduled-task-container">
+ <el-dialog
+ v-model="isShow"
+ center
+ title="定时任务"
+ width="80%"
+ :show-close="false"
+ class="scheduled-task-dialog"
+ top="0"
+ :destroy-on-close="true"
+ :close-on-click-modal="false"
+ >
+ <template #header>
+ <div class="scheduled-task-header">
+ <span class="header-title">{{ title }}</span>
+ <img
+ :src="closeImg"
+ class="header-close"
+ @click="handleCancel"
+ alt=""
+ />
+ </div>
+ </template>
+ <div class="scheduled-task-content" v-loading="loading">
+ <div class="content-left">
+ <div class="content-left-date">
+ <TimePicker v-model="timeValue" />
+ <!-- 创建的情况 -->
+ <div class="date-btn" v-if="type === 0">
+ <div
+ v-for="(item, index) in dateOptions"
+ :key="index"
+ class="my-button date"
+ @click="onAddMinutesClick(item)"
+ >
+ {{ item.label }}
+ </div>
+ </div>
+ <!-- 查看的情况 -->
+ <div class="countdown" v-else>
+ <div class="countdown-btn">
+ 剩余时间:{{ formattedCountdown }}
+ </div>
+ </div>
+ </div>
+ <div class="content-left-stereotyped-writing">
+ <div class="stereotyped-writing">
+ <input
+ v-model.trim="taskName"
+ type="text"
+ :disabled="isInpDisabled"
+ class="stereotyped-writing-input"
+ placeholder="请输入自定义内容"
+ />
+ </div>
+ <div class="stereotyped-writing-list">
+ <div
+ v-for="(item, index) in taskOptions"
+ :key="index"
+ class="my-button list-item"
+ @click="onStereotypedWritingClick(item)"
+ :class="taskItemCheck === item.value ? 'check' : ''"
+ >
+ {{ item.label }}
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="content-right">
+ <div class="stop-btn" v-if="type === 1" @click="stopTask">
+ 紧急停止
+ </div>
+ </div>
+ </div>
+ <template #footer>
+ <div class="my-button cancel" @click="handleCancel">取消</div>
+ <div
+ class="my-button confirm"
+ :class="loading ? 'cancel' : ''"
+ @click="handleConfirm"
+ >
+ 确认
+ </div>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script lang="ts" setup>
+import { computed, onUnmounted, ref, watch } from "vue";
+import dayjs from "dayjs";
+import { setTimeoutAlert, stopTimeoutAlert } from "@/utils/httpApi";
+import { useBedsideAuxiliaryScreenStore } from "@/store/bedsideAuxiliaryScreen";
+import { taskOptions } from "@/store/type/task.type";
+import type { TaskItem } from "@/store/type/task.type";
+
+// @ts-ignore
+import TimePicker from "./TimePicker.vue";
+
+import closeImg from "@/img/close.png";
+import { ElMessage, ElMessageBox } from "element-plus";
+
+interface DateItem {
+ label: string;
+ value: number;
+}
+
+const bedsideAuxiliaryScreenStore = useBedsideAuxiliaryScreenStore();
+
+const isShow = ref(false);
+const type = ref<0 | 1>(0); // 0 创建 | 1 查看
+const taskName = ref(""); // 任务名称
+const isInpDisabled = ref(false); // 输入框是否禁用
+const timeValue = ref("");
+const detaCheck = ref<number | null>(); // 这个是判断时间按钮的
+const loading = ref(false);
+
+const dateOptions = ref<DateItem[]>([
+ { label: "15分钟", value: 15 },
+ { label: "30分钟", value: 30 },
+ { label: "45分钟", value: 45 },
+ { label: "60分钟", value: 60 },
+]);
+
+let timer: ReturnType<typeof setInterval> | null = null;
+const countdown = ref(null); // 定时任务的倒计时
+
+const formattedCountdown = computed(() => {
+ if (countdown.value == null || countdown.value <= 0) return "0s";
+
+ const minutes = Math.floor(countdown.value / 60);
+ const seconds = countdown.value % 60;
+
+ if (minutes > 0) {
+ return `${minutes}m ${seconds}s`;
+ } else {
+ return `${seconds}s`;
+ }
+});
+
+const title = computed(() => {
+ return type.value ? "查看定时任务" : "创建定时任务";
+});
+
+const taskItemCheck = computed(() => {
+ return taskOptions.find((e) => e.value === taskName.value)?.value || "";
+});
+
+watch(
+ () => bedsideAuxiliaryScreenStore.taskData?.[0]?.countdown,
+ (val) => {
+ if (typeof val === "number") {
+ startCountdown(val);
+ } else {
+ clearTimer();
+ }
+ },
+ { immediate: true }
+);
+
+watch(countdown, (newVal) => {
+ if (newVal <= 0) {
+ loading.value = false;
+ handleCancel();
+ // ElMessage.warning('当前任务发生变化,请重新设置');
+ }
+});
+
+// 清除定时器函数
+function clearTimer() {
+ if (timer) {
+ clearInterval(timer);
+ timer = null;
+ }
+}
+
+// 启动新的倒计时
+function startCountdown(seconds: number) {
+ clearTimer();
+ countdown.value = seconds;
+
+ timer = setInterval(() => {
+ if (countdown.value > 0) {
+ countdown.value -= 1;
+ } else {
+ clearTimer();
+ }
+ }, 1000);
+}
+
+const openDialog = () => {
+ isShow.value = true;
+ if (
+ !bedsideAuxiliaryScreenStore.taskData ||
+ bedsideAuxiliaryScreenStore.taskData.length <= 0
+ ) {
+ const time = dayjs();
+ timeValue.value = time.format("HH:mm");
+ detaCheck.value = null;
+ isInpDisabled.value = false;
+ taskName.value = "";
+ type.value = 0;
+ } else {
+ const taskDataItem = bedsideAuxiliaryScreenStore.taskData[0];
+ timeValue.value = dayjs(taskDataItem.taskDate).format("HH:mm");
+ // 查是否是定型文的内容
+ const writing = taskOptions.find((e) => e.value === taskDataItem.taskName);
+ if (writing) {
+ taskName.value = writing.value;
+ isInpDisabled.value = true;
+ } else {
+ taskName.value = taskDataItem.taskName;
+ }
+ type.value = 1;
+ }
+};
+
+const onStereotypedWritingClick = (item: TaskItem) => {
+ if (taskName.value === item.value) {
+ taskName.value = "";
+ isInpDisabled.value = false;
+ } else {
+ taskName.value = item.value;
+ isInpDisabled.value = true;
+ }
+};
+
+const onAddMinutesClick = (item: DateItem) => {
+ detaCheck.value = item.value;
+ timeValue.value = addMinutes(timeValue.value, item.value);
+};
+
+const addMinutes = (time: string, delta: number): string => {
+ const [hours, minutes] = time.split(":").map(Number);
+ const totalMinutes = hours * 60 + minutes + delta;
+
+ // 处理负数和超过 1440(一天分钟数)情况
+ const normalized = (totalMinutes + 1440) % 1440;
+
+ const newHours = Math.floor(normalized / 60);
+ const newMinutes = normalized % 60;
+
+ return `${newHours.toString().padStart(2, "0")}:${newMinutes
+ .toString()
+ .padStart(2, "0")}`;
+};
+
+const handleCancel = () => {
+ if (loading.value) return;
+ isShow.value = false;
+};
+
+const handleConfirm = async () => {
+ if (loading.value) return;
+ const today = dayjs().format("YYYY-MM-DD");
+ const fullDateTime = dayjs(`${today} ${timeValue.value}`)
+ .second(0)
+ .millisecond(0); // 秒和毫秒都去掉,要不然间隔短了就是0分钟
+
+ const now = dayjs().second(0).millisecond(0); // 秒和毫秒都去掉
+ if (!fullDateTime.isAfter(now))
+ return ElMessage.warning("任务提醒时间不能早于或等于当前时间");
+ if (!taskName.value) return ElMessage.warning("任务内容不能为空");
+ loading.value = true;
+ try {
+ const diffMinutes = fullDateTime.diff(now, "minute");
+ const params = {
+ deviceCode: bedsideAuxiliaryScreenStore.deviceData.deviceCode,
+ minutes: diffMinutes,
+ alertText: taskName.value,
+ };
+ const recordCode = bedsideAuxiliaryScreenStore.deviceData.recordCode;
+ const { data, message } = await setTimeoutAlert(params);
+ if (data !== "OK") return ElMessage.warning(message);
+ ElMessage.success("操作成功");
+ bedsideAuxiliaryScreenStore.setSyncTask({
+ deviceCode: params.deviceCode,
+ recordCode: recordCode,
+ taskDate: dayjs(fullDateTime).format("YYYY-MM-DD HH:mm"),
+ taskName: params.alertText,
+ overdue: false,
+ sync: false,
+ countdown: diffMinutes * 60,
+ });
+ // 这里得先把loading关了
+ loading.value = false;
+ handleCancel();
+ } catch (error) {
+ console.log(error);
+ } finally {
+ loading.value = false;
+ }
+};
+
+/** 停止任务 */
+const stopTask = () => {
+ ElMessageBox.confirm(
+ '是否确认停止当前任务?',
+ '提示',
+ {
+ confirmButtonText: '确认',
+ cancelButtonText: '取消',
+ type: 'warning',
+ }
+ )
+ .then(async () => {
+ loading.value = true;
+ try {
+ await stopTimeoutAlert(bedsideAuxiliaryScreenStore.deviceData.deviceCode);
+ bedsideAuxiliaryScreenStore.clearTask();
+ clearTimer();
+ loading.value = false;
+ handleCancel();
+ ElMessage.success('已停止')
+ } catch(error) {
+ ElMessage.error('操作失败:' + error)
+ } finally {
+ loading.value = false;
+ }
+ })
+ .catch(() => {
+
+ })
+};
+
+onUnmounted(() => {
+ clearTimer();
+});
+
+defineExpose({
+ openDialog,
+});
+</script>
+
+<style lang="less" scoped>
+* {
+ box-sizing: border-box;
+}
+.scheduled-task-container {
+ ::v-deep(.el-dialog) {
+ padding: 0;
+ border-radius: 6px;
+ overflow: hidden;
+ }
+ ::v-deep(.el-dialog__footer) {
+ padding: 4px;
+ }
+ ::v-deep(.el-upload-dragger) {
+ height: 65px;
+ padding: 0 !important;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+ ::v-deep(.el-upload-dragger .el-icon--upload) {
+ display: none;
+ }
+ ::v-deep(.el-dialog__header) {
+ padding-bottom: 6px;
+ }
+ .scheduled-task-header {
+ position: relative;
+ height: 16px;
+ background: #ff7472;
+ .header-title {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ transform: translateX(-50%) translateY(-50%);
+ font-family: AlibabaPuHuiTi, AlibabaPuHuiTi;
+ font-weight: 500;
+ font-size: 8px;
+ color: #ffffff;
+ line-height: 11px;
+ text-align: center;
+ }
+ .header-close {
+ position: absolute;
+ top: 50%;
+ transform: translateY(-50%);
+ right: 6px;
+ width: 15px;
+ height: 15px;
+ transition: transform 0.2s;
+
+ &:active {
+ opacity: 0.6;
+ transform: translateY(-50%) scale(0.95);
+ }
+ }
+ }
+ .scheduled-task-content {
+ padding: 0 12px 0px 12px;
+ margin-bottom: 4px;
+ border-bottom: 1px solid #d8d8d8;
+ display: flex;
+ .content-left {
+ flex: 1;
+ border-right: 1px solid #d8d8d8;
+ padding-bottom: 6px;
+ padding-right: 12px;
+ .content-left-date {
+ border-bottom: 1px solid #d8d8d8;
+ display: flex;
+
+ .date-btn {
+ flex: 1;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 9px;
+ margin-left: 5px;
+ justify-content: flex-end;
+ .date {
+ margin-left: 0;
+ padding: 0;
+ width: 44px;
+ height: 18px;
+ line-height: 18px;
+ text-align: center;
+ font-size: 11px;
+ background-color: #769aff;
+ }
+ }
+ .countdown {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-left: 5px;
+ .countdown-btn {
+ padding: 0 8px;
+ background: #769aff;
+ box-shadow: 1px 1 0px 0px rgba(130, 126, 126, 0.5);
+ border-radius: 2px;
+ font-family: PingFangSC, PingFang SC;
+ font-weight: 600;
+ font-size: 11px;
+ line-height: 23px;
+ color: #ffffff;
+ text-shadow: 1px 1px 0px rgba(130, 126, 126, 0.5);
+ text-align: left;
+ font-style: normal;
+ }
+ }
+ }
+ .content-left-stereotyped-writing {
+ .stereotyped-writing {
+ position: relative;
+ height: 16px;
+ padding-top: 6px;
+ margin-bottom: 6px;
+ border-radius: 2px;
+ border: 1px solid #979797;
+ overflow: hidden;
+ .stereotyped-writing-input {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ border: none;
+ outline: none;
+ padding: 0 4px;
+ line-height: 16px;
+ font-size: 9px;
+ font-family: PingFangSC, PingFang SC;
+ vertical-align: middle;
+ box-sizing: border-box; // 避免padding撑高
+
+ &::placeholder {
+ font-family: inherit;
+ font-size: inherit;
+ line-height: inherit;
+ color: #aaaaaa;
+ opacity: 1;
+ }
+ &:disabled {
+ background-color: #f5f5f5; // 灰色背景
+ color: #999999; // 灰色文字
+ cursor: not-allowed;
+ }
+ }
+ }
+ .stereotyped-writing-list {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 7px;
+ .list-item {
+ width: 40px;
+ margin-left: 0;
+ padding-left: 0;
+ padding-right: 0;
+ background-color: #e6a23c;
+ &.check {
+ background-color: #bbc6dd;
+ }
+ }
+ }
+ }
+ }
+ .content-right {
+ width: 59px;
+ padding: 0 3px;
+ .stop-btn {
+ width: 100%;
+ background: #ff7472;
+ border-radius: 2px;
+ font-family: PingFangSC, PingFang SC;
+ font-weight: 600;
+ font-size: 6px;
+ color: #ffffff;
+ line-height: 18px;
+ text-align: center;
+ font-style: normal;
+ }
+ }
+ }
+ .my-button {
+ display: inline-block;
+ border-radius: 2px;
+ padding: 0px 10px;
+ font-family: PingFangSC, PingFang SC;
+ font-weight: 500;
+ font-size: 7px;
+ color: #ffffff;
+ line-height: 16px;
+ letter-spacing: 1px;
+ text-align: center;
+ font-style: normal;
+ transition: transform 0.1s ease, opacity 0.1s ease;
+ cursor: pointer;
+ &:active {
+ transform: scale(0.95);
+ opacity: 0.8;
+ }
+
+ &:not(:first-child) {
+ margin-left: 6px;
+ }
+
+ &.confirm {
+ background: #769aff;
+ }
+ &.cancel {
+ background: #bbc6dd;
+ }
+ &.refresh {
+ background: #e6a23c;
+ }
+ }
+}
+</style>
+<style>
+.scheduled-task-dialog {
+ margin: 0 auto;
+ top: 50% !important;
+ transform: translateY(-50%) !important;
+}
+</style>
\ No newline at end of file
--
Gitblit v1.8.0