hldy_app_mini/pages/NursingNew/component/nurse/newindex.vue

1488 lines
46 KiB
Vue
Raw Normal View History

2026-01-16 14:28:12 +08:00
<!-- 护嘱 -->
<template>
<view class="right-container" :style="isshow?{opacity: `1`}:{opacity: `0`}"
@click="bottomisShaking=false;shakyTable = false">
<view class="doctorsay-container-view">
<view class="doctorsay-container-container">
<view class="super-card">
<view class="boom-father">
<view class="boom" :style="{ transform: transformStyle }">
<view>
<view v-for="(item,index) in timearr[0]?.children" :key="index">
<view class="boom-son" v-show="item.tagName">
<text class="boom-text">
{{item.tagName}}
</text>
</view>
</view>
</view>
</view>
</view>
<view class="super-card-container">
2026-01-20 15:35:05 +08:00
<scroll-view style="width: 100%;" scroll-with-animation :scroll-left="cardLeft" scroll-x
@scroll="handleTop" :show-scrollbar="false">
2026-01-16 14:28:12 +08:00
<view style="display: flex;width:4824rpx;">
2026-01-20 15:35:05 +08:00
<view v-for="(item0,index0) in timearr" :key="index0" class="super-card-right">
2026-01-16 14:28:12 +08:00
<view class="super-card-time">
{{(item0.positioning.length == 1 ? ('0' + item0.positioning) : item0.positioning) + ":00"}}
</view>
</view>
</view>
2026-01-20 15:35:05 +08:00
<view style="display: flex;height: 1225rpx;position: relative;">
2026-01-16 14:28:12 +08:00
<view class="xian-bian"></view>
2026-01-20 15:35:05 +08:00
<scroll-view style="height: 100%;width:6960rpx;background-color: #fff;"
:scroll-top="scrollTop" scroll-with-animation :scroll-y="true"
@scroll="handleScrolltime" :show-scrollbar="false">
2026-01-16 14:28:12 +08:00
<view style="display: flex;height: 100%;">
<view v-for="(item0,index0) in timearr" :key="index0">
<view class="super-card-time-und">
<view v-for="(item1,index1) in item0?.children" style="width: 100%;"
:key="index1">
<view
:class=" targetRuler.index0 === index0 && targetRuler.index1 === index1 ? targetRuler.index1 ?`title-time-border-big`:`title-time-border-big-top` : `super-card-time-card` "
:style="!targetRuler.bordershow && saveRulerTime.index0 === index0 && saveRulerTime.index1 === index1 ? {zIndex:999} : {borderBottom: '1rpx solid transparent'}"
:id="`a${index0}_${index1}`" style="position: relative;"
@click="rulerTouchClick(item1,index0,index1)"
:data-index0="index0" :data-index1="index1">
<view class="time-button-orange-spe"
:style="{left:flyNumber.index0?`-130rpx`:`0`}"
v-if="flyNumber.index0 === index0 && flyNumber.index1 === index1 && index1==0">
请选择服务指令迁移的目标单元格
</view>
<view class="time-button-orange"
:style="{left:flyNumber.index0?`-130rpx`:`0`}"
v-if="flyNumber.index0 === index0 && flyNumber.index1 === index1 && index1">
请选择服务指令迁移的目标单元格
</view>
<view class="title-time-blue"
v-show="saveEditIndex.index0 == index0 && saveEditIndex.index1 == index1 && isRule">
2026-01-20 15:35:05 +08:00
<image class="blue-img" lazy-load
src="/static/index/bluetarget.png" />
2026-01-16 14:28:12 +08:00
</view>
<view :class="getClass(item1,index0,index1)"
style="font-size: 30rpx;overflow: hidden;"
2026-01-20 15:35:05 +08:00
:style="{ animationDelay:`-${computeDelay(index0, index1).toFixed(2)}s`,border:saveEditIndex.index0 == index0 && saveEditIndex.index1 == index1? `2rpx solid #46B2F6`:'' }">
2026-01-16 14:28:12 +08:00
<view class="title-time" v-if="item1.startTime"
style="flex-direction: column;">
<image v-show="item1.startTime"
style="width: 50rpx;height: 50rpx;margin: 0 auto;margin-top: 20rpx"
:src="item1.netImmediateFile?item1.netImmediateFile:`/static/logo.png`" />
<view class="title-time-time" style="font-size: 30rpx;">
{{item1.startTime + `-` + item1.endTime}}
</view>
<image class="title-time-button"
style="width: 80rpx;height: 50rpx;"
v-if="item1.cycleTypeId!=1"
src="/static/index/newruler/jiao.png" />
<view class="title-time-font"
style="right: 10rpx;top: 5rpx;"
v-if="item1.cycleTypeId!=1">
{{item1.cycleType}}
</view>
</view>
<view v-if="item1.startTime" class="title-time-font-rel">
{{splitString(item1.directiveName)[0]}}
</view>
<view
v-if="saveEditIndex.index0 == index0 && saveEditIndex.index1 == index1 && isRule && !item1.startTime"
class="pulic-time">
{{(item0.positioning.length == 1 ? ('0' + item0.positioning) : item0.positioning) + ":" + timeArray[index1]}}
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
</scroll-view>
</view>
2026-01-20 15:35:05 +08:00
<view class="right-order">
<view class="right-tags">
<view class="right-tags-left">
2026-01-16 14:28:12 +08:00
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 点击的弹出层 -->
<view v-show="isopen" class="popup-overlay" @click="isopen=false;flyNumber.index0=999;touchindex1=-1">
<view class="popup-overlay-content" style="height: 390rpx;" popup-overlay:class="getjiao"
v-if="timearr[showDetail[0]]?.children[showDetail[1]]?.izPackage==='N'"
:style="{ top: (2*openY - 350) + 'rpx',left: (2*openX - 780) + 'rpx',opacity: isopacity ? 1 : 0 }"
@click.stop>
<view class="popup-overlay-content-left">
<image class="popup-overlay-content-left-img"
:src="timearr[showDetail[0]]?.children[showDetail[1]]?.netImmediateFileFocus" />
</view>
<view class="popup-overlay-content-right">
<view class="time-font">
{{ timearr[showDetail[0]]?.children[showDetail[1]]?.startTime }} -
{{ timearr[showDetail[0]]?.children[showDetail[1]]?.endTime }}
</view>
<view class="time-text">
{{ timearr[showDetail[0]]?.children[showDetail[1]]?.directiveName }}
</view>
</view>
</view>
<view class="popup-overlay-content" :class="getjiao" v-else
:style="{ top: (2*openY - 350) + 'rpx',left: (2*openX - 780) + 'rpx',opacity: isopacity ? 1 : 0 }"
@click.stop>
<view class="specia-onshow">
<view class="specia-title">
{{ timearr[showDetail[0]]?.children[showDetail[1]]?.startTime }} -
{{ timearr[showDetail[0]]?.children[showDetail[1]]?.endTime }}
</view>
<view class="specia-cards" v-if="timearr[showDetail[0]]?.children[showDetail[1]]?.directivesList">
<view class="title-time-border-yellow"
style="font-size: 30rpx;overflow: hidden;width: 232rpx;height: 150rpx;"
v-for="(item1,index1) in timearr[showDetail[0]]?.children[showDetail[1]]?.directivesList"
:key="index1">
<view class="title-time" style="flex-direction: column;">
<image style="width: 60rpx;height: 60rpx;margin: 0 auto;margin-top: 10rpx"
:src="item1.netImmediateFile" />
<view class="title-time-time" style="font-size: 30rpx;">
{{item1.serviceDuration + "分钟"}}
</view>
<image class="title-time-button" style="width: 80rpx;height: 48rpx;"
src="/static/index/newruler/jiao.png" />
<view class="title-time-font" style="right: 10rpx;top: 5rpx;font-size: 23rpx;">
{{ item1.cycleType }}
</view>
</view>
<view class="title-time-font-rel">
{{splitString(item1.directiveName)[0]}}
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 替换表格的的弹出层 -->
<view v-show="sayisopen" class="popup-say" @click="sayisopen=false">
<view class="popup-say-content" style="padding: 30rpx 0;" :style="{ opacity: sayisopacity ? 1 : 0 }"
@click.stop>
<view style="margin-top: 20rpx;margin-bottom: 20rpx;;margin-left: 30rpx;font-size: 32rpx;">
<view>
体型标签
</view>
</view>
<view style="display: flex;flex-wrap: wrap;">
<view v-for="(item,index) in nameArray.slice(0,5)" :key="index">
<view class="tags-father">
<image class="tags-img" :src="`/static/index/tagNames/${index}0.png`" />
<view class="tags-font">{{item}}</view>
</view>
</view>
</view>
<view style="margin-top: 40rpx;margin-bottom: 20rpx;;margin-left: 30rpx;font-size: 32rpx;">
<view>
情绪标签
</view>
</view>
<view style="display: flex;flex-wrap: wrap;">
<view v-for="(item,index) in nameArray.slice(5,12)" :key="index">
<view class="tags-father">
<image class="tags-img" :src="`/static/index/tagNames/${index+5}0.png`" />
<view class="tags-font">{{item}}</view>
</view>
</view>
</view>
<view class="popup-say-three"></view>
</view>
</view>
<!-- 分享的弹出层 -->
<view v-show="shareShow" class="popup-share" @click="shareShow=false">
<view class="popup-share-content" :style="{ opacity: deletedownisopacity ? 1 : 0 }" @click.stop>
<view class="share-other">
<view class="share-title">
<image style="width: 50rpx;height: 50rpx;" src="/static/index/sharelogo.png" />
<view style="font-weight: 600;margin-left: 15rpx;">
护理单元
</view>
</view>
<view class="share-others">
<view style="font-weight: 600;font-size: 45rpx;">
护理流程
</view>
<view style="margin-top: 30rpx;">
{{uni.getStorageSync('nuName')}}
<text style="color: #1083F8;">
{{uni.getStorageSync('NUall').elderInfo?uni.getStorageSync('NUall').elderInfo.name:""}}
</text>
</view>
<view class="blue-button">
分享
</view>
</view>
</view>
</view>
</view>
<view :class="['neuro-wrapper', deletedonghua ? 'is-active' : '']" v-if="isDelete">
<!-- 遮罩层点击触发关闭 -->
<view class="neuro-mask" @click="isDelete=false"></view>
<!-- 拟态框阻止冒泡点击 -->
<view class="neuro-box" @click.stop>
<view class="delete-button-father">
<view class="button-white" @click="isDelete=false">取消</view>
<view class="button" @click="openDelete()">确定</view>
</view>
<view class="title">确定要删除指令吗</view>
<view class="title-time-border-yellow"
style="font-size: 30rpx;overflow: hidden;width: 240rpx;height: 170rpx;">
<view class="title-time" style="flex-direction: column;">
<image style="width: 60rpx;height: 60rpx;margin: 0 auto;margin-top: 10rpx"
:src="timearr[saveEditIndex.index0]?.children[saveEditIndex.index1].netImmediateFile?timearr[saveEditIndex.index0].children[saveEditIndex.index1].netImmediateFile:`/static/logo.png`" />
<view class="title-time-time" style="font-size: 30rpx;">
{{timearr[saveEditIndex.index0].children[saveEditIndex.index1].startTime + `-` + timearr[saveEditIndex.index0].children[saveEditIndex.index1].endTime}}
</view>
<image class="title-time-button" style="width: 80rpx;height: 48rpx;"
v-if="timearr[saveEditIndex.index0]?.children[saveEditIndex.index1].cycleTypeId!=1"
src="/static/index/newruler/jiao.png" />
<view class="title-time-font" style="right: 10rpx;top: 5rpx;font-size: 23rpx;"
v-if="timearr[saveEditIndex.index0].children[saveEditIndex.index1].cycleTypeId!=1">
{{ timearr[saveEditIndex.index0]?.children[saveEditIndex.index1].cycleType }}
</view>
</view>
<view class="title-time-font-rel">
{{splitString(timearr[saveEditIndex.index0]?.children[saveEditIndex.index1].directiveName)[0]}}
</view>
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, computed, nextTick, watch } from 'vue';
import { onShow, onHide } from '@dcloudio/uni-app';
import { getNclist, addBatch, addDirective, addInstant, deleteDirective, deleteInstant, editDirective } from "./api.js";
import { myArray } from './yaoshandiao.js';
const props = defineProps({
isshow: {
type: Boolean,
required: true,
},
});
const bodystatus = ref(false);
const bodystatustarget = ref(-1);
const facestatus = ref(false);
const facestatustarget = ref(-1);
/* ---- transform ----
注意虽然这是响应式但我们只在 rAF 里更新它受控更新避免频繁触发 Vue 渲染 */
const transformStyle = ref('translate3d(0, 0, 0)');
/* rAF / 计数器变量 */
let lastY = 0;
let ticking = false;
const bodydonghua = ref(false)
const openbody = ref(false)
const openface = ref(false)
2026-01-20 15:35:05 +08:00
2026-01-16 14:28:12 +08:00
const facedonghua = ref(false)
2026-01-20 15:35:05 +08:00
2026-01-16 14:28:12 +08:00
const bodytarget = ref([]);
const facetarget = ref([]);
const addbody = (index : number) => {
if (bodyTagList.value[index].izSelected == 'Y') {
bodyTagList.value[index].izSelected = 'N';
} else {
let targetNumber = 0;
bodyTagList.value.forEach((element : any) => {
if (element.izSelected == 'Y') {
targetNumber++
}
})
if (targetNumber > 1) {
uni.showToast({
title: "每种标签最多只能添加两个",
icon: 'none',
duration: 3000
})
return
} else {
bodyTagList.value[index].izSelected = 'Y';
}
}
saveAll()
}
const addface = (index : number) => {
if (emotionTagList.value[index].izSelected == 'Y') {
emotionTagList.value[index].izSelected = 'N'
} else {
let targetNumber = 0;
emotionTagList.value.forEach((element : any) => {
if (element.izSelected == 'Y') {
targetNumber++
}
})
if (targetNumber > 1) {
uni.showToast({
title: "标签最多只能添加两个",
icon: 'none',
duration: 3000
})
return
} else {
emotionTagList.value[index].izSelected = 'Y';
}
}
saveAll()
}
const open = ref(false);
const bottomItems = ref([])
const nameArray = [
`标准`, `超重`, `强直`, `偏瘫`, `佝偻`, `稳定`, `焦虑`, `抑郁`, `暴力`, `恐惧`, `烦躁`, `易怒`, `臆想`,
]
const timeArray = [
`00`, `05`, `10`, `15`, `20`, `25`, `30`, `35`, `40`, `45`, `50`, `55`
];
const weekDays = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
const days = Array.from({ length: 31 }, (_, i) => (i + 1).toString().padStart(2, "0"));
const isweek = ref(true);
// 是否周期
const iszhouqi = ref(false);
//高度回到最高
const firsttop = ref(0);
const secondtop = ref(0);
const scrollLeft = ref(0);
const cardLeft = ref(678);
//移动表格
const scrollTop = ref(0)
//左下的数组
const downList = ref<any>()
const isop = ref(false);
const bigArray = ref([]);
const isopen = ref(false)
const songisopen = ref(false)
const isopacity = ref(false)
const songisopacity = ref(false)
// 删除表格弹窗
const deleteisopen = ref(false);
const deletename = ref("")
const deleteisopacity = ref(false)
// 解释图标弹窗
const sayisopen = ref(false);
const sayname = ref("")
const sayisopacity = ref(false)
const saveleft = ref(6);
const saveright = ref(11);
const savetop = ref(0);
const savebottom = ref(3);
const isMove = ref(false);
const getjiao = computed(() => {
if (jiao.value[0] && jiao.value[1]) {
return "left-bottom"
} else if (!jiao.value[0] && jiao.value[1]) {
return "right-bottom"
} else if (jiao.value[0] && !jiao.value[1]) {
return "left-top"
} else {
return "right-top"
}
})
// 这是二级菜单的动画的模板
const secondtemp = ref([])
// 上次点击时间
const lastTap = ref(0)
// 双击的最大间隔ms可根据体验调整
const DOUBLE_TAP_DELAY = 300
function onTap(e) {
const now = Date.now()
thirdmenuIndex.value = e
// console.log("????",thirdmenuIndex.value)
if (now - lastTap.value < DOUBLE_TAP_DELAY) {
// 双击成立
thirdmenuIndex.value = e
addnew()
// 重置,避免多次触发
lastTap.value = 0
} else {
thirdmenuIndex.value = e
// 记录本次时间,等待下次点击
lastTap.value = now
}
}
// 替换新的
const getNew = () => {
if (flyNumber.value.index0 === saveEditIndex.value.index0 && flyNumber.value.index1 === saveEditIndex.value.index1) {
isMove.value = false;
flyNumber.value.index0 = -1;
flyNumber.value.index1 = -1;
return
}
if (timearr.value[saveEditIndex.value.index0]?.children[saveEditIndex.value.index1].id) {
deleteDirective(timearr.value[saveEditIndex.value.index0]?.children[saveEditIndex.value.index1]).then((res : any) => {
doChangeNew()
})
} else {
doChangeNew()
}
}
const doChangeNew = () => {
let object = JSON.parse(JSON.stringify(timearr.value[flyNumber.value.index0]?.children[flyNumber.value.index1]))
indexsave.value = [saveEditIndex.value.index0, saveEditIndex.value.index1]
// 旧的tagName保存了
let tagName = timearr.value[flyNumber.value.index0]?.children[flyNumber.value.index1].tagName
timearr.value[flyNumber.value.index0].children[flyNumber.value.index1] = { directiveName: '', tagName: tagName }
//然后保存新的
let newtagName = timearr.value[indexsave.value[0]].children[indexsave.value[1]].tagName
timearr.value[indexsave.value[0]].children[indexsave.value[1]] = object
timearr.value[indexsave.value[0]].children[indexsave.value[1]].tagName = newtagName
let startTime = timearr.value[indexsave.value[0]].children[indexsave.value[1]].startTime;
let endTime = timearr.value[indexsave.value[0]].children[indexsave.value[1]].endTime;
let positioning = timearr.value[indexsave.value[0]].positioning;
// 解析原始时间(健壮地 trim
const parseTime = (t) => {
const parts = String(t || '').split(':').map(s => s.trim());
const h = Number(parts[0] ?? 0);
const m = Number(parts[1] ?? 0);
return {
hour: Number.isFinite(h) ? h : 0,
minute: Number.isFinite(m) ? m : 0
};
};
const sOrig = parseTime(startTime);
const eOrig = parseTime(endTime);
// 计算时长(分钟)。若发生负值,认为跨天(加 24*60
let duration = (eOrig.hour * 60 + eOrig.minute) - (sOrig.hour * 60 + sOrig.minute);
if (duration < 0) duration += 24 * 60; // 假设结束是在次日或跨日
// 解析新的起始分钟和小时positioning 可能是字符串)
const newStartMin = Number(String(newtagName).trim());
const newHour = Number(String(positioning).trim());
// 校验
if (!Number.isFinite(newStartMin) || newStartMin < 0 || newStartMin >= 60) {
throw new Error('newtagName 必须是 0-59 的数字字符串或数字');
}
if (!Number.isFinite(newHour) || newHour < 0) {
throw new Error('positioning 必须是有效小时(数字或数字字符串)');
}
// 计算新的起止总分钟
const newStartTotal = newHour * 60 + newStartMin;
const newEndTotal = newStartTotal + duration;
// 处理跨小时/跨天
const newEndHour = Math.floor(newEndTotal / 60) % 24; // 保持在 0-23
const newEndMin = newEndTotal % 60;
// 格式化字符串
const pad2 = (n) => String(n).padStart(2, '0');
timearr.value[indexsave.value[0]].children[indexsave.value[1]].startTime =
`${String(newHour)}:${pad2(newStartMin)}`;
timearr.value[indexsave.value[0]].children[indexsave.value[1]].endTime =
`${String(newEndHour)}:${pad2(newEndMin)}`;
flyNumber.value.index1 = -1;
isMove.value = false;
let data = {
index0: saveEditIndex.value.index0,
index1: saveEditIndex.value.index1
}
whereEvent(data);
let infoValue = timearr.value[saveEditIndex.value.index0].children[saveEditIndex.value.index1]
infoValue.positioning = saveEditIndex.value.index0;
infoValue.positioningLong = saveEditIndex.value.index1;
editDirective(infoValue).then((res : any) => {
if (res.success) {
geteverything()
}
})
}
//变更左侧菜单
const changLeft = (index : number) => {
if (index === 5) {
uni.navigateTo({
url: "/pages/watch/full"
})
return
}
iszhouqi.value = false;
weekIndex.value = -1;
monthIndex.value = -1;
weekValue.value = "";
monthValue.value = "";
secondtop.value = 0.01
firsttop.value = 0.01
//这个东西完全是为了给动画用的因为downmenuIndex这个吊东西其他地方在用所以需要再整一个属性。
downdonghua.value = -1;
nextTick(() => {
secondtop.value = 0
firsttop.value = 0
downdonghua.value = 0;
})
downmenuIndex.value = 0;
upmenuIndex.value = index
downList.value = bigArray.value[index]?.children
thirdmenuIndex.value = 0;
}
const isempty = ref(false);
const changecard = () => {
isDelete.value = false;
open.value = false;
if (isMove.value) {
getNew()
} else {
if (timearr.value[saveEditIndex.value.index0]?.children[saveEditIndex.value.index1].directiveName) {
flyNumber.value.index0 = saveEditIndex.value.index0;
flyNumber.value.index1 = saveEditIndex.value.index1;
isMove.value = true
}
else {
isempty.value = true;
setTimeout(() => {
isempty.value = false;
}, 1500)
}
}
}
const isRule = ref(false);
const topindex = ref(-1)
const weekValue = ref("");
const weekIndex = ref(-1);
const monthValue = ref("");
const monthIndex = ref(-1);
const clickWeek = (item, index) => {
const now = Date.now()
if (now - lastTap.value < DOUBLE_TAP_DELAY) {
// 双击成立
weekValue.value = item;
weekIndex.value = index;
addnew()
// 重置,避免多次触发
lastTap.value = 0
} else {
weekValue.value = item;
weekIndex.value = index;
// 记录本次时间,等待下次点击
lastTap.value = now
}
}
const clickMonth = (item, index) => {
const now = Date.now()
if (now - lastTap.value < DOUBLE_TAP_DELAY) {
// 双击成立
monthValue.value = item;
monthIndex.value = index;
addnew()
// 重置,避免多次触发
lastTap.value = 0
} else {
monthValue.value = item;
monthIndex.value = index;
// 记录本次时间,等待下次点击
lastTap.value = now
}
}
const isDelete = ref(false);
const openDelete = () => {
flyNumber.value.index0 = -1
flyNumber.value.index1 = -1
isMove.value = false;
open.value = false
if (timearr.value[saveEditIndex.value.index0]?.children[saveEditIndex.value.index1].directiveName) {
if (!isDelete.value) {
isDelete.value = true;
setTimeout(() => deletedonghua.value = true, 50)
} else {
isDelete.value = false;
deleteRuler(saveEditIndex.value.index0, saveEditIndex.value.index1);
}
} else {
isempty.value = true;
setTimeout(() => {
isempty.value = false;
}, 1500)
}
}
// 给抖动用的
function pseudoRandom(index0, index1) {
const seed = index0 * 55.9898 + index1 * 78.233;
// 产生一个伪随机数,取小数部分
return Math.abs(Math.sin(seed) * 43758.5453) % 1;
}
function computeDelay(index0, index1) {
const range = 2; // 延迟范围 0 ~ 2 秒
return pseudoRandom(index0, index1) * range;
}
// 在作用域中声明一个定时器变量
let throttleTimer = null;
//监听拖拽
/* 兼容:如果不支持 requestAnimationFrame就用 setTimeout */
const requestAnimationFrame =
typeof window !== 'undefined' && window.requestAnimationFrame
? window.requestAnimationFrame
: (cb) => setTimeout(cb, 16);
function handleScrolltime(e) {
// uni-app 的 scroll 事件通常把位置放在 e.detail.scrollTop
// 为保险,先做容错判断
const scrollTop = (e && e.detail && (e.detail.scrollTop ?? e.detail.scrollY)) || 0;
lastY = scrollTop;
// 如果还在等待上一帧更新,就直接返回;否则安排下一帧更新
if (!ticking) {
ticking = true;
requestAnimationFrame(() => {
// 把 transform 更新为负的 scrollTop向上平移
// 使用 translate3d 触发 GPU 合成层
transformStyle.value = `translate3d(0, -${lastY}px, 0)`;
ticking = false;
});
}
}
const leftIn = ref(0)
function handleTop(e) {
leftIn.value = e.detail.scrollLeft
}
// 方法:根据条件返回不同的类名
const getClass = (item, index0, index1) => {
if (item.startTime) {
if (flyNumber.value.index0 === (index0) && flyNumber.value.index1 === index1) {
return 'title-time-border-yellow-active-transparent';
} else if (shakyTable.value) {
return 'title-time-border-yellow-active';
} else {
return 'title-time-border-yellow';
}
}
return 'title-time-border';
}
// 通用的生成函数
function genPaths(base, prefix, count, ext = 'png', startIndex = 0, pad = false) {
return Array.from({ length: count }, (_, i) => {
const idx = pad
? String(i + startIndex).padStart(2, '0')
: i + startIndex
return `${base}/${prefix}${idx}.${ext}`
})
}
// 初始化下面侧单列表 ---这是一级菜单的模版
const doctorsayList = ref([
{
url: genPaths(
'/static/index/newruler',
'daily_',
6, // 张数
'png',
1, // 起始索引
false // 不补零
), name: '日常'
},
{
url: genPaths(
'/static/index/newruler',
'clean_',
8, // 张数
'png',
1, // 起始索引
false // 不补零
), name: '清洁'
},
{
url: genPaths(
'/static/index/newruler',
'diet_',
7, // 张数
'png',
1, // 起始索引
false // 不补零
), name: '饮食'
},
{
url: genPaths(
'/static/index/newruler',
'sleep_',
5, // 张数
'png',
1, // 起始索引
false // 不补零
), name: '睡眠'
},
{
url: genPaths(
'/static/index/newruler',
'defecate_',
5, // 张数
'png',
1, // 起始索引
false // 不补零
), name: '排泄'
},
]);
// 当前选中的菜单索引
const upmenuIndex = ref<number>(1);
const downmenuIndex = ref<number>(0);
const downdonghua = ref(-1);
const thirdmenuIndex = ref<number>(0);
const forthmenuIndex = ref<number>(0);
const clickzhilingbao = (e : number) => {
const now = Date.now()
forthmenuIndex.value = e
if (now - lastTap.value < DOUBLE_TAP_DELAY) {
// 双击成立
addnew()
// 重置,避免多次触发
lastTap.value = 0
} else {
// 记录本次时间,等待下次点击
lastTap.value = now
}
}
const saveEditIndex = ref({
index0: -1,
index1: -1
})
const secondContant = (index : number) => {
iszhouqi.value = false;
weekIndex.value = -1;
monthIndex.value = -1;
weekValue.value = "";
monthValue.value = "";
downmenuIndex.value = index;
downdonghua.value = index;
thirdmenuIndex.value = 0;
firsttop.value = 0.01;
nextTick(() => {
firsttop.value = 0;
})
}
const timer = ref(null);//计时器
const elementsInfo = ref({})//所有表格的信息
const moveX = ref(0)
const moveY = ref(0)
const openX = ref(0)
const openY = ref(0)
const flyNumber = ref({
index0: 999,
index1: 999,
tagName: ''
})
const deletedownisopacity = ref(false);
const touchindex1 = ref(-1);
// 分享矩阵到微信
const shareShow = ref(false);
const shareToWeixin = () => {
shareShow.value = true;
deletedownisopacity.value = false;
setTimeout(() => {
deletedownisopacity.value = true
}, 100)
}
const clickshare = () => {
uni.share({
provider: "weixin",
scene: "WXSceneSession",
type: 0,
href: "http://192.168.2.31:3101/daytoday",
title: "护理日程分享",
summary: "九泰护理日程测试",
imageUrl: "https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/uni@2x.png",
success: function (res) {
console.log("success:");
},
fail: function (err) {
console.log("fail:");
}
});
}
const jiao = ref([false, false])
//表格点击开始
const showDetail = ref([-1, -1])
const rulerTouchClick = (item : any, index0 : number, index1 : number) => {
isDelete.value = false;
saveEditIndex.value.index0 = index0;
saveEditIndex.value.index1 = index1;
centerCell();
isRule.value = true;
setTimeout(() => {
if (item.directiveName && open.value) {
touchindex1.value = index1;
const query = uni.createSelectorQuery()
query
.selectAll('.super-card-time-card')
.boundingClientRect((data : any) => {
data.forEach(async (res : any) => {
// 根据你的条件筛选元素
if (res.left > 100 && res.left < 1067 && res.top < 670 && res.top > 50 && res.dataset.index0 == index0 && res.dataset.index1 == index1) {
if (res.left > 100 && res.left < 500) {
// 表格太靠左侧,修改到右面
openX.value = Math.floor(res.left) + 528;
jiao.value[0] = true
} else {
openX.value = Math.floor(res.left) - 18
jiao.value[0] = false
}
if (res.top > 300) {
// 表格太靠上侧,修改到下面
openY.value = Math.floor(res.top) + 100;
jiao.value[1] = true
} else {
openY.value = Math.floor(res.top) + 180
jiao.value[1] = false
}
await nextTick()
isopen.value = true;
2026-01-20 15:35:05 +08:00
2026-01-16 14:28:12 +08:00
showDetail.value[0] = index0;
showDetail.value[1] = index1;
// console.log("shaa",timearr.value[showDetail.value[0]]?.children[showDetail.value[1]]?.izPackage)
isopacity.value = false;
//加动画
setTimeout(() => {
isopacity.value = true;
}, 100)
}
})
})
.exec()
}
}, 400)
}
const shakyTable = ref(false);
const reldata = ref([]);
const deleteRuler = (index0 : number, index1 : number) => {
deleteDirective(timearr.value[index0]?.children[index1]).then((res : any) => {
if (res.success) {
geteverything()
}
})
}
const saveX = ref(0);
const saveY = ref(0);
const bottomTimer = ref(null);
const bottomisShaking = ref(false);
const bottomTouchStart = (e) => {
saveX.value = Math.floor(e.touches[0].pageX);
saveY.value = Math.floor(e.touches[0].pageY);
// 2秒后触发抖动效果
bottomTimer.value = setTimeout(() => {
bottomisShaking.value = true
}, 500)
}
const bottomTouchMove = (e) => {
const moveX = Math.floor(e.touches[0].pageX);
const moveY = Math.floor(e.touches[0].pageY);
// 计算移动距离
if (
Math.abs(moveX - saveX.value) > 0 ||
Math.abs(moveY - saveY.value) > 0
) {
if (bottomTimer.value) {
clearTimeout(bottomTimer.value)
bottomTimer.value = null
}
}
}
const bottomTouchEnd = () => {
if (bottomTimer.value) {
clearTimeout(bottomTimer.value)
bottomTimer.value = null
}
}
const indexsave = ref([-1, -1]);
const opensay = () => {
sayisopacity.value = false;
sayisopen.value = true;
setTimeout(() => {
sayisopacity.value = true
}, 50)
}
const buttonBlue = ref(false)
let animTimer = null
async function addnewbutton() {
// 清定时器(防止残留)
if (animTimer) {
clearTimeout(animTimer)
animTimer = null
}
// 先把状态设为 false等待 DOM 更新后再设为 true —— 达到重启动画的效果
buttonBlue.value = false
await nextTick() // 等待下一帧,确保样式被移除
// 重新打开动画
buttonBlue.value = true
addnew()
// 1s 后还原(与动画时长保持一致)
animTimer = setTimeout(() => {
buttonBlue.value = false
animTimer = null
}, 1000)
}
const doaddDirective = (element : any) => {
addDirective(element).then((res) => {
if (res.success) {
geteverything()
}
})
}
const killjishi = (id : string) => {
deleteInstant({ id: id }).then((res) => {
if (res.success) {
geteverything()
}
})
}
const addnew = () => {
if (isDelete.value) {
isDelete.value = false;
deleteRuler(saveEditIndex.value.index0, saveEditIndex.value.index1);
return
}
if (isMove.value) {
getNew()
return
}
let haveValue = false;
if (timearr.value[saveEditIndex.value.index0]?.children[saveEditIndex.value.index1].directiveName) {
haveValue = true
}
// return
if (iszhiling.value) {
let allobject = savePackagelist.value[forthmenuIndex.value];
if (saveEditIndex.value.index1 === -1 && saveEditIndex.value.index0 === -1) {
return
}
if (isDelete.value) {
isDelete.value = false;
deleteRuler(saveEditIndex.value.index0, saveEditIndex.value.index1);
return
}
if (isMove.value) {
getNew()
return
}
flyNumber.value.index0 = -1;
flyNumber.value.index1 = -1;
isMove.value = false;
let cycleType = `打包`;
let cycleValue = "";
const startHour = Number(saveEditIndex.value.index0)
const startMinute = Number(timearr.value[saveEditIndex.value.index0]?.children[saveEditIndex.value.index1].tagName)
const endMinute = startMinute + Number(allobject.serviceDuration)
const endHour = startHour + Math.floor(endMinute / 60)
const formattedStart = `${String(startHour)}:${String(startMinute).padStart(2, '0')}`
const formattedEnd = `${String(endHour)}:${String(endMinute % 60).padStart(2, '0')}`
let param = {
id: haveValue ? timearr.value[saveEditIndex.value.index0]?.children[saveEditIndex.value.index1].id : "",
nuId: uni.getStorageSync('nuId'),
nuName: uni.getStorageSync('nuName'),
elderId: uni.getStorageSync('elderId'),
elderName: uni.getStorageSync('NUall').elderInfo.name,
directiveId: allobject.id,
directiveName: allobject.packageName,
typeId: "",
typeName: "",
categoryId: "",
categoryName: "",
cycleTypeId: "",
cycleType: cycleType,
cycleValue: cycleValue,
startTime: formattedStart,
endTime: formattedEnd,
serviceDuration: allobject.serviceDuration,
positioning: saveEditIndex.value.index0.toString(),
positioningLong: saveEditIndex.value.index1.toString(),
izPackage: 'Y',
previewFile: "",
previewFileSmall: "",
2026-01-20 15:35:05 +08:00
netPreviewFile: "",
netPreviewFileSmall: "",
2026-01-16 14:28:12 +08:00
immediateFile: "",
immediateFileFocus: "",
2026-01-20 15:35:05 +08:00
netImmediateFileFocus: "",
2026-01-16 14:28:12 +08:00
netImmediateFile: "",
tagName: timearr.value[saveEditIndex.value.index0]?.children[saveEditIndex.value.index1].tagName,
mp3File: allobject.mp3File,
netMp3File: allobject.netMp3File,
mp4File: allobject.mp4File,
netMp4File: allobject.netMp4File,
serviceContent: allobject.serviceContent,
}
//给表格赋值
timearr.value[saveEditIndex.value.index0].children[saveEditIndex.value.index1] = param;
let data = {
index0: saveEditIndex.value.index0,
index1: saveEditIndex.value.index1
}
whereEvent(data);
if (haveValue) {
editDirective(param).then((res : any) => {
if (res.success) {
geteverything()
}
})
} else {
doaddDirective(param);
}
return
}
let stopIt = false;
let allobject = bigArray.value[upmenuIndex.value].children[downmenuIndex.value].children[thirdmenuIndex.value]
if (allobject.cycleTypeId == 3) {
scrollLeft.value = 1;
bottomItems.value.forEach((element : any, index : number) => {
if (element.directiveName == allobject.title) {
stopIt = true
}
})
nextTick(() => {
if (!stopIt) {
scrollLeft.value = 0;
if (bottomItems.value.length && bottomItems.value[0].target === "#03a4ff") {
bottomItems.value[0].target = "#fff"
clearTimeout(cleansettimeout.value);
}
let pushValue = allobject;
pushValue.directiveId = allobject.id;
pushValue.directiveName = allobject.title;
pushValue.serviceDuration = allobject.serviceDuration;
pushValue.target = `#03a4ff`
pushValue.id = ""
pushValue.nuId = uni.getStorageSync('nuId');
pushValue.nuName = uni.getStorageSync('nuName');
pushValue.elderId = uni.getStorageSync('elderId');
pushValue.elderName = uni.getStorageSync('NUall').elderInfo.name;
bottomItems.value.unshift(pushValue)
// 实现即时指令动画
cleansettimeout.value = setTimeout(() => {
bottomItems.value[0].target = `#fff`;
console.log("即时指令看看进入了啥", pushValue)
addInstant(pushValue).then((res : any) => {
if (res.success) {
geteverything()
}
})
}, 1500)
} else {
clearTimeout(cleansettimeoutrel.value);
isop.value = true;
cleansettimeoutrel.value = setTimeout(() => {
isop.value = false;
// saveAll()
}, 1500)
}
})
return
}
if (saveEditIndex.value.index1 === -1 && saveEditIndex.value.index0 === -1) {
return
}
flyNumber.value.index0 = -1;
flyNumber.value.index1 = -1;
isMove.value = false;
if (allobject.cycleTypeId == 2 && iszhouqi.value && weekIndex.value == -1 && monthIndex.value == -1) {
return
}
else if (allobject.cycleTypeId == 2 && !iszhouqi.value) {
iszhouqi.value = true
return
}
let cycleType = "";
let cycleValue = "";
if (allobject.cycleTypeId == 1) {
cycleType = "日常"
} else {
if (weekIndex.value != -1) {
cycleType = weekValue.value
let cycleTypeIndex = 0;
weekDays.forEach((element : any, index : any) => {
if (element == weekValue.value) {
cycleTypeIndex = index
}
})
cycleValue = cycleTypeIndex.toString()
} else {
cycleType = monthValue.value + "号";
cycleValue = monthValue.value
}
iszhouqi.value = false;
weekIndex.value = -1;
monthIndex.value = -1;
weekValue.value = "";
monthValue.value = "";
}
const startHour = Number(saveEditIndex.value.index0)
const startMinute = Number(timearr.value[saveEditIndex.value.index0].children[saveEditIndex.value.index1].tagName)
const endMinute = startMinute + Number(allobject.serviceDuration)
const endHour = startHour + Math.floor(endMinute / 60)
const formattedStart = `${String(startHour)}:${String(startMinute).padStart(2, '0')}`
const formattedEnd = `${String(endHour)}:${String(endMinute % 60).padStart(2, '0')}`
let param = {
id: haveValue ? timearr.value[saveEditIndex.value.index0].children[saveEditIndex.value.index1].id : "",
nuId: uni.getStorageSync('nuId'),
nuName: uni.getStorageSync('nuName'),
elderId: uni.getStorageSync('elderId'),
elderName: uni.getStorageSync('NUall').elderInfo.name,
directiveId: allobject.id,
directiveName: allobject.title,
typeId: allobject.typeId,
typeName: allobject.typeName,
categoryId: allobject.categoryId,
categoryName: allobject.categoryName,
cycleTypeId: allobject.cycleTypeId,
cycleType: cycleType,
cycleValue: cycleValue,
startTime: formattedStart,
endTime: formattedEnd,
positioning: saveEditIndex.value.index0.toString(),
positioningLong: saveEditIndex.value.index1.toString(),
izPackage: 'N',
previewFile: allobject.previewFile,
previewFileSmall: allobject.previewFileSmall,
serviceDuration: allobject.serviceDuration,
immediateFile: allobject.immediateFile,
immediateFileFocus: allobject.immediateFileFocus,
2026-01-20 15:35:05 +08:00
netImmediateFileFocus: allobject.netImmediateFileFocus,
2026-01-16 14:28:12 +08:00
netImmediateFile: allobject.netImmediateFile,
tagName: timearr.value[saveEditIndex.value.index0].children[saveEditIndex.value.index1].tagName,
mp3File: allobject.mp3File,
netMp3File: allobject.netMp3File,
mp4File: allobject.mp4File,
netMp4File: allobject.netMp4File,
serviceContent: allobject.serviceContent,
2026-01-20 15:35:05 +08:00
netPreviewFile: allobject.netPreviewFile,
netPreviewFileSmall: allobject.netPreviewFileSmall,
2026-01-16 14:28:12 +08:00
}
//给表格赋值
timearr.value[saveEditIndex.value.index0].children[saveEditIndex.value.index1] = param;
let data = {
index0: saveEditIndex.value.index0,
index1: saveEditIndex.value.index1
}
whereEvent(data);
if (haveValue) {
editDirective(param).then((res : any) => {
if (res.success) {
geteverything()
}
})
} else {
doaddDirective(param);
}
}
const cleansettimeout = ref(null);
const cleansettimeoutrel = ref(null);
const saveAll = () => {
if (!cansumit.value) {
return
}
let info = []
bodyTagList.value.forEach((element : any) => {
if (element.izSelected == 'Y') {
info.push(element)
}
})
emotionTagList.value.forEach((element : any) => {
if (element.izSelected == 'Y') {
info.push(element)
}
})
let allvalue = {
nuId: uni.getStorageSync('nuId'),
nuName: uni.getStorageSync('nuName'),
elderId: uni.getStorageSync('elderId'),
elderName: uni.getStorageSync('NUall').elderInfo.name,
tagList: info
}
addBatch(allvalue).then(() => {
geteverything()
})
}
const routerPush = () => {
uni.navigateTo({
url: `/pages/timeMatrix/indexnew`
})
}
// 暂存器
const saveRulerTime = ref({
index0: -1,
index1: -1
})
const targetRuler = ref({
index0: -1,
index1: -1,
current: -1,
bordershow: true
})
const solveWatch = ref(0)
const whereEvent = (data : any) => {
saveEditIndex.value.index0 = data.index0;
saveEditIndex.value.index1 = data.index1;
centerCell();
targetRuler.value.index0 = data.index0;
targetRuler.value.index1 = data.index1;
saveRulerTime.value.index0 = targetRuler.value.index0;
saveRulerTime.value.index1 = targetRuler.value.index1;
targetRuler.value.bordershow = false;
setTimeout(() => {
targetRuler.value.index0 = -1;
targetRuler.value.index1 = -1;
targetRuler.value.current = -1
}, 400)
setTimeout(() => {
targetRuler.value.bordershow = true;
saveRulerTime.value.index0 = -1;
saveRulerTime.value.index1 = -1;
}, 1000)
}
// 定义每小时中的分钟数组,表示每 5 分钟一个时间段,总共 12 个
const minuteArr = ['00', '05', '10', '15', '20', '25', '30', '35', '40', '45', '50', '55']
const timearr = ref(
Array.from({ length: 24 }, (_, hour) => ({
positioning: hour.toString(),
children: minuteArr.map(time => ({
tagName: time, // 表示分钟,如 '00', '05' 等
directiveName: '' // 默认的 directiveName
}))
}))
)
const emotionTagList = ref([]);
const bodyTagList = ref([]);
const geteverything = () => {
if (uni.getStorageSync('nuId') && uni.getStorageSync('elderId')) {
getNclist(uni.getStorageSync('nuId'), uni.getStorageSync('elderId')).then((res : any) => {
timearr.value = Array.from({ length: 24 }, (_, hour) => ({
positioning: hour.toString(),
children: minuteArr.map(time => ({
tagName: time, // 表示分钟,如 '00', '05' 等
directiveName: '' // 默认的 directiveName
}))
}))
res.result.serviceList.forEach((res : any) => {
timearr.value[res.positioning].children[res.positioningLong] = res;
})
bottomItems.value = res.result.instantList
cansumit.value = true;
emotionTagList.value = res.result.emotionTagList;
emotionTagListLook.value = []
bodyTagListLook.value = []
res.result.emotionTagList.forEach((res : any) => {
if (res.izSelected == 'Y') {
emotionTagListLook.value.push(res.netPic)
}
})
bodyTagList.value = res.result.bodyTagList
res.result.bodyTagList.forEach((res : any) => {
if (res.izSelected == 'Y') {
bodyTagListLook.value.push(res.netPic)
}
})
})
}
}
const savePackagelist = ref([]);
onMounted(() => {
savePackagelist.value = uni.getStorageSync('Packagelist') || []
let res = uni.getStorageSync('saveTree0')
2026-01-19 14:18:45 +08:00
// console.log("00000",res)
2026-01-16 14:28:12 +08:00
let goodArray = []
myArray.forEach((element : any) => {
element.children.forEach((element1 : any) => {
goodArray.push({
name: element1.title,
url: element1.url,
})
})
})
secondtemp.value = goodArray
if (res.result) {
res.result.forEach((element : any) => {
if (element.netFlag == '0') {
doctorsayList.value.forEach((res : any) => {
if (res.name == element.title) {
element.url = res.url
}
})
} else {
element.url = element.animationPath
}
element.children.forEach((res1 : any) => {
if (res1.netFlag == '0') {
secondtemp.value.forEach((res2 : any) => {
if (res2.name == res1.title) {
res1.url = res2.url
}
})
} else {
res1.url = res1.animationPath
}
})
})
bigArray.value = res.result;
}
downList.value = bigArray.value[0].children
upmenuIndex.value = -1;
downdonghua.value = -1;
setTimeout(() => {
upmenuIndex.value = 0;
downdonghua.value = 0;
}, 50)
uni.$on('where', findback);
downdonghua.value = 0;
geteverything()
2026-01-20 15:35:05 +08:00
nextTick(() => {
timeNowMove()
})
2026-01-16 14:28:12 +08:00
})
2026-01-20 15:35:05 +08:00
const hournow = ref(new Date().getHours());
// 表格进来就给我居中
function timeNowMove() {
const cellCenterX = (hournow.value + 0.5) * 259;
let width = cellCenterX - visibleWidth / 2;
const totalWidth = totalColumns * 259;
cardLeft.value = Math.max(0, Math.min(width, totalWidth - visibleWidth)) / 2;
}
2026-01-16 14:28:12 +08:00
const bodyTagListLook = ref([]);
const emotionTagListLook = ref([]);
const cansumit = ref(false);
onBeforeUnmount(() => {
if (animTimer) clearTimeout(animTimer)
uni.$off('where', findback);
ticking = false;
})
function findback(data : any) {
solveWatch.value = 3;
whereEvent(data)
}
// 切割bigArray
function splitString(str) {
// 使用正则表达式找到所有括号的内容
let result = [];
let remainingStr = str;
// 正则匹配最外层括号(支持全角和半角)
let regex = /([^(]*)[(]([^)]+)[)]/;
while (regex.test(remainingStr)) {
let match = remainingStr.match(regex);
if (match) {
// 添加括号前的部分(去掉空白)
if (match[1].trim()) {
result.push(match[1].trim());
}
// 添加括号内的内容
if (match[2].trim()) {
result.push(match[2].trim());
}
// 更新剩余的字符串
remainingStr = remainingStr.replace(match[0], '').trim();
}
}
// 如果最后还有剩余部分,也加入结果
if (remainingStr.trim()) {
result.push(remainingStr.trim());
}
return result;
}
const totalColumns = 24; // 总列数
const totalRows = 11; // 总行数
2026-01-20 15:35:05 +08:00
const visibleWidth = 1295; // 可视区域宽度 (rpx),基于 scalcType * widthType ≈ 2220
const visibleHeight = 1225; // 可视区域高度 (rpx)假设显示约5行时 heightType = 102.5
2026-01-16 14:28:12 +08:00
function centerCell() {
if (saveEditIndex.value.index0 >= 0 && saveEditIndex.value.index0 <= totalColumns && saveEditIndex.value.index1 >= 0 && saveEditIndex.value.index1 <= totalRows) {
// 计算点击格子的中心位置 (rpx)
const cellCenterX = (saveEditIndex.value.index0 + 0.5) * 259;
2026-01-20 15:35:05 +08:00
const cellCenterY = (saveEditIndex.value.index1 + 0.5) * 245;
2026-01-16 14:28:12 +08:00
// 计算 scrollLeft 和 scrollTop使格子中心位于可视区域中心
cardLeft.value = cellCenterX - visibleWidth / 2;
scrollTop.value = cellCenterY - visibleHeight / 2;
// 计算网格总宽高
const totalWidth = totalColumns * 259;
2026-01-20 15:35:05 +08:00
const totalHeight = totalRows * 245;
2026-01-16 14:28:12 +08:00
// 限制 scrollLeft 和 scrollTop 在有效范围内
cardLeft.value = Math.max(0, Math.min(cardLeft.value, totalWidth - visibleWidth)) / 2;
// scrollTop.value = 0
scrollTop.value = Math.max(0, Math.min(scrollTop.value, totalHeight - visibleHeight)) / 2;
}
}
const iszhiling = ref(false)
const zhilingbao = () => {
iszhiling.value = !iszhiling.value
forthmenuIndex.value = 0;
}
const deletedonghua = ref(false);
</script>
<style lang="less" scoped>
// 主页的css
@import './index';
</style>