最新改版

This commit is contained in:
17792275749
2025-06-10 14:33:21 +08:00
parent 5b8d213f08
commit db94e5fee1
@@ -13,7 +13,9 @@
<div>体重管理行动员工</div>
<div>
<div>{{ currentData }}</div>
<div>活动开始第<span>{{ doingDay }}</span></div>
<div>
活动开始第<span>{{ doingDay }}</span>
</div>
</div>
</div>
<div></div>
@@ -29,26 +31,27 @@
</div>
<div class="staffContent" ref="staffContent">
<div
v-if="list && list.length"
class="staff-list-wrapper"
ref="staffList"
:style="{
transform: `translateY(${scrollPosition}px)`,
transition: isScrolling ? `transform ${scrollAnimationDuration}s linear` : 'none'
}"
@transitionend="isScrolling = false">
<div
class="staffContentList"
v-for="item in list"
:key="item.rank + item.departName + item.realName"
>
<div>{{ item.rank }}</div>
<div>{{ item.departName }}</div>
<div>{{ item.realName }}</div>
<div>{{ item.value }}</div>
</div>
transition: scrollAnimationDuration > 0 ? `transform ${scrollAnimationDuration}s linear` : 'none',
}"
>
<template v-if="list && list.length">
<div
class="staffContentList"
v-for="item in list"
:key="item.rank + item.departName + item.realName"
>
<div>{{ item.rank }}</div>
<div>{{ item.departName }}</div>
<div>{{ item.realName }}</div>
<div>{{ item.value }}</div>
</div>
</template>
</div>
<template v-else>
<template v-if="!list || list.length === 0">
<div class="nullData">
<div></div>
<div>~ 暂无体重管理信息 ~</div>
@@ -59,7 +62,6 @@
</div>
</template>
<template v-if="personnelType === 'department'">
<div class="title departmentTitle">
<div></div>
@@ -108,11 +110,17 @@ import { getCurrentDate } from "@/utils/times";
export default {
name: "dietaryNutritionTips",
data: function () { // 箭头函数转为普通函数
data: function () {
return {
setTimeoutParams: {
status: true,
times: 15, // 页面切换的间隔时间,单位:秒
retryDelay: 5, // 错误或无数据时重试/切换的延迟时间,单位:秒
scrollDelay: 2, // 滚动前停顿时间,单位:秒
scrollEndDelay: 2, // 滚动结束后等待时间,单位:秒
initialRenderDelay: 100, // 首次渲染延迟,确保 DOM 准备好
maxRefRetry: 10, // 获取 ref 的最大重试次数
refRetryInterval: 100, // 获取 ref 的重试间隔,单位:毫秒
},
requestConfig: {
@@ -127,232 +135,430 @@ export default {
currentData: "",
doingDay: "-",
list: [],
timerId: null, // 用于控制页面切换的定时器ID
timerId: null, // 用于控制页面切换或重试的定时器ID
isRequesting: false, // 防止重复请求
isScrolling: false, // 标志是否正在进行滚动动画
// 新增用于单次滚动的状态
scrollPosition: 0, // 滚动位置
scrollAnimationDuration: 0, // 滚动动画持续时间
isScrolling: false, // 标志是否正在进行滚动动画
initialDelayTimer: null, // 用于滚动前停顿的定时器ID
isPageVisible: true, // 页面是否可见
onTransitionEndHandlerRef: null, // 存储 transitionend 事件处理函数的引用
refRetryCount: 0, // 记录 $refs 获取的重试次数
mutationObserver: null, // MutationObserver 实例
};
},
created: function () { // 箭头函数转为普通函数
let url = this.$route.query.url;
created: function () {
const _this = this;
let url = _this.$route.query.url;
if (url) {
this.requestConfig.url = url;
_this.requestConfig.url = url;
}
let code = this.$route.query.orgCode;
let code = _this.$route.query.orgCode;
if (code) {
this.requestConfig.orgCode = code;
_this.requestConfig.orgCode = code;
}
this.currentData = getCurrentDate();
this.startSequentialLoading();
_this.currentData = getCurrentDate();
// 绑定页面可见性监听器
document.addEventListener("visibilitychange", _this.handleVisibilityChange);
// 组件创建后,首次启动流程时增加一个小的延迟,确保 DOM 结构基本准备好
_this.timerId = setTimeout(() => {
_this.startSequentialLoading();
}, _this.setTimeoutParams.initialRenderDelay);
},
mounted() {
// 在 mounted 钩子中初始化 MutationObserver
this.setupMutationObserver();
},
methods: {
/**
* 调用 Android 端的 postLog 方法,用于调试日志输出
*/
callAndroidPostLog(content) {
// Android 接口检查
if (typeof window.Android !== 'undefined' && window.Android.postLog) {
try {
window.Android.postLog(content);
} catch (e) {
console.error("调用 Android.postLog 失败:", e);
}
} else {
console.log("H5 Log (Android Interface Not Available):", content);
}
},
/**
* 设置 MutationObserver 来监控关键元素的 DOM 变化
*/
setupMutationObserver: function() {
const _this = this;
// 确保只设置一次
if (_this.mutationObserver) return;
const targetNode = _this.$el; // 监控组件的根元素
const config = { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] };
_this.mutationObserver = new MutationObserver((mutationsList) => {
for (let mutation of mutationsList) {
if (mutation.type === 'childList' || mutation.type === 'attributes') {
// 当 `staffContent` 或 `staffList` 相关的 DOM 发生变化时,尝试重新评估是否需要滚动
// 这里只是一个简单的触发机制,实际的滚动逻辑在 startSingleScroll 中处理
if (_this.personnelType === 'staff' && _this.isPageVisible && !_this.isScrolling && _this.list.length > 0) {
// 避免过于频繁的触发,仅在当前没有滚动且有数据时才检查
// 实际的滚动启动在 loadDataSequential 的 nextTick 中
// 此处只做辅助性监控,若发现异常再做处理
}
}
}
});
if (targetNode) {
_this.mutationObserver.observe(targetNode, config);
_this.callAndroidPostLog("MutationObserver 已设置在组件根元素上。");
} else {
_this.callAndroidPostLog("警告:无法获取组件根元素设置 MutationObserver。");
}
},
/**
* 页面可见性处理
*/
handleVisibilityChange: function () {
const _this = this;
const listWrapperEl = _this.$refs.staffList;
if (document.hidden) {
_this.isPageVisible = false;
_this.callAndroidPostLog("页面不可见,暂停所有流程。");
_this.clearAllTimers(); // 统一清除所有定时器和动画状态
if (listWrapperEl) {
listWrapperEl.style.transition = "none";
listWrapperEl.style.transform = "translateY(0px)";
void listWrapperEl.offsetWidth; // 强制浏览器重绘
}
} else {
_this.isPageVisible = true;
_this.callAndroidPostLog("页面可见,恢复所有流程。");
// 页面恢复可见时,重置重试计数并重新开始加载流程
_this.refRetryCount = 0;
_this.startSequentialLoading();
}
},
/**
* 统一清除所有定时器和滚动状态
*/
clearAllTimers: function() {
const _this = this;
if (_this.timerId) {
clearTimeout(_this.timerId);
_this.timerId = null;
}
if (_this.initialDelayTimer) {
clearTimeout(_this.initialDelayTimer);
_this.initialDelayTimer = null;
}
_this.isScrolling = false;
const listWrapperEl = _this.$refs.staffList;
if (listWrapperEl && _this.onTransitionEndHandlerRef) {
listWrapperEl.removeEventListener("transitionend", _this.onTransitionEndHandlerRef);
_this.onTransitionEndHandlerRef = null; // 清除引用
}
},
/**
* 页面切换回调函数,用于在员工和部门模式之间切换。
*/
pageTimerCallback: function () { // 箭头函数转为普通函数
// 切换人员类型,并重新启动加载和滚动流程
this.personnelType = this.personnelType === "department" ? "staff" : "department";
this.startSequentialLoading();
pageTimerCallback: function () {
const _this = this;
if (!_this.isPageVisible) {
_this.callAndroidPostLog("页面不可见,暂停 pageTimerCallback。");
return;
}
_this.refRetryCount = 0; // 每次切换页面或加载数据前重置重试计数
_this.personnelType = _this.personnelType === "department" ? "staff" : "department";
_this.startSequentialLoading();
},
/**
* 启动或重置顺序加载流程。
* 根据当前人员类型设置 pageSize,并调用数据加载方法。
*/
startSequentialLoading: function () { // 箭头函数转为普通函数
this.list = []; // 清空列表,准备加载新数据
if (this.personnelType === "staff") {
this.pageSize = 45;
} else {
this.pageSize = 10;
startSequentialLoading: function () {
const _this = this;
if (!_this.isPageVisible) {
_this.callAndroidPostLog("页面不可见,暂停 startSequentialLoading。");
return;
}
this.loadDataSequential();
_this.clearAllTimers(); // 确保在启动新流程前清除所有旧的定时器
_this.list = []; // 清空列表,准备加载新数据
if (_this.personnelType === "staff") {
_this.pageSize = 45;
} else {
_this.pageSize = 10;
}
_this.loadDataSequential();
},
/**
* 加载数据并根据人员类型处理滚动或定时器。
*/
loadDataSequential: function () { // 箭头函数转为普通函数
let this_ = this; // 缓存 this 指向
loadDataSequential: function () {
const _this = this;
// **保留 urlPrefix 变量**
let urlPrefix = "/health-intervene/api/anon";
let apiUrl = "";
// 根据人员类型构建 API URL
if (this_.personnelType === "staff") {
apiUrl = urlPrefix + "/userRank";
} else {
apiUrl = urlPrefix + "/deptRank";
}
let url = this_.requestConfig.agreement + "://" + this_.requestConfig.url + apiUrl + "?orgCode=" + this_.requestConfig.orgCode + "&pageNo=1&pageSize=" + this_.pageSize;
// 防止重复请求
if (this_.isRequesting) {
console.warn('请求正在进行中,跳过本次调用。');
if (!_this.isPageVisible) {
_this.callAndroidPostLog("页面不可见,暂停 loadDataSequential。");
return;
}
this_.isRequesting = true; // 设置请求状态为进行中
// 发送 HTTP 请求
httpRequest('POST', url, {}, {}).then(function (res) { // 回调函数使用普通函数
if (res.success && res.result && res.result.rankList) {
if (this_.personnelType === "staff") {
this_.doingDay =
res.result.doingDay === null ? "-" : res.result.doingDay;
if (_this.isRequesting) {
_this.callAndroidPostLog('请求正在进行中,跳过本次调用。');
return;
}
_this.isRequesting = true;
_this.clearAllTimers(); // 在发送请求前再次清除定时器
// **使用 urlPrefix 构建 apiUrl**
apiUrl = urlPrefix + (_this.personnelType === "staff" ? "/userRank" : "/deptRank");
let requestUrl = _this.requestConfig.agreement + "://" + _this.requestConfig.url + apiUrl + "?orgCode=" + _this.requestConfig.orgCode + "&pageNo=1&pageSize=" + _this.pageSize;
httpRequest("POST", requestUrl, {}, {})
.then(
(res) => {
if (res.success && res.result && res.result.rankList) {
if (_this.personnelType === "staff") {
_this.doingDay = res.result.doingDay === null ? "-" : res.result.doingDay;
}
_this.list = res.result.rankList; // 更新列表数据
_this.$nextTick(() => {
// 只有在 staff 模式且有数据时才尝试滚动
if (_this.personnelType === "staff" && _this.list.length > 0) {
_this.refRetryCount = 0; // 每次尝试滚动前重置重试计数
_this.startSingleScroll();
} else {
// 部门模式或员工无数据,直接设置页面切换定时器
_this.timerId = setTimeout(
_this.pageTimerCallback,
_this.setTimeoutParams.times * 1000
);
}
});
} else {
_this.list = []; // 数据不成功或无数据时清空列表
_this.callAndroidPostLog("请求成功但数据无效或为空,将在 " + _this.setTimeoutParams.retryDelay + " 秒后重试/切换。");
_this.timerId = setTimeout(
_this.pageTimerCallback,
_this.setTimeoutParams.retryDelay * 1000
);
}
}
this_.list = res.result.rankList; // 更新列表数据
} else {
this_.list = []; // 数据不成功或无数据时清空列表
}
}).catch(function (error) { // 回调函数使用普通函数
console.error("请求失败:", error);
this_.list = []; // 请求失败时清空列表
}).finally(function () { // 回调函数使用普通函数
this_.isRequesting = false; // 请求完成,重置请求状态
// 清除可能存在的页面切换定时器
if (this_.timerId) {
clearTimeout(this_.timerId);
this_.timerId = null;
}
// 清除可能存在的初始延迟定时器
if (this_.initialDelayTimer) {
clearTimeout(this_.initialDelayTimer);
this_.initialDelayTimer = null;
}
if (this_.setTimeoutParams.status) {
if (this_.personnelType === "staff" && this_.list.length > 0) {
// 员工模式下,如果数据存在,则在下一渲染周期启动单次滚动
this_.$nextTick(function () { // 回调函数使用普通函数
this_.startSingleScroll();
});
} else {
// 部门模式或员工无数据,直接设置页面切换定时器
this_.timerId = setTimeout(function () { // 回调函数使用普通函数
this_.pageTimerCallback();
}, this_.setTimeoutParams.times * 1000);
)
.catch(
(error) => {
_this.callAndroidPostLog("请求失败: " + JSON.stringify(error));
_this.list = []; // 请求失败时清空列表
_this.callAndroidPostLog('请求失败,将在 ' + _this.setTimeoutParams.retryDelay + ' 秒后重试/切换。');
_this.timerId = setTimeout(
_this.pageTimerCallback,
_this.setTimeoutParams.retryDelay * 1000
);
}
}
});
)
.finally(
() => {
_this.isRequesting = false;
}
);
},
/**
* 开始员工列表的单次滚动动画。
* 包含滚动前停顿、精确高度计算、动画重置和动画结束回调
* 确保滚动容器和列表元素已就绪
*/
startSingleScroll: function () { // 箭头函数转为普通函数
let contentEl = this.$refs.staffContent; // 滚动容器 (staffContent)
let listWrapperEl = this.$refs.staffList; // 实际滚动的元素 (staff-list-wrapper)
let this_ = this; // 缓存 this 指向,在 setTimeout 和 requestAnimationFrame 回调中使用
startSingleScroll: function () {
const _this = this;
const contentEl = _this.$refs.staffContent; // 滚动容器
const listWrapperEl = _this.$refs.staffList; // 实际滚动的元素
// 如果元素不存在或正在滚动中,则不执行
if (!contentEl || !listWrapperEl || this.isScrolling) {
if (!_this.isPageVisible) {
_this.callAndroidPostLog("页面不可见,暂停 startSingleScroll 内部执行。");
_this.timerId = setTimeout(_this.pageTimerCallback, _this.setTimeoutParams.retryDelay * 1000);
return;
}
// **重试机制:如果 refs 未就绪,进行重试**
if (!contentEl || !listWrapperEl || contentEl.clientHeight === 0 || listWrapperEl.offsetHeight === 0) {
_this.callAndroidPostLog(`Refs未就绪或尺寸为0。contentEl: ${contentEl}, listWrapperEl: ${listWrapperEl}, contentHeight: ${contentEl ? contentEl.clientHeight : 'N/A'}, listHeight: ${listWrapperEl ? listWrapperEl.offsetHeight : 'N/A'}`);
if (_this.refRetryCount < _this.setTimeoutParams.maxRefRetry) {
_this.refRetryCount++;
_this.callAndroidPostLog(`尝试获取refs,重试次数:${_this.refRetryCount}/${_this.setTimeoutParams.maxRefRetry}。将在 ${_this.setTimeoutParams.refRetryInterval}ms 后重试。`);
_this.initialDelayTimer = setTimeout(() => {
_this.startSingleScroll(); // 再次尝试启动滚动
}, _this.setTimeoutParams.refRetryInterval);
return; // 退出当前函数,等待重试
} else {
_this.callAndroidPostLog("致命错误:滚动元素staffContent或staffList多次尝试后仍未就绪或尺寸为0,放弃滚动。将切换页面。");
// 此时无法滚动,直接切换页面
_this.timerId = setTimeout(_this.pageTimerCallback, _this.setTimeoutParams.retryDelay * 1000);
return;
}
}
if (_this.isScrolling) {
_this.callAndroidPostLog("滚动正在进行中,跳过本次启动。");
return;
}
// 在每次启动滚动前,确保 CSS 动画属性被清除,防止旧动画残留
listWrapperEl.style.transition = "none";
_this.scrollPosition = 0; // 重置位置到顶部
listWrapperEl.style.transform = `translateY(${_this.scrollPosition}px)`;
void listWrapperEl.offsetWidth; // 强制浏览器重绘
let contentHeight = contentEl.clientHeight; // 容器的可见高度
let actualListHeight = 0; // 存储列表的真实总高度,包括 margin-bottom
let actualListHeight = 0;
// 确保列表项已经渲染,才能获取其高度
if (listWrapperEl.children.length > 0) {
let firstItem = listWrapperEl.children[0];
let itemHeight = firstItem.offsetHeight; // 元素自身高度 (内容+内边距+边框)
let itemMarginBottom = parseFloat(
window.getComputedStyle(firstItem).marginBottom
); // 获取计算后的 margin-bottom
// 累加所有元素的实际占据高度 (每个item的高度 + 它的margin-bottom)
let itemHeight = firstItem.offsetHeight;
let itemMarginBottom = parseFloat(window.getComputedStyle(firstItem).marginBottom);
actualListHeight = (itemHeight + itemMarginBottom) * listWrapperEl.children.length;
// 注意:如果你的设计是滚动到最后一个列表项的底部(而不是它下方margin-bottom的位置),
// 可以在这里减去最后一个元素的 margin-bottom
// if (listWrapperEl.children.length > 0) {
// actualListHeight -= itemMarginBottom;
// }
} else {
_this.callAndroidPostLog("staffList内部无子元素,无法计算滚动高度。将切换页面。");
// 无列表项时也直接切换页面
_this.timerId = setTimeout(_this.pageTimerCallback, _this.setTimeoutParams.times * 1000);
return;
}
// 如果列表实际高度小于或等于容器高度,则无需滚动
if (actualListHeight <= contentHeight) {
this.scrollAnimationDuration = 0;
this.scrollPosition = 0; // 确保位置重置为顶部
// 即使不滚动,也要在一段时间后切换到部门视图
this.timerId = setTimeout(function () { // 回调函数使用普通函数
this_.pageTimerCallback();
}, this.setTimeoutParams.times * 1000);
_this.scrollAnimationDuration = 0;
_this.scrollPosition = 0;
_this.callAndroidPostLog("列表高度 (" + actualListHeight + "px) 小于或等于容器高度 (" + contentHeight + "px),无需滚动。将切换页面。");
_this.timerId = setTimeout(_this.pageTimerCallback, _this.setTimeoutParams.times * 1000);
return;
}
this.isScrolling = true; // 标记开始滚动
let scrollDistance = actualListHeight - contentHeight; // 计算需要滚动的总距离
_this.isScrolling = true;
let scrollDistance = actualListHeight - contentHeight;
let scrollSpeed = 40; // 像素/秒
_this.scrollAnimationDuration = scrollDistance / scrollSpeed;
let scrollSpeed = 40; // 像素/秒,你可以根据需要调整这个值,值越小滚动越慢
this.scrollAnimationDuration = scrollDistance / scrollSpeed; // 计算动画持续时间(秒)
// --- 滚动前停顿 ---
_this.initialDelayTimer = setTimeout(
() => {
if (window.requestAnimationFrame) {
window.requestAnimationFrame(() => {
_this.performScrollAnimation(listWrapperEl, scrollDistance);
});
} else {
_this.performScrollAnimation(listWrapperEl, scrollDistance);
}
},
_this.setTimeoutParams.scrollDelay * 1000
);
},
// --- 关键重置步骤 ---
// 1. 立即重置 transform 属性到顶部 (0px)
listWrapperEl.style.transform = 'translateY(0px)';
// 2. 立即移除 transition 属性,确保没有旧动画的干扰
listWrapperEl.style.transition = 'none';
this.scrollPosition = 0; // 更新数据模型中的位置
// 统一处理 transitionend 事件,方便移除监听器
onTransitionEnd: function(event) {
const _this = this;
const listWrapperEl = _this.$refs.staffList;
if (event.target === listWrapperEl && event.propertyName === 'transform') {
listWrapperEl.removeEventListener("transitionend", _this.onTransitionEndHandlerRef);
_this.onTransitionEndHandlerRef = null; // 清除引用
_this.isScrolling = false;
_this.callAndroidPostLog("滚动动画完成。");
// 3. 强制浏览器重绘/回流,使其立即应用上述样式变化
// 这是确保动画从正确初始状态开始的关键一步。访问 offsetWidth/Height 等属性可以触发回流。
void listWrapperEl.offsetWidth;
// --- 滚动结束后,设置页面切换定时器 ---
_this.timerId = setTimeout(
_this.pageTimerCallback,
_this.setTimeoutParams.scrollEndDelay * 1000
);
}
},
// --- 新增:滚动前停顿一秒 ---
this.initialDelayTimer = setTimeout(function () { // 回调函数使用普通函数
// 在延迟结束后,通过 requestAnimationFrame 确保在浏览器下一次重绘前开始动画
requestAnimationFrame(function () { // 回调函数使用普通函数
// 设置目标位置(负值表示向上滚动)
this_.scrollPosition = -scrollDistance;
// 重新添加 transition 属性,启动平滑滚动
listWrapperEl.style.transition = 'transform ' + this_.scrollAnimationDuration + 's linear';
performScrollAnimation: function (listWrapperEl, scrollDistance) {
const _this = this;
// 监听动画结束事件
// 使用一个局部变量来存储事件处理函数,以便在事件触发后正确移除
let onTransitionEnd = function () { // 回调函数使用普通函数
listWrapperEl.removeEventListener('transitionend', onTransitionEnd); // 移除监听,防止重复触发
this_.isScrolling = false; // 标记滚动结束
// 移除任何旧的监听器,防止重复绑定
if (_this.onTransitionEndHandlerRef) {
listWrapperEl.removeEventListener("transitionend", _this.onTransitionEndHandlerRef);
_this.onTransitionEndHandlerRef = null;
}
// --- 滚动结束后,设置页面切换定时器 ---
this_.timerId = setTimeout(function () { // 回调函数使用普通函数
this_.pageTimerCallback();
}.bind(this_), 2000);
}.bind(this_); // 绑定 this_
listWrapperEl.addEventListener('transitionend', onTransitionEnd);
});
}, 2000); // 停顿 2000 毫秒(2秒)
_this.onTransitionEndHandlerRef = _this.onTransitionEnd.bind(_this);
listWrapperEl.addEventListener("transitionend", _this.onTransitionEndHandlerRef);
// 设置目标位置(负值表示向上滚动)
_this.scrollPosition = -scrollDistance;
listWrapperEl.style.transition = `transform ${_this.scrollAnimationDuration}s linear`;
listWrapperEl.style.transform = `translateY(${_this.scrollPosition}px)`; // 立即应用样式
_this.callAndroidPostLog(`开始滚动动画。距离:${scrollDistance}px,时长:${_this.scrollAnimationDuration}s`);
// 备用定时器,确保即使 transitionend 未触发也能继续流程
_this.timerId = setTimeout(
() => {
if (_this.isScrolling) {
_this.callAndroidPostLog("TransitionEnd 未触发,使用备用定时器强制页面切换。");
_this.isScrolling = false;
listWrapperEl.style.transition = "none";
listWrapperEl.style.transform = "translateY(0px)";
void listWrapperEl.offsetWidth; // 强制重绘
if (_this.onTransitionEndHandlerRef) {
listWrapperEl.removeEventListener("transitionend", _this.onTransitionEndHandlerRef);
_this.onTransitionEndHandlerRef = null;
}
_this.pageTimerCallback();
}
},
(_this.scrollAnimationDuration + _this.setTimeoutParams.scrollEndDelay) * 1000 + 500 // 动画持续时间 + 动画结束后等待时间 + 0.5秒缓冲
);
},
},
beforeDestroy: function () { // 箭头函数转为普通函数
// 组件销毁前清除所有可能存在的定时器,防止内存泄漏
if (this.timerId) {
clearTimeout(this.timerId);
this.timerId = null;
}
if (this.initialDelayTimer) {
clearTimeout(this.initialDelayTimer);
this.initialDelayTimer = null;
beforeDestroy: function () {
const _this = this;
_this.clearAllTimers(); // 清除所有定时器
// 确保在组件销毁时,正在进行的 CSS 动画被停止并归位
const listWrapperEl = _this.$refs.staffList;
if (listWrapperEl) {
listWrapperEl.style.transition = "none";
listWrapperEl.style.transform = "translateY(0px)";
void listWrapperEl.offsetWidth;
}
// 确保移除可能残余的 transitionend 监听器,特别是在动画未完成时组件被销毁的情况
let listWrapperEl = this.$refs.staffList;
if (listWrapperEl) {
// 由于 onTransitionEnd 是在 startSingleScroll 内部定义的局部函数,
// 且每次调用 startSingleScroll 都会创建一个新的 onTransitionEnd
// 因此这里无法直接移除特定的命名函数。
// 对于当前实现,由于事件会在结束后自行移除,且 setTimeout 会被清除,
// 通常情况下不会造成严重问题。
// 移除页面可见性监听器
document.removeEventListener("visibilitychange", _this.handleVisibilityChange);
// 断开 MutationObserver
if (_this.mutationObserver) {
_this.mutationObserver.disconnect();
_this.mutationObserver = null;
_this.callAndroidPostLog("MutationObserver 已断开。");
}
},
};
</script>
<style scoped lang="less">
/* 样式部分保持不变 */
#dietaryNutritionTips2 {
width: 100%;
height: 100%;
@@ -361,10 +567,10 @@ export default {
flex-direction: column;
padding: 44px 40px 0;
box-sizing: border-box;
background-size: cover;
background-repeat: no-repeat;
background-position: center top;
background-image: url("@/assets/images/dietaryNutritionTips/bg.png");
//background-size: cover;
//background-repeat: no-repeat;
//background-position: center top;
//background-image: url("@/assets/images/dietaryNutritionTips/bg.png");
.tips {
width: 100%;
@@ -512,7 +718,11 @@ export default {
padding: 105px 24px 0;
box-sizing: border-box;
border: 2px solid #ffc548;
background: linear-gradient(180deg, rgba(225, 225, 225, 0) 0%, rgba(225, 225, 225, 0.3) 100%);
background: linear-gradient(
180deg,
rgba(225, 225, 225, 0) 0%,
rgba(225, 225, 225, 0.3) 100%
);
.staff {
width: 100%;
@@ -562,9 +772,11 @@ export default {
width: 100%;
overflow: hidden;
height: calc(100% - 152px); // 根据实际高度调整
position: relative; // 为 nullData 定位提供基础
.staff-list-wrapper {
width: 100%;
min-height: 1px; // 确保即使无子元素也有高度,避免 refs 无法获取
}
.staffContentList {
@@ -658,6 +870,7 @@ export default {
width: 100%;
overflow: hidden;
height: calc(100% - 142px);
position: relative; // 为 nullData 定位提供基础
.departmentContentList {
width: 100%;
@@ -716,7 +929,13 @@ export default {
.nullData {
width: 100%;
padding-top: 350px;
// 定位到容器中心,确保在有滚动列表时不会覆盖
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding-top: 0; // 移除之前的 paddingTop
text-align: center; // 确保内容居中
> div:nth-of-type(1) {
width: 280px;