单应用项目,可以创建很多独立工具类页面 ,不用登录 初始化的页面
zhangchen
2025-07-24 223644ebb8c5546c7b55ea86ff972898c0de835a
ID1625-暂存
3个文件已修改
1个文件已添加
494 ■■■■■ 已修改文件
src/views/mobile/bedsideAuxiliaryScreen/components/Header.vue 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mobile/bedsideAuxiliaryScreen/components/ScheduledTask.vue 334 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mobile/bedsideAuxiliaryScreen/components/TimePicker.vue 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mobile/bedsideAuxiliaryScreen/pages/UnderTreatment.vue 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mobile/bedsideAuxiliaryScreen/components/Header.vue
@@ -26,7 +26,7 @@
      </template>
    </div>
    <div class="header-right">
      <img :src="atRegularTimeImg" class="btn-img" alt="" />
      <img :src="atRegularTimeImg" class="btn-img" alt="" @click="openScheduledTaskDialog" />
      <img
        :src="setUpImg"
        class="btn-img"
@@ -38,6 +38,8 @@
  </div>
  <!-- 设置设备编号组件 -->
  <SettingDeviceDialog ref="settingDeviceDialogRef" />
  <!-- 定时任务组件 -->
  <ScheduledTaskDialog ref="scheduledTaskDialogRef" />
