隐患详情重新设计,检查计划重新设计成做题排查式

This commit is contained in:
王利强
2026-06-21 16:30:12 +08:00
parent 1fe87ec438
commit cc94d3e3e9
256 changed files with 12542 additions and 5956 deletions

View File

@@ -0,0 +1,164 @@
import { ref, watch, nextTick, getCurrentInstance } from 'vue';
import { onReady } from '@dcloudio/uni-app';
import { generateHazardHistory } from './hazardDetail.js';
/**
* 隐患详情左右联动滚动(与 HazardDetailPanel 保持一致)
* - 右侧滚动时,根据 node-section 位置同步左侧 activeIndex
* - 点击左侧步骤时,右侧 scroll-into-view 定位到对应卡片
*/
export function useHazardDetailScroll(detailSource, loadingSource) {
const instance = getCurrentInstance();
const queryScope = instance?.proxy || instance;
const historyList = ref([]);
const activeIndex = ref(0);
const sectionOffsets = ref([0]);
const contentViewHeight = ref(0);
const contentScrollIntoView = ref('');
const stepScrollIntoView = ref('');
const scrollWithAnimation = ref(true);
const isProgrammaticScroll = ref(false);
let measureLayoutTimer = null;
const measureLayout = () => {
if (!historyList.value.length) return;
nextTick(() => {
const query = uni.createSelectorQuery().in(queryScope);
query.select('.content-scroll').boundingClientRect();
query.select('.content-scroll').scrollOffset();
query.selectAll('.node-section').boundingClientRect();
query.exec((res) => {
const containerRect = res?.[0];
const scrollOffset = res?.[1];
const sections = res?.[2] || [];
if (!containerRect || !sections.length) return;
contentViewHeight.value = containerRect.height || 0;
const baseScrollTop = scrollOffset?.scrollTop || 0;
sectionOffsets.value = sections.map(
(section) => section.top - containerRect.top + baseScrollTop
);
});
});
};
const scheduleMeasureLayout = () => {
clearTimeout(measureLayoutTimer);
measureLayoutTimer = setTimeout(() => {
measureLayout();
}, 80);
};
const rebuildHistory = (data) => {
historyList.value = generateHazardHistory(data);
activeIndex.value = 0;
contentScrollIntoView.value = '';
scheduleMeasureLayout();
setTimeout(scheduleMeasureLayout, 300);
};
const syncActiveIndex = (scrollTop, scrollHeight = 0) => {
const count = historyList.value.length;
if (!count) return;
if (scrollHeight > 0 && contentViewHeight.value > 0) {
if (scrollTop + contentViewHeight.value >= scrollHeight - 60) {
if (activeIndex.value !== count - 1) {
activeIndex.value = count - 1;
}
return;
}
}
const offsets = sectionOffsets.value;
if (!offsets.length) return;
let idx = 0;
for (let i = offsets.length - 1; i >= 0; i--) {
if (scrollTop >= offsets[i] - 80) {
idx = i;
break;
}
}
if (idx !== activeIndex.value) {
activeIndex.value = idx;
}
};
const onContentScroll = (e) => {
if (isProgrammaticScroll.value) return;
const { scrollTop = 0, scrollHeight = 0 } = e.detail || {};
syncActiveIndex(scrollTop, scrollHeight);
scheduleMeasureLayout();
};
const onScrollToLower = () => {
const count = historyList.value.length;
if (count > 0 && activeIndex.value !== count - 1) {
activeIndex.value = count - 1;
}
};
const scrollToNode = (index) => {
if (index < 0 || index >= historyList.value.length) return;
isProgrammaticScroll.value = true;
scrollWithAnimation.value = true;
activeIndex.value = index;
contentScrollIntoView.value = 'hazard-node-' + index;
setTimeout(() => {
contentScrollIntoView.value = '';
isProgrammaticScroll.value = false;
scheduleMeasureLayout();
}, 350);
};
watch(
() => (typeof loadingSource === 'function' ? loadingSource() : loadingSource?.value),
(loading) => {
if (!loading) {
scheduleMeasureLayout();
setTimeout(scheduleMeasureLayout, 300);
}
}
);
watch(
() => (typeof detailSource === 'function' ? detailSource() : detailSource?.value),
(val) => rebuildHistory(val),
{ immediate: true, deep: true }
);
watch(activeIndex, (index) => {
stepScrollIntoView.value = 'hazard-step-' + index;
setTimeout(() => {
stepScrollIntoView.value = '';
}, 300);
});
watch(
() => historyList.value.length,
() => scheduleMeasureLayout()
);
onReady(() => {
scheduleMeasureLayout();
setTimeout(scheduleMeasureLayout, 300);
});
return {
historyList,
activeIndex,
contentScrollIntoView,
stepScrollIntoView,
scrollWithAnimation,
onContentScroll,
onScrollToLower,
scrollToNode,
scheduleMeasureLayout
};
}