</template>
<script lang="ts" setup name="Header">
@@ -45,6 +47,9 @@
const SettingDeviceDialog = defineAsyncComponent(
  () => import("./SettingDeviceDialog.vue")
);
const ScheduledTaskDialog = defineAsyncComponent(
  () => import("./ScheduledTask.vue")
)
import atRegularTimeImg from "../../../../img/dingshi.png";
import setUpImg from "../../../../img/shezhi.png";
import userImg from "../../../../img/user.png";
@@ -56,6 +61,7 @@
const bedsideAuxiliaryScreenStore = useBedsideAuxiliaryScreenStore();
const settingDeviceDialogRef = ref<any>(null);
const scheduledTaskDialogRef = ref<any>(null);
const pageType = computed(() => {
  return bedsideAuxiliaryScreenStore.deviceData.pageType;
@@ -80,6 +86,10 @@
  settingDeviceDialogRef.value?.openDialog();
};
const openScheduledTaskDialog = () => {
  scheduledTaskDialogRef.value?.openDialog();
};
const openLoginDialog = () => {
  ElMessage({
    message: "功能开发中,敬请期待!",
src/views/mobile/bedsideAuxiliaryScreen/components/ScheduledTask.vue
@@ -0,0 +1,334 @@
<template>
  <div class="scheduled-task-container">
    <el-dialog
      v-model="isShow"
      center
      title="定时任务"
      width="80%"
      :show-close="false"
    >
      <template #header>
        <div class="scheduled-task-header">
          <span class="header-title">创建定时任务</span>
          <img
            :src="closeImg"
            class="header-close"
            @click="handleCancel"
            alt=""
          />
        </div>
      </template>
      <div class="scheduled-task-content">
        <div class="content-left">
          <div class="content-left-date">
            <TimePicker v-model="timeValue" />
            <div class="date-btn"></div>
          </div>
          <div class="content-left-stereotyped-writing">
            <div class="stereotyped-writing">
              <input
                v-model="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>
      </div>
      <template #footer>
        <div class="my-button cancel" @click="handleCancel">取消</div>
        <div class="my-button confirm" @click="handleConfirm">确认</div>
      </template>
    </el-dialog>
  </div>
</template>
<script lang="ts" setup>
import { computed, ref } from "vue";
// @ts-ignore
import TimePicker from './TimePicker.vue'
import closeImg from "@/img/close.png";
import alertbaojin from "@/assets/alert.wav";
import cxybaojing from "@/assets/cxy.mp3";
import gybaojing from "@/assets/gy.mp3";
import kclbaojing from "@/assets/kcl.mp3";
import tdddbaojing from "@/assets/tzddd.mp3";
import tzxllbaojing from "@/assets/tzxll.mp3";
import cgbaojing from "@/assets/cg.mp3";
interface TaskItem {
  label: string;
  value: string;
  backgroundColor: string;
  promptTone: string;
}
interface DateItem {
    label: string;
    value: number;
}
const isShow = ref(false);
const taskName = ref(""); // 任务名称
const isInpDisabled = ref(false); // 输入框是否禁用
const timeValue = ref('19:16');
const detaCheck = ref<number | null>(); // 这个是判断时间按钮的
const taskOptions = ref<TaskItem[]>([
  {
    label: "测血压",
    value: "测血压",
    backgroundColor: "#E6A23C",
    promptTone: cxybaojing,
  },
  {
    label: "开超滤",
    value: "开超滤",
    backgroundColor: "#E6A23C",
    promptTone: kclbaojing,
  },
  {
    label: "给药",
    value: "给药",
    backgroundColor: "#E6A23C",
    promptTone: gybaojing,
  },
  {
    label: "调电导度",
    value: "调电导度",
    backgroundColor: "#E6A23C",
    promptTone: tdddbaojing,
  },
  {
    label: "调血流量",
    value: "调血流量",
    backgroundColor: "#E6A23C",
    promptTone: tzxllbaojing,
  },
  {
    label: "冲管",
    value: "冲管",
    backgroundColor: "#E6A23C",
    promptTone: cgbaojing,
  },
]);
const dateOptions = ref<DateItem[]>([
    { label: '15分钟', value: 15 },
    { label: '30分钟', value: 30 },
    { label: '45分钟', value: 45 },
    { label: '60分钟', value: 60 },
]);
const taskItemCheck = computed(() => {
  return taskOptions.value.find((e) => e.value === taskName.value)?.value || "";
});
const openDialog = () => {
  isShow.value = true;
};
const onStereotypedWritingClick = (item: TaskItem) => {
  if (taskName.value === item.value) {
    taskName.value = "";
    isInpDisabled.value = false;
  } else {
    taskName.value = item.value;
    isInpDisabled.value = true;
  }
};
const handleCancel = () => {
  isShow.value = false;
};
const handleConfirm = () => {};
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;
    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;
      }
      .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;
    }
  }
  .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;
    }
    &.cancel {
      background: #bbc6dd;
    }
    &.confirm {
      background: #769aff;
    }
    &.refresh {
      background: #e6a23c;
    }
  }
}
</style>
src/views/mobile/bedsideAuxiliaryScreen/components/TimePicker.vue
New file
@@ -0,0 +1,119 @@
<template>
  <div class="time-picker">
    <div class="picker-column" ref="hourRef" @scroll="onScroll('hour')">
      <div v-for="h in hours" :key="h" class="picker-item" :class="{ active: h === selectedHour }">{{ h.toString().padStart(2, '0') }}</div>
    </div>
    <span class="colon">:</span>
    <div class="picker-column" ref="minuteRef" @scroll="onScroll('minute')">
      <div v-for="m in minutes" :key="m" class="picker-item" :class="{ active: m === selectedMinute }">{{ m.toString().padStart(2, '0') }}</div>
    </div>
  </div>
</template>
<script setup lang="ts">
import { ref, watch, onMounted, nextTick } from 'vue'
interface Props {
  modelValue: string // 格式为 "HH:mm"
}
const props = defineProps<Props>()
const emit = defineEmits(['update:modelValue'])
const selectedHour = ref(0)
const selectedMinute = ref(0)
const hourRef = ref<HTMLDivElement | null>(null)
const minuteRef = ref<HTMLDivElement | null>(null)
const hours = Array.from({ length: 24 }, (_, i) => i)
const minutes = Array.from({ length: 60 }, (_, i) => i)
function scrollTo(refEl: HTMLDivElement | null, index: number) {
  if (!refEl) return
  refEl.scrollTo({ top: index * 40, behavior: 'smooth' })
}
function updateModel() {
  const value = `${selectedHour.value.toString().padStart(2, '0')}:${selectedMinute.value.toString().padStart(2, '0')}`
  emit('update:modelValue', value)
}
function onScroll(type: 'hour' | 'minute') {
  const el = type === 'hour' ? hourRef.value : minuteRef.value
  if (!el) return
  clearTimeout((el as any)._timer)
  ;(el as any)._timer = setTimeout(() => {
    const index = Math.round(el.scrollTop / 40)
    if (type === 'hour') {
      selectedHour.value = hours[index]
      scrollTo(hourRef.value, index)
    } else {
      selectedMinute.value = minutes[index]
      scrollTo(minuteRef.value, index)
    }
    updateModel()
  }, 100)
}
watch(
  () => props.modelValue,
  (newVal) => {
    if (!newVal) return
    const [h, m] = newVal.split(':').map(Number)
    selectedHour.value = h
    selectedMinute.value = m
    nextTick(() => {
      scrollTo(hourRef.value, h)
      scrollTo(minuteRef.value, m)
    })
  },
  { immediate: true }
)
</script>
<style scoped>
.time-picker {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 200px;
  background: #e6efff;
  font-family: sans-serif;
}
.picker-column {
  width: 60px;
  height: 200px;
  overflow-y: scroll;
  scroll-snap-type: y mandatory;
  -webkit-overflow-scrolling: touch;
  text-align: center;
  position: relative;
  padding-top: 80px;
  padding-bottom: 80px;
}
.picker-item {
  height: 40px;
  line-height: 40px;
  font-size: 14px;
  color: #666;
  scroll-snap-align: center;
  transition: all 0.2s;
  transform: scale(0.8);
  opacity: 0.5;
}
.picker-item.active {
  font-size: 24px;
  font-weight: bold;
  color: #333;
  transform: scale(1.2);
  opacity: 1;
}
.colon {
  font-size: 24px;
  margin: 0 10px;
}
</style>
src/views/mobile/bedsideAuxiliaryScreen/pages/UnderTreatment.vue
@@ -394,9 +394,13 @@
  );
});
watch(() => pageData.value.ktvList, (newVal) => {
watch(
  () => pageData.value.ktvList,
  (newVal) => {
    generateKtvListEchart(newVal);
}, { deep: true });
  },
  { deep: true }
);
watch(
  () => pageData.value.monitoringRecord,
@@ -487,9 +491,9 @@
  const option = {
    grid: [
      { top: "20%", height: "27%", left: 30, right: 20 }, // 伸缩压
      { top: "45%", height: "27%", left: 30, right: 20 }, // 舒张压
      { top: "67%", height: "27%", left: 30, right: 20 }, // 脉搏
      { top: "10%", height: "25%", left: 30, right: 20 },
      { top: "38%", height: "25%", left: 30, right: 20 },
      { top: "66%", height: "25%", left: 30, right: 20 },
    ],
    tooltip: {
      trigger: "axis",
@@ -534,9 +538,8 @@
        axisTick: { show: false },
        axisLabel: { show: false },
        splitLine: { show: false },
        min: 80,
        max: 180,
        interval: 20,
        min: "dataMin", // 自动以数据最小值为最小值
        max: "dataMax",
        gridIndex: 0,
      },
      {
@@ -546,9 +549,8 @@
        axisTick: { show: false },
        axisLabel: { show: false },
        splitLine: { show: false },
        min: 40,
        max: 120,
        interval: 20,
        min: "dataMin", // 自动以数据最小值为最小值
        max: "dataMax",
        gridIndex: 1,
      },
      {
@@ -558,9 +560,8 @@
        axisTick: { show: false },
        axisLabel: { show: false },
        splitLine: { show: false },
        min: 40,
        max: 140,
        interval: 20,
        min: "dataMin", // 自动以数据最小值为最小值
        max: "dataMax",
        gridIndex: 2,
      },
    ],