apple 3 дней назад
Родитель
Сommit
57d9bd9315
9 измененных файлов с 2578 добавлено и 2160 удалено
  1. 149 142
      js/mp_base/base.js
  2. 196 105
      js/mp_common/common.js
  3. 5 4
      js/mp_request/request.js
  4. 295 106
      js/mp_ss_components/mp-ss-components.js
  5. 426 409
      page/autoLogin.html
  6. 4 0
      page/login.html
  7. 1 1
      page/mp_objInp.html
  8. 1416 1319
      page/mp_objList.html
  9. 86 74
      skin/mp_easy/base.css

+ 149 - 142
js/mp_base/base.js

@@ -1,149 +1,156 @@
 window.ss = {
-	dom: {
-		TYPE: {
-			INPUT: 1,
-			OBJP: 2,
-			DATE: 3,
-			TIME: 4,
-			DATETIME: 5,
-			ONOFFBTN: 6,
-			PIC: 7,
-			HTML: 9,
-			CCPSINGLE: 51, //只对应一个对象属性的,带编码规则的级联菜单,如“出生地区码”(对应省、市、区)
-			CCPMUTIPLE: 52, //对应多个对象属性的级联菜单
-			SEARCHINPUT: 31, //查询页用到的文本框
-			SEARCHDATE: 33, //查询页用到的日期选择
-			SEARCHTIME: 34, //查询页用到的时间选择
-			SEARCHDATETIME: 35, //查询页用到的日期时间选择
-		},
-	},
-	config: {},
+  dom: {
+    TYPE: {
+      INPUT: 1,
+      OBJP: 2,
+      DATE: 3,
+      TIME: 4,
+      DATETIME: 5,
+      ONOFFBTN: 6,
+      PIC: 7,
+      HTML: 9,
+      CCPSINGLE: 51, //只对应一个对象属性的,带编码规则的级联菜单,如“出生地区码”(对应省、市、区)
+      CCPMUTIPLE: 52, //对应多个对象属性的级联菜单
+      SEARCHINPUT: 31, //查询页用到的文本框
+      SEARCHDATE: 33, //查询页用到的日期选择
+      SEARCHTIME: 34, //查询页用到的时间选择
+      SEARCHDATETIME: 35, //查询页用到的日期时间选择
+    },
+  },
+  config: {},
 };
 
 window.SS = window.ss; //最终都用小写ss,暂时先兼容大写SS
 
 (function () {
-	// ========== 移动端适配:设置根元素font-size ==========
-	// 设计稿宽度430px,分成10份,1rem = 43px(在430px屏幕上)
-	function setRootFontSize() {
-		const designWidth = 430; // 设计稿宽度
-		const rootValue = designWidth / 10; // 43px
-		const clientWidth = document.documentElement.clientWidth || window.innerWidth;
-
-		// 计算当前屏幕对应的根font-size
-		const rootFontSize = (clientWidth / designWidth) * rootValue;
-
-		// 设置根元素font-size
-		document.documentElement.style.fontSize = rootFontSize + 'px';
-
-		// 调试信息(可选)
-		// console.log(`屏幕宽度: ${clientWidth}px, 根font-size: ${rootFontSize}px, 1rem = ${rootFontSize}px`);
-	}
-
-	// 立即执行
-	setRootFontSize();
-
-	// 监听窗口大小变化
-	window.addEventListener('resize', setRootFontSize);
-
-	// 监听屏幕旋转
-	window.addEventListener('orientationchange', setRootFontSize);
-	// ========== 移动端适配结束 ==========
-
-	const _deps = [
-		['script', '/js/mp_jq/jquery1.11.3.min.js', ''],
-		['script', '/js/mp_vue/vue.global.js', ''],
-		['script', '/js/mp_axios/axios.min.js', ''],
-		// 通用富文本依赖(PC/H5共享目录) by xu 2026-03-01
-		['style', '/skin/easy/css/jodit.css'],
-		['script', '/js/jodit/jodit.js', ''],
-		['script', '/js/mp_jweixin/jweixin-1.6.0.js', ''],
-		['script', '/js/mp_common/common.js', ''],
-		['script', '/js/mp_toast/toast.js', ''],
-		['script', '/js/mp_request/request.js', ''],
-		['script', '/js/mp_user_api/user-api.js', ''],
-		['script', '/js/mp_validator/validator-rules.js', ''],
-		['script', '/js/mp_validation/validation-manager.js', ''],
-		['script', '/js/mp_ss_components/mp-ss-components.js', ''],
-		['script', '/js/mp_bridge/bridge.js', ''],
-		['script', '/js/mp_utils/field-formatter.js', ''],
-		['script', '/js/mp_utils/navigation.js', ''],
-		// 引入 Vant UI 库
-		['script', '/js/mp_vant/vant.min.js'],
-		['style', '/skin/mp_easy/vant.min.css'],
-
-		// 引入 Day.js 日期处理库
-		['script', '/js/mp_dayjs/dayjs.min.js'],
-
-		// 引入 Cropper.js 图片裁剪库
-		['script', '/js/mp_cropper/cropper.min.js'],
-		['style', '/skin/mp_easy/cropper.min.css'],
-
-		// ['style', '/skin/mp_easy/base.css'],
-		['style', '/skin/mp_easy/base.css'],
-		['style', '/skin/mp_easy/iconfont.css'],
-	];
-
-	function loadResource(type, src, module) {
-		return new Promise((resolve, reject) => {
-			const elem = document.createElement(
-				type === 'script' ? 'script' : 'link'
-			);
-
-			if (type === 'script') {
-				elem.src = src;
-				elem.type = module ? 'module' : '';
-			} else {
-				elem.rel = 'stylesheet';
-				elem.href = src;
-			}
-
-			elem.onload = () => {
-				if (type === 'script') {
-					// elem.remove();
-				}
-				resolve();
-			};
-
-			elem.onerror = reject;
-			document.head.appendChild(elem);
-		});
-	}
-
-	// 等待所有依赖加载完成后再执行回调
-	window.SS.ready = function (callback) {
-		if (window.SS && window.SS.dom && window.SS.dom.initializeFormApp) {
-			callback();
-		} else {
-			document.addEventListener('SSReady', () => {
-				// 确保组件初始化完成后再执行回调
-				if (
-					window.SS &&
-					window.SS.dom &&
-					window.SS.dom.initializeFormApp
-				) {
-					callback();
-				}
-			});
-		}
-	};
-
-	async function loadAll() {
-		// 然后加载资源
-		for (const [type, src, module] of _deps) {
-			await loadResource(type, src, module);
-			if (src.includes('ace.js')) {
-				window.ace.require('ace/config').set('workerPath', null);
-			}
-		}
-		document.dispatchEvent(new Event('SSReady'));
-	}
-
-	// 当 DOM 加载完成后执行清理和加载
-	if (document.readyState === 'loading') {
-		document.addEventListener('DOMContentLoaded', loadAll);
-		console.log('DOMContentLoaded');
-	} else {
-		loadAll();
-	}
+  // ========== 移动端适配:设置根元素font-size ==========
+  // 设计稿宽度430px,分成10份,1rem = 43px(在430px屏幕上)
+  function setRootFontSize() {
+    const designWidth = 430; // 设计稿宽度
+    const rootValue = designWidth / 10; // 43px
+    const clientWidth =
+      document.documentElement.clientWidth || window.innerWidth;
+
+    // 计算当前屏幕对应的根font-size
+    const rootFontSize = (clientWidth / designWidth) * rootValue;
+
+    // 设置根元素font-size
+    document.documentElement.style.fontSize = rootFontSize + "px";
+
+    // 调试信息(可选)
+    // console.log(`屏幕宽度: ${clientWidth}px, 根font-size: ${rootFontSize}px, 1rem = ${rootFontSize}px`);
+  }
+
+  // 立即执行
+  setRootFontSize();
+
+  // 监听窗口大小变化
+  window.addEventListener("resize", setRootFontSize);
+
+  // 监听屏幕旋转
+  window.addEventListener("orientationchange", setRootFontSize);
+  // ========== 移动端适配结束 ==========
+
+  const _deps = [
+    ["script", "/js/mp_jq/jquery1.11.3.min.js", ""],
+    ["script", "/js/mp_vue/vue.global.js", ""],
+    ["script", "/js/mp_axios/axios.min.js", ""],
+    // 通用富文本依赖(PC/H5共享目录) by xu 2026-03-01
+    ["style", "/skin/easy/css/jodit.css"],
+    ["script", "/js/jodit/jodit.js", ""],
+    ["script", "/js/mp_jweixin/jweixin-1.6.0.js", ""],
+    ["script", "/js/mp_common/common.js", ""],
+    ["script", "/js/mp_toast/toast.js", ""],
+    ["script", "/js/mp_request/request.js", ""],
+    ["script", "/js/mp_user_api/user-api.js", ""],
+    ["script", "/js/mp_validator/validator-rules.js", ""],
+    ["script", "/js/mp_validation/validation-manager.js", ""],
+    ["script", "/js/mp_ss_components/mp-ss-components.js", ""],
+    ["script", "/js/mp_bridge/bridge.js", ""],
+    ["script", "/js/mp_utils/field-formatter.js", ""],
+    ["script", "/js/mp_utils/navigation.js", ""],
+    // 引入 Vant UI 库
+    ["script", "/js/mp_vant/vant.min.js"],
+    ["style", "/skin/mp_easy/vant.min.css"],
+
+    // 引入 Day.js 日期处理库
+    ["script", "/js/mp_dayjs/dayjs.min.js"],
+
+    // 引入 Cropper.js 图片裁剪库
+    ["script", "/js/mp_cropper/cropper.min.js"],
+    ["style", "/skin/mp_easy/cropper.min.css"],
+
+    // ['style', '/skin/mp_easy/base.css'],
+    ["style", "/skin/mp_easy/base.css"],
+    ["style", "/skin/mp_easy/iconfont.css"],
+  ];
+
+  function loadResource(type, src, module) {
+    return new Promise((resolve, reject) => {
+      const elem = document.createElement(
+        type === "script" ? "script" : "link"
+      );
+
+      if (type === "script") {
+        elem.src = src;
+        elem.type = module ? "module" : "";
+      } else {
+        elem.rel = "stylesheet";
+        elem.href = src;
+      }
+
+      elem.onload = () => {
+        if (type === "script") {
+          // elem.remove();
+        }
+        resolve();
+      };
+
+      elem.onerror = reject;
+      document.head.appendChild(elem);
+    });
+  }
+
+  // 等待所有依赖加载完成后再执行回调
+  window.SS.ready = function (callback) {
+    if (window.SS && window.SS.dom && window.SS.dom.initializeFormApp) {
+      callback();
+    } else {
+      document.addEventListener("SSReady", () => {
+        // 确保组件初始化完成后再执行回调
+        if (window.SS && window.SS.dom && window.SS.dom.initializeFormApp) {
+          callback();
+        }
+      });
+    }
+  };
+
+  async function loadAll() {
+    // 然后加载资源
+    for (const [type, src, module] of _deps) {
+      await loadResource(type, src, module);
+      if (src.includes("ace.js")) {
+        window.ace.require("ace/config").set("workerPath", null);
+      }
+    }
+
+    // 所有 H5 页面公共依赖加载完成后,先判断是否需要做消息直达登录分流。
+    // 如果这里已经跳去了 login/autoLogin,就直接终止当前页面初始化,避免业务页先渲染再被重定向。
+    if (typeof window.handleMessageEntryAuthRedirect === "function") {
+      const redirected = window.handleMessageEntryAuthRedirect();
+      if (redirected) {
+        return;
+      }
+    }
+
+    document.dispatchEvent(new Event("SSReady"));
+  }
+
+  // 当 DOM 加载完成后执行清理和加载
+  if (document.readyState === "loading") {
+    document.addEventListener("DOMContentLoaded", loadAll);
+    console.log("DOMContentLoaded");
+  } else {
+    loadAll();
+  }
 })();

+ 196 - 105
js/mp_common/common.js

@@ -2,88 +2,160 @@
  * H5公共工具函数
  * 避免在多个文件中重复定义相同的函数
  */
+const ENTRY_SOURCE_KEY = "entrySource";
+const ENTRY_SOURCE_MY = "my";
+const AUTH_REDIRECT_DONE_KEY = "authRedirectDone";
 
 // 获取URL参数的工具函数
 function getUrlParams() {
-  const params = {}
-  const urlSearchParams = new URLSearchParams(window.location.search)
+  const params = {};
+  const urlSearchParams = new URLSearchParams(window.location.search);
   for (const [key, value] of urlSearchParams) {
-    params[key] = decodeURIComponent(value)
+    params[key] = decodeURIComponent(value);
   }
-  return params
+  return params;
+}
+
+function isAuthPage(pathname = window.location.pathname) {
+  return /\/(login|autoLogin)\.html$/i.test(pathname || "");
+}
+
+function getCurrentPageUrl() {
+  return `${window.location.origin}${window.location.pathname}${window.location.search}`;
+}
+
+function getStoredAuthToken() {
+  const userInfo = localStorage.getItem("userInfo");
+  if (userInfo) {
+    try {
+      const parsed = JSON.parse(userInfo);
+      const token = parsed?.yhsbToken || parsed?.onlineToken || "";
+      if (token) {
+        return token;
+      }
+    } catch (error) {
+      console.warn("解析 userInfo 获取 token 失败:", error);
+    }
+  }
+
+  return (
+    localStorage.getItem("yhsbToken") ||
+    localStorage.getItem("onlineToken") ||
+    ""
+  );
+}
+
+// 小程序进入 H5 时,只有 my 页面主动点击进入的场景才会带 entrySource=my。
+// 因此这里把“from=miniprogram 且不是认证页 且没有 entrySource=my”的页面
+// 统一视为消息直达等需先认证的入口,进入登录/自动登录分流。
+// 但如果已经带了 authRedirectDone=1,说明当前页是登录成功后回跳回来的,
+// 本次不应再重复进入登录分流,否则会在业务页和 autoLogin.html 之间循环跳转。
+function isMiniProgramMessageEntry(params = getUrlParams()) {
+  return (
+    params.from === "miniprogram" &&
+    params[ENTRY_SOURCE_KEY] !== ENTRY_SOURCE_MY &&
+    params[AUTH_REDIRECT_DONE_KEY] !== "1" &&
+    !isAuthPage()
+  );
+}
+
+// 公共层在每个业务 H5 页面初始化前都会先调用这个函数。
+// 一旦命中消息直达分流,就立即跳到 login.html 或 autoLogin.html,
+// 并把当前业务页完整地址放到 redirect 参数里,登录成功后再尝试回到这里。
+function handleMessageEntryAuthRedirect() {
+  const params = getUrlParams();
+  if (!isMiniProgramMessageEntry(params)) {
+    return false;
+  }
+
+  const redirectUrl = getCurrentPageUrl();
+  const token = getStoredAuthToken();
+  const authPage = token ? "autoLogin" : "login";
+  const authUrl = `/page/${authPage}.html?entryScene=message&redirect=${encodeURIComponent(
+    redirectUrl
+  )}`;
+
+  console.log("🔐 检测到消息直达页面,准备跳转认证页:", {
+    authPage,
+    redirectUrl,
+    hasToken: !!token,
+  });
+
+  window.location.replace(authUrl);
+  return true;
 }
 
 // 获取设备信息 (优先从URL参数获取,备用从localStorage获取)
 function getDeviceInfo() {
-  const urlParams = getUrlParams()
+  const urlParams = getUrlParams();
 
   // 优先从URL参数获取
-  let deviceId = urlParams.devId || ''
-  let model = urlParams.sbmc || ''
+  let deviceId = urlParams.devId || "";
+  let model = urlParams.sbmc || "";
 
   // 如果URL参数中没有,尝试从localStorage获取
   if (!deviceId || !model) {
-    const cachedDeviceInfo = localStorage.getItem('deviceInfo')
+    const cachedDeviceInfo = localStorage.getItem("deviceInfo");
     if (cachedDeviceInfo) {
       try {
-        const parsed = JSON.parse(cachedDeviceInfo)
-        deviceId = deviceId || parsed.deviceId || ''
-        model = model || parsed.model || ''
-        console.log('🔄 从localStorage获取设备信息:', { deviceId, model })
+        const parsed = JSON.parse(cachedDeviceInfo);
+        deviceId = deviceId || parsed.deviceId || "";
+        model = model || parsed.model || "";
+        console.log("🔄 从localStorage获取设备信息:", { deviceId, model });
       } catch (e) {
-        console.warn('解析localStorage中的设备信息失败:', e)
+        console.warn("解析localStorage中的设备信息失败:", e);
       }
     }
   }
 
   // 如果获取到了设备信息,保存到localStorage作为备用
   if (deviceId && model) {
-    const deviceInfo = { deviceId, model }
-    localStorage.setItem('deviceInfo', JSON.stringify(deviceInfo))
+    const deviceInfo = { deviceId, model };
+    localStorage.setItem("deviceInfo", JSON.stringify(deviceInfo));
   }
 
   return {
     deviceId: deviceId,
-    model: model
-  }
+    model: model,
+  };
 }
 
 // 获取JSESSIONID (多源获取,确保可用性)
 function getJSessionId() {
   // 优先级:localStorage > URL参数 > Cookie
-  let jsessionId = localStorage.getItem('JSESSIONID')
-  
+  let jsessionId = localStorage.getItem("JSESSIONID");
+
   if (!jsessionId) {
-    const urlParams = getUrlParams()
-    jsessionId = urlParams.JSESSIONID
+    const urlParams = getUrlParams();
+    jsessionId = urlParams.JSESSIONID;
   }
-  
+
   if (!jsessionId) {
     // 尝试从document.cookie中获取
-    const cookies = document.cookie.split(';')
+    const cookies = document.cookie.split(";");
     for (let cookie of cookies) {
-      const [name, value] = cookie.trim().split('=')
-      if (name === 'JSESSIONID') {
-        jsessionId = value
-        break
+      const [name, value] = cookie.trim().split("=");
+      if (name === "JSESSIONID") {
+        jsessionId = value;
+        break;
       }
     }
   }
-  
-  console.log('🔍 获取JSESSIONID:', {
-    fromLocalStorage: localStorage.getItem('JSESSIONID'),
+
+  console.log("🔍 获取JSESSIONID:", {
+    fromLocalStorage: localStorage.getItem("JSESSIONID"),
     fromURL: getUrlParams().JSESSIONID,
-    final: jsessionId
-  })
-  
-  return jsessionId || ''
+    final: jsessionId,
+  });
+
+  return jsessionId || "";
 }
 
 // 保存JSESSIONID到localStorage
 function saveJSessionId(jsessionId) {
   if (jsessionId) {
-    localStorage.setItem('JSESSIONID', jsessionId)
-    console.log('✅ H5 JSESSIONID已保存:', jsessionId)
+    localStorage.setItem("JSESSIONID", jsessionId);
+    console.log("✅ H5 JSESSIONID已保存:", jsessionId);
   }
 }
 
@@ -91,154 +163,173 @@ function saveJSessionId(jsessionId) {
 function getMockWechatCode() {
   // 在H5环境下,我们可以生成一个模拟的code
   // 实际项目中可能需要接入微信H5授权
-  return 'h5_mock_code_' + Date.now()
+  return "h5_mock_code_" + Date.now();
 }
 
 // 用户信息管理
 const userManager = {
   // 保存用户信息
   saveUserInfo(userInfo) {
-    console.log('💾 H5保存用户信息:', userInfo)
-    localStorage.setItem('userInfo', JSON.stringify(userInfo))
+    console.log("💾 H5保存用户信息:", userInfo);
+    localStorage.setItem("userInfo", JSON.stringify(userInfo));
   },
 
   // 获取保存的用户信息
   getSavedUserInfo() {
-    const userInfo = localStorage.getItem('userInfo')
-    return userInfo ? JSON.parse(userInfo) : null
+    const userInfo = localStorage.getItem("userInfo");
+    return userInfo ? JSON.parse(userInfo) : null;
   },
 
   // 检查登录状态
   checkLoginStatus() {
-    const userInfo = localStorage.getItem('userInfo')
-    const jsessionId = localStorage.getItem('JSESSIONID')
-    
-    console.log('🔍 H5检查登录状态:', { 
-      hasUserInfo: !!userInfo, 
-      hasJSessionId: !!jsessionId 
-    })
+    const userInfo = localStorage.getItem("userInfo");
+    const jsessionId = localStorage.getItem("JSESSIONID");
+
+    console.log("🔍 H5检查登录状态:", {
+      hasUserInfo: !!userInfo,
+      hasJSessionId: !!jsessionId,
+    });
 
     return {
       isLoggedIn: !!(userInfo && jsessionId),
       userInfo: userInfo ? JSON.parse(userInfo) : null,
-      jsessionId: jsessionId
-    }
+      jsessionId: jsessionId,
+    };
   },
 
   // 清除登录信息
   clearLoginInfo() {
-    localStorage.removeItem('userInfo')
-    localStorage.removeItem('JSESSIONID')
-    localStorage.removeItem('deviceInfo')
-    console.log('🧹 已清除登录信息')
-  }
-}
+    localStorage.removeItem("userInfo");
+    localStorage.removeItem("JSESSIONID");
+    localStorage.removeItem("deviceInfo");
+    console.log("🧹 已清除登录信息");
+  },
+};
 
 // 获取图片URL (用于图片回显)
 function getImageUrl(path) {
-  if (!path) return ''
+  if (!path) return "";
 
   // 如果已经是完整URL,直接返回
-  if (path.startsWith('http://') || path.startsWith('https://') || path.startsWith('blob:')) {
-    return path
+  if (
+    path.startsWith("http://") ||
+    path.startsWith("https://") ||
+    path.startsWith("blob:")
+  ) {
+    return path;
   }
 
   // 获取baseUrl (从request中获取或使用默认值)
-  const baseUrl = window.request?.defaults?.baseURL || window.location.origin
+  const baseUrl = window.request?.defaults?.baseURL || window.location.origin;
 
   // 构造图片下载URL
-  return baseUrl + '/service?ssServ=dlByHttp&wdConfirmationCaptchaService=0&type=img&path=' + path
+  return (
+    baseUrl +
+    "/service?ssServ=dlByHttp&wdConfirmationCaptchaService=0&type=img&path=" +
+    path
+  );
 }
 
 // 获取文件URL (用于文件下载)
 function getFileUrl(path) {
-  if (!path) return ''
+  if (!path) return "";
 
   // 如果已经是完整URL,直接返回
-  if (path.startsWith('http://') || path.startsWith('https://')) {
-    return path
+  if (path.startsWith("http://") || path.startsWith("https://")) {
+    return path;
   }
 
   // 获取baseUrl
-  const baseUrl = window.request?.defaults?.baseURL || window.location.origin
+  const baseUrl = window.request?.defaults?.baseURL || window.location.origin;
 
   // 构造文件下载URL
-  return baseUrl + '/service?ssServ=dlByHttp&wdConfirmationCaptchaService=0&type=file&path=' + path
+  return (
+    baseUrl +
+    "/service?ssServ=dlByHttp&wdConfirmationCaptchaService=0&type=file&path=" +
+    path
+  );
 }
 
 // 格式化日期时间 (使用 dayjs)
-function formatDate(dateStr, format = 'YYYY-MM-DD HH:mm:ss') {
+function formatDate(dateStr, format = "YYYY-MM-DD HH:mm:ss") {
   if (!dateStr) {
-    console.log('formatDate: 时间字符串为空')
-    return ''
+    console.log("formatDate: 时间字符串为空");
+    return "";
   }
 
-  console.log('formatDate 输入:', dateStr, '格式要求:', format)
+  console.log("formatDate 输入:", dateStr, "格式要求:", format);
 
   // 检查 dayjs 是否可用
-  if (typeof dayjs === 'undefined') {
-    console.error('❌ dayjs 未加载,无法格式化时间')
-    return dateStr
+  if (typeof dayjs === "undefined") {
+    console.error("❌ dayjs 未加载,无法格式化时间");
+    return dateStr;
   }
 
   // 清理字符串:移除特殊空格字符(如 \u202F),替换为普通空格
   const cleanedDateStr = String(dateStr)
-    .replace(/[\u202F\u00A0]/g, ' ')  // 替换不间断空格
-    .replace(/\s+/g, ' ')              // 多个空格合并为一个
-    .trim()
+    .replace(/[\u202F\u00A0]/g, " ") // 替换不间断空格
+    .replace(/\s+/g, " ") // 多个空格合并为一个
+    .trim();
 
-  console.log('清理后的字符串:', cleanedDateStr)
+  console.log("清理后的字符串:", cleanedDateStr);
 
   // 尝试使用原生 Date 解析(兼容性最好)
-  let date = null
+  let date = null;
   try {
-    const jsDate = new Date(cleanedDateStr)
+    const jsDate = new Date(cleanedDateStr);
     if (!isNaN(jsDate.getTime())) {
-      date = dayjs(jsDate)
-      console.log('✅ 使用 Date 解析成功')
+      date = dayjs(jsDate);
+      console.log("✅ 使用 Date 解析成功");
     }
   } catch (e) {
-    console.warn('Date 解析失败:', e)
+    console.warn("Date 解析失败:", e);
   }
 
   // 如果 Date 解析失败,尝试直接用 dayjs
   if (!date || !date.isValid()) {
-    date = dayjs(cleanedDateStr)
-    console.log('尝试使用 dayjs 直接解析')
+    date = dayjs(cleanedDateStr);
+    console.log("尝试使用 dayjs 直接解析");
   }
 
-  console.log('dayjs 解析结果 isValid:', date ? date.isValid() : 'null')
+  console.log("dayjs 解析结果 isValid:", date ? date.isValid() : "null");
 
   if (!date || !date.isValid()) {
-    console.warn('⚠️ 无效的日期格式:', dateStr)
-    return '' // 解析失败返回空字符串
+    console.warn("⚠️ 无效的日期格式:", dateStr);
+    return ""; // 解析失败返回空字符串
   }
 
   // dayjs 的格式化 (支持常见格式)
   // dayjs 使用大写的格式标记: YYYY-MM-DD HH:mm:ss
-  const result = date.format(format)
-  console.log('格式化结果:', result)
+  const result = date.format(format);
+  console.log("格式化结果:", result);
 
-  return result
+  return result;
 }
 
-// 导出到全局
-window.getUrlParams = getUrlParams
-window.getDeviceInfo = getDeviceInfo
-window.getJSessionId = getJSessionId
-window.saveJSessionId = saveJSessionId
-window.getMockWechatCode = getMockWechatCode
-window.userManager = userManager
-window.getImageUrl = getImageUrl
-window.getFileUrl = getFileUrl
-window.formatDate = formatDate
+window.ENTRY_SOURCE_KEY = ENTRY_SOURCE_KEY;
+window.ENTRY_SOURCE_MY = ENTRY_SOURCE_MY;
+window.getUrlParams = getUrlParams;
+
+window.isAuthPage = isAuthPage;
+window.getCurrentPageUrl = getCurrentPageUrl;
+window.getStoredAuthToken = getStoredAuthToken;
+window.isMiniProgramMessageEntry = isMiniProgramMessageEntry;
+window.handleMessageEntryAuthRedirect = handleMessageEntryAuthRedirect;
+window.getDeviceInfo = getDeviceInfo;
+window.getJSessionId = getJSessionId;
+window.saveJSessionId = saveJSessionId;
+window.getMockWechatCode = getMockWechatCode;
+window.userManager = userManager;
+window.getImageUrl = getImageUrl;
+window.getFileUrl = getFileUrl;
+window.formatDate = formatDate;
 
 // 同时挂载到SS.utils下(兼容组件调用)
 if (!window.SS.utils) {
-  window.SS.utils = {}
+  window.SS.utils = {};
 }
-window.SS.utils.getImageUrl = getImageUrl
-window.SS.utils.getFileUrl = getFileUrl
-window.SS.utils.formatDate = formatDate
+window.SS.utils.getImageUrl = getImageUrl;
+window.SS.utils.getFileUrl = getFileUrl;
+window.SS.utils.formatDate = formatDate;
 
-console.log('✅ H5公共工具已加载')
+console.log("✅ H5公共工具已加载");

+ 5 - 4
js/mp_request/request.js

@@ -7,6 +7,7 @@
 // 环境配置
 const env = {
   baseUrl: "https://m.hfdcschool.com",
+  // baseUrl: 'http://192.168.3.186:8080'
   // baseUrl: 'https://yx.newfeifan.cn'
 };
 
@@ -157,21 +158,21 @@ const request = {
     if (options.loading === false) {
       loadingConfig = false;
     } else {
-      // 功能说明:H5 请求超时统一调到 1 分钟,避免审核/变动类慢接口过早超时 by xu 2026-03-09
+      // 功能说明:H5 请求超时统一调到 11 分钟,避免审核/变动类慢接口过早超时 by xu 2026-03-09
       loadingConfig = {
         show: true,
         title: "加载中...",
         mask: true,
         delay: 300,
-        timeout: 60000,
+        timeout: 660000,
         ...(typeof options.loading === "object" ? options.loading : {}),
       };
     }
 
     // 解析请求配置
-    // 功能说明:H5 axios 请求超时统一调到 1 分钟,和 loading 超时提示保持一致 by xu 2026-03-09
+    // 功能说明:H5 axios 请求超时统一调到 11 分钟,和 loading 超时提示保持一致 by xu 2026-03-09
     const requestConfig = {
-      timeout: 60000,
+      timeout: 660000,
       ...options.request,
     };
 

+ 295 - 106
js/mp_ss_components/mp-ss-components.js

@@ -353,11 +353,9 @@
       const hasSwipeActions = computed(() => swipeActions.value.length > 0);
 
       const swipeActionPalette = [
-        { backgroundColor: "#1d3388", color: "#fff" },
-        { backgroundColor: "#2b4cd1", color: "#fff" },
-        { backgroundColor: "#3c77ec", color: "#fff" },
-        { backgroundColor: "#96b5fb", color: "#1d3388" },
-        { backgroundColor: "#dfe9fd", color: "#1d3388" },
+        { backgroundColor: "#357cdf", color: "#fff" },
+        { backgroundColor: "#94bfff", color: "#000000" },
+        { backgroundColor: "#e0edff", color: "#000000" },
       ];
 
       const actionPaneWidth = computed(() => {
@@ -393,7 +391,7 @@
       const swipeIndicatorStyle = computed(() => {
         const palette = getSwipeActionStyle(0) || {};
         return {
-          backgroundColor: palette.backgroundColor || "#1d3388",
+          backgroundColor: palette.backgroundColor || "#357cdf",
           opacity: swipeOffset.value > 0 ? 0 : 1,
         };
       });
@@ -486,7 +484,10 @@
         const nextOffset = swipeOpening.value
           ? actionPaneWidth.value - deltaX
           : -deltaX;
-        swipeOffset.value = Math.max(0, Math.min(actionPaneWidth.value, nextOffset));
+        swipeOffset.value = Math.max(
+          0,
+          Math.min(actionPaneWidth.value, nextOffset)
+        );
       };
 
       const handleTouchEnd = () => {
@@ -528,7 +529,10 @@
 
       onBeforeUnmount(() => {
         document.removeEventListener("closeAllCardMenus", closeMenu);
-        document.removeEventListener("closeAllCardSwipes", handleCloseAllSwipes);
+        document.removeEventListener(
+          "closeAllCardSwipes",
+          handleCloseAllSwipes
+        );
       });
 
       // H5环境下的清理逻辑
@@ -797,6 +801,8 @@
 		`,
   };
 
+  let activeSsSelectCloser = null;
+
   // ss-select 下拉选择组件 - 从小程序转换
   const SsSelect = {
     name: "SsSelect",
@@ -879,14 +885,20 @@
     },
     emits: ["update:modelValue", "change", "search", "clear", "loaded"],
     setup(props, { emit }) {
-      // 响应式数据
       const isOpen = ref(false);
       const containerRef = ref(null);
+      const inputRef = ref(null);
       const selectedValue = ref(props.modelValue);
       const searchKeyword = ref("");
+      const filterKeyword = ref("");
       const remoteOptions = ref([]);
       const remoteLoading = ref(false);
       const autoMinWidth = ref("");
+      const forceShowAll = ref(false);
+
+      const canInput = computed(() => {
+        return props.inp === true || props.inp === "true";
+      });
 
       // 功能说明:ss-select 未显式传宽度时,按 placeholder/选项文本计算稳定最小宽度,避免回显和下拉宽度随选中项抖动 by xu 2026-03-06
       const normalizeCssSize = (value) => {
@@ -902,6 +914,93 @@
         return widthText !== "100%" && widthText !== "auto";
       });
 
+      const parseFilterObj = () => {
+        if (!props.filter) return {};
+        if (typeof props.filter === "object") return props.filter;
+        if (typeof props.filter === "string") {
+          try {
+            const obj = JSON.parse(props.filter);
+            return obj && typeof obj === "object" ? obj : {};
+          } catch (_) {
+            return {};
+          }
+        }
+        return {};
+      };
+
+      const normalizeResultToOptions = (respData) => {
+        const raw = respData || {};
+        if (Array.isArray(raw.resultList)) {
+          return raw.resultList.map((it) => {
+            if (it && typeof it === "object") return it;
+            return { n: String(it || ""), v: String(it || "") };
+          });
+        }
+        if (raw.result && typeof raw.result === "object") {
+          return Object.keys(raw.result).map((k) => ({
+            n: raw.result[k],
+            v: k,
+          }));
+        }
+        if (Array.isArray(raw.objectList)) {
+          return raw.objectList.map((it) => {
+            if (it && typeof it === "object") return it;
+            return { n: String(it || ""), v: String(it || "") };
+          });
+        }
+        return [];
+      };
+
+      const needRemoteData = computed(() => !!String(props.cb || "").trim());
+
+      const optionsList = computed(() => {
+        if (Array.isArray(props.options) && props.options.length > 0) {
+          return props.options;
+        }
+        if (needRemoteData.value) {
+          return remoteOptions.value;
+        }
+        return [];
+      });
+
+      const getOptionText = (option) => {
+        if (!option || typeof option !== "object") return "";
+        const text = option[props.mapping.text];
+        return text === undefined || text === null ? "" : String(text);
+      };
+
+      const findSelectedOption = () => {
+        return optionsList.value.find(
+          (option) => option?.[props.mapping.value] === selectedValue.value
+        );
+      };
+
+      const syncInputFromSelection = () => {
+        if (!canInput.value) return;
+        const selectedOption = findSelectedOption();
+        searchKeyword.value = selectedOption
+          ? getOptionText(selectedOption)
+          : "";
+        filterKeyword.value = "";
+        forceShowAll.value = false;
+      };
+
+      const filteredOptions = computed(() => {
+        const list = Array.isArray(optionsList.value) ? optionsList.value : [];
+        if (!canInput.value || forceShowAll.value) {
+          return list;
+        }
+        const keyword = String(filterKeyword.value || "")
+          .trim()
+          .toLowerCase();
+        if (!keyword) {
+          return list;
+        }
+        return list.filter((option) =>
+          getOptionText(option).toLowerCase().includes(keyword)
+        );
+      });
+
       const measureStableMinWidth = () => {
         if (hasExplicitWidth.value) {
           autoMinWidth.value = "";
@@ -914,7 +1013,9 @@
               (option) => option?.[props.mapping.text]
             )
           )
-          .map((item) => (item === undefined || item === null ? "" : String(item).trim()))
+          .map((item) =>
+            item === undefined || item === null ? "" : String(item).trim()
+          )
           .filter(Boolean);
 
         if (!texts.length || typeof document === "undefined") {
@@ -923,7 +1024,8 @@
         }
 
         const measureNode =
-          containerRef.value?.querySelector?.(".select-text") || containerRef.value;
+          containerRef.value?.querySelector?.(".select-text") ||
+          containerRef.value;
         const computedStyle = measureNode
           ? window.getComputedStyle(measureNode)
           : null;
@@ -940,44 +1042,10 @@
         }, 0);
 
         const reservedWidth = 52;
-        autoMinWidth.value = `${Math.max(88, Math.ceil(widestText + reservedWidth))}px`;
-      };
-
-      const parseFilterObj = () => {
-        if (!props.filter) return {};
-        if (typeof props.filter === "object") return props.filter;
-        if (typeof props.filter === "string") {
-          try {
-            const obj = JSON.parse(props.filter);
-            return obj && typeof obj === "object" ? obj : {};
-          } catch (_) {
-            return {};
-          }
-        }
-        return {};
-      };
-
-      const normalizeResultToOptions = (respData) => {
-        const raw = respData || {};
-        if (Array.isArray(raw.resultList)) {
-          return raw.resultList.map((it) => {
-            if (it && typeof it === "object") return it;
-            return { n: String(it || ""), v: String(it || "") };
-          });
-        }
-        if (raw.result && typeof raw.result === "object") {
-          return Object.keys(raw.result).map((k) => ({
-            n: raw.result[k],
-            v: k,
-          }));
-        }
-        if (Array.isArray(raw.objectList)) {
-          return raw.objectList.map((it) => {
-            if (it && typeof it === "object") return it;
-            return { n: String(it || ""), v: String(it || "") };
-          });
-        }
-        return [];
+        autoMinWidth.value = `${Math.max(
+          88,
+          Math.ceil(widestText + reservedWidth)
+        )}px`;
       };
 
       const maybeAutoSelectFirst = (opts) => {
@@ -987,8 +1055,9 @@
           selectedValue.value !== undefined &&
           selectedValue.value !== null &&
           selectedValue.value !== ""
-        )
+        ) {
           return;
+        }
         const first = opts[0];
         if (!first || typeof first !== "object") return;
         const value = first[props.mapping.value];
@@ -996,10 +1065,9 @@
         selectedValue.value = value;
         emit("update:modelValue", value);
         emit("change", value);
+        syncInputFromSelection();
       };
 
-      const needRemoteData = computed(() => !!String(props.cb || "").trim());
-
       const loadRemoteOptions = async () => {
         if (!needRemoteData.value) return;
         if (!window.request || typeof window.request.post !== "function")
@@ -1008,7 +1076,7 @@
         remoteLoading.value = true;
         try {
           const objpParam = {
-            input: String(props.inp === true || props.inp === "true"),
+            input: String(canInput.value),
             codebook: String(props.cb || ""),
             ...parseFilterObj(),
           };
@@ -1028,6 +1096,9 @@
           remoteOptions.value = opts;
           emit("loaded", opts);
           maybeAutoSelectFirst(opts);
+          if (!isOpen.value) {
+            syncInputFromSelection();
+          }
         } catch (e) {
           remoteOptions.value = [];
           emit("loaded", []);
@@ -1037,16 +1108,9 @@
         }
       };
 
-      // 计算属性
-      const optionsList = computed(() => {
-        if (Array.isArray(props.options) && props.options.length > 0)
-          return props.options;
-        if (needRemoteData.value) return remoteOptions.value;
-        return [];
+      const finalLoading = computed(() => {
+        return !!props.loading || remoteLoading.value;
       });
-      const finalLoading = computed(
-        () => !!props.loading || remoteLoading.value
-      );
 
       const selectContainerStyle = computed(() => {
         const style = {};
@@ -1071,62 +1135,128 @@
 
       const displayText = computed(() => {
         if (!selectedValue.value) return props.placeholder;
-
-        const selectedOption = optionsList.value.find(
-          (option) => option[props.mapping.value] === selectedValue.value
-        );
-
+        const selectedOption = findSelectedOption();
         return selectedOption
-          ? selectedOption[props.mapping.text]
+          ? getOptionText(selectedOption)
           : props.placeholder;
       });
 
-      // 监听 modelValue 变化
-      watch(
-        () => props.modelValue,
-        (newValue) => {
-          selectedValue.value = newValue;
+      const enterInputFilterMode = () => {
+        if (!canInput.value) return;
+        const selectedOption = findSelectedOption();
+        const selectedText = selectedOption
+          ? getOptionText(selectedOption)
+          : "";
+
+        if (!filterKeyword.value && searchKeyword.value === selectedText) {
+          searchKeyword.value = "";
         }
-      );
 
-      // 切换下拉框
+        filterKeyword.value = searchKeyword.value;
+        forceShowAll.value = false;
+      };
+
+      const openDropdown = (showAll = false) => {
+        if (props.disabled) return;
+        if (activeSsSelectCloser && activeSsSelectCloser !== closeDropdown) {
+          activeSsSelectCloser({ restoreInput: true });
+        }
+        forceShowAll.value = showAll;
+        isOpen.value = true;
+        activeSsSelectCloser = closeDropdown;
+      };
+
+      const closeDropdown = ({ restoreInput = true } = {}) => {
+        isOpen.value = false;
+        forceShowAll.value = false;
+        if (activeSsSelectCloser === closeDropdown) {
+          activeSsSelectCloser = null;
+        }
+        if (restoreInput) {
+          syncInputFromSelection();
+        }
+      };
+
       const toggleDropdown = () => {
         if (props.disabled) return;
-        isOpen.value = !isOpen.value;
+        if (isOpen.value) {
+          closeDropdown();
+        } else {
+          openDropdown(false);
+        }
+      };
+
+      const handleContainerClick = () => {
+        if (props.disabled || canInput.value) return;
+        toggleDropdown();
+      };
+
+      const handleTextClick = () => {
+        if (props.disabled) return;
+        if (!canInput.value) {
+          toggleDropdown();
+          return;
+        }
+        enterInputFilterMode();
+        openDropdown(false);
+        if (inputRef.value) {
+          inputRef.value.focus();
+        }
+      };
+
+      const handleInputFocus = () => {
+        if (props.disabled || !canInput.value) return;
+        enterInputFilterMode();
+        openDropdown(false);
+      };
+
+      const handleInput = (event) => {
+        const value = event?.target?.value || "";
+        searchKeyword.value = value;
+        filterKeyword.value = value;
+        forceShowAll.value = false;
+        isOpen.value = true;
+        emit("search", value);
+      };
+
+      const handleArrowClick = () => {
+        if (props.disabled) return;
+        if (!canInput.value) {
+          toggleDropdown();
+          return;
+        }
+        filterKeyword.value = "";
+        forceShowAll.value = true;
+        isOpen.value = true;
       };
 
-      // 选择选项
       const selectOption = (option) => {
         const value = option[props.mapping.value];
         selectedValue.value = value;
+        searchKeyword.value = getOptionText(option);
+        filterKeyword.value = "";
+        forceShowAll.value = false;
         isOpen.value = false;
 
         emit("update:modelValue", value);
         emit("change", value);
       };
 
-      // 点击外部关闭
       const handleClickOutside = (event) => {
-        if (!event.target.closest(".ss-select-container")) {
-          isOpen.value = false;
+        if (!containerRef.value?.contains(event.target)) {
+          closeDropdown();
         }
       };
 
-      onMounted(() => {
-        document.addEventListener("click", handleClickOutside);
-        loadRemoteOptions();
-        if (!needRemoteData.value && Array.isArray(props.options)) {
-          emit("loaded", props.options);
-        }
-        measureStableMinWidth();
-      });
-
       watch(
-        () => [props.cb, props.url, props.filter],
-        () => {
-          if (!needRemoteData.value) return;
-          loadRemoteOptions();
-        }
+        () => props.modelValue,
+        (newValue) => {
+          selectedValue.value = newValue;
+          if (!isOpen.value) {
+            syncInputFromSelection();
+          }
+        },
+        { immediate: true }
       );
 
       watch(
@@ -1134,6 +1264,17 @@
         (newVal) => {
           if (needRemoteData.value) return;
           emit("loaded", Array.isArray(newVal) ? newVal : []);
+          if (!isOpen.value) {
+            syncInputFromSelection();
+          }
+        }
+      );
+
+      watch(
+        () => [props.cb, props.url, props.filter],
+        () => {
+          if (!needRemoteData.value) return;
+          loadRemoteOptions();
         }
       );
 
@@ -1152,49 +1293,97 @@
         { immediate: true }
       );
 
+      watch(
+        () => optionsList.value,
+        () => {
+          if (!isOpen.value) {
+            syncInputFromSelection();
+          }
+        },
+        { deep: true }
+      );
+
+      onMounted(() => {
+        document.addEventListener("click", handleClickOutside);
+        loadRemoteOptions();
+        if (!needRemoteData.value && Array.isArray(props.options)) {
+          emit("loaded", props.options);
+        }
+        syncInputFromSelection();
+        measureStableMinWidth();
+      });
+
+      onBeforeUnmount(() => {
+        if (activeSsSelectCloser === closeDropdown) {
+          activeSsSelectCloser = null;
+        }
+        document.removeEventListener("click", handleClickOutside);
+      });
+
       return {
         containerRef,
+        inputRef,
         isOpen,
         selectedValue,
         searchKeyword,
-        optionsList,
+        canInput,
+        filteredOptions,
         finalLoading,
         displayText,
         selectContainerStyle,
-        toggleDropdown,
+        handleContainerClick,
+        handleTextClick,
+        handleInputFocus,
+        handleInput,
+        handleArrowClick,
         selectOption,
       };
     },
     template: `
-			<div ref="containerRef" class="ss-select-container" :class="{ open: isOpen }" :style="selectContainerStyle" @click.stop="toggleDropdown">
-				<!-- 显示区域 -->
+			<div
+				ref="containerRef"
+				class="ss-select-container"
+				:class="{ open: isOpen }"
+				:style="selectContainerStyle"
+				@click.stop="handleContainerClick"
+			>
 				<div class="ss-select" :class="{ disabled: disabled }">
-					<span class="select-text" :class="{ placeholder: !selectedValue }">{{ displayText }}</span>
-					<div class="select-arrow" :class="{ rotate: isOpen }">
+					<div class="select-text" @click.stop="handleTextClick">
+						<input
+							v-if="canInput"
+							ref="inputRef"
+							:type="'text'"
+							:value="searchKeyword"
+							:placeholder="placeholder"
+							:disabled="disabled"
+							@focus.stop="handleInputFocus"
+							@click.stop="handleTextClick"
+							@input="handleInput"
+							style="width: 100%; border: none; outline: none; background: transparent; color: inherit; font-size: inherit; padding: 0; margin: 0;"
+						/>
+						<span v-else :class="{ placeholder: !selectedValue }">{{ displayText }}</span>
+					</div>
+					<div class="select-arrow" :class="{ rotate: isOpen }" @click.stop="handleArrowClick">
 						<Icon name="icon-xiangxiajiantou" size="32" :color="disabled ? '#ccc' : '#999'"/>
 					</div>
 				</div>
 
-				<!-- 选项列表 -->
 				<div class="ss-options" v-show="isOpen">
-					<!-- 加载状态 -->
 					<div
 						v-if="finalLoading"
 						class="option-item loading-item"
 					>
 						<span class="loading-text">加载中...</span>
 					</div>
-					<!-- 无选项 -->
 					<div
-						v-else-if="optionsList.length === 0"
+						v-else-if="filteredOptions.length === 0"
 						class="option-item no-options"
 					>
 						无选项
 					</div>
-					<!-- 选项列表 -->
 					<div
 						v-else
-						v-for="(option, index) in optionsList"
+						v-for="(option, index) in filteredOptions"
 						:key="index"
 						class="option-item"
 						:class="{ selected: option[mapping.value] === selectedValue }"

+ 426 - 409
page/autoLogin.html

@@ -1,441 +1,458 @@
 <!DOCTYPE html>
 <html lang="zh-CN">
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <title>自动登录</title>
     <script src="/js/mp_base/base.js"></script>
-</head>
-<body>
+  </head>
+  <body>
     <!-- 页面加载状态 -->
     <div id="page-loading" class="page-loading">
-        <div class="loading-content">
-            <div class="loading-spinner"></div>
-            <div class="loading-text">正在自动登录...</div>
-        </div>
+      <div class="loading-content">
+        <div class="loading-spinner"></div>
+        <div class="loading-text">正在自动登录...</div>
+      </div>
     </div>
 
     <!-- 主要内容 -->
     <div id="app" v-cloak>
-        <!-- 自动登录状态 -->
-        <div class="auto-login-container">
-            <div class="status-icon">
-                <div v-if="status === 'loading'" class="loading-spinner"></div>
-                <div v-else-if="status === 'success'" class="success-icon">✓</div>
-                <div v-else-if="status === 'error'" class="error-icon">✗</div>
-            </div>
-            
-            <div class="status-text">{{ statusText }}</div>
-            
-            <div v-if="status === 'error'" class="retry-section">
-                <button @click="handleRetry" class="retry-btn">重试</button>
-                <button @click="handleManualLogin" class="manual-btn">手动登录</button>
-            </div>
+      <!-- 自动登录状态 -->
+      <div class="auto-login-container">
+        <div class="status-icon">
+          <div v-if="status === 'loading'" class="loading-spinner"></div>
+          <div v-else-if="status === 'success'" class="success-icon">✓</div>
+          <div v-else-if="status === 'error'" class="error-icon">✗</div>
         </div>
 
-        <!-- Loading 遮罩 -->
-        <div v-if="showLoading" class="loading-mask">
-            <div class="loading-content">
-                <div class="loading-spinner"></div>
-                <div class="loading-text">{{ loadingText }}</div>
-            </div>
+        <div class="status-text">{{ statusText }}</div>
+
+        <div v-if="status === 'error'" class="retry-section">
+          <button @click="handleRetry" class="retry-btn">重试</button>
+          <button @click="handleManualLogin" class="manual-btn">
+            手动登录
+          </button>
         </div>
+      </div>
 
-        <!-- Toast 提示 -->
-        <div v-if="toast.show" class="toast" :class="toast.type">
-            {{ toast.message }}
+      <!-- Loading 遮罩 -->
+      <div v-if="showLoading" class="loading-mask">
+        <div class="loading-content">
+          <div class="loading-spinner"></div>
+          <div class="loading-text">{{ loadingText }}</div>
         </div>
+      </div>
+
+      <!-- Toast 提示 -->
+      <div v-if="toast.show" class="toast" :class="toast.type">
+        {{ toast.message }}
+      </div>
     </div>
 
     <script>
-        // 获取yhsbToken的函数
-        function getYhsbToken() {
-            // 从localStorage获取用户信息
-            const userInfo = localStorage.getItem('userInfo');
-            if (userInfo) {
-                try {
-                    const userData = JSON.parse(userInfo);
-                    return userData.yhsbToken || userData.onlineToken || '';
-                } catch (e) {
-                    console.error('解析用户信息失败:', e);
-                }
+      // 获取yhsbToken的函数
+      function getYhsbToken() {
+        // 从localStorage获取用户信息
+        const userInfo = localStorage.getItem("userInfo");
+        if (userInfo) {
+          try {
+            const userData = JSON.parse(userInfo);
+            return userData.yhsbToken || userData.onlineToken || "";
+          } catch (e) {
+            console.error("解析用户信息失败:", e);
+          }
+        }
+
+        // 从其他可能的存储位置获取
+        return localStorage.getItem("yhsbToken") || "";
+      }
+
+      // 等待SS框架加载完成
+      window.SS.ready(function () {
+        // 使用SS框架的方式创建Vue实例
+        window.SS.dom.initializeFormApp({
+          el: "#app",
+          data() {
+            return {
+              status: "loading", // loading, success, error
+              statusText: "正在自动登录...",
+              showLoading: false,
+              loadingText: "",
+              toast: {
+                show: false,
+                message: "",
+                type: "info",
+              },
+            };
+          },
+
+          mounted() {
+            // 隐藏页面加载状态
+            const pageLoading = document.getElementById("page-loading");
+            if (pageLoading) {
+              pageLoading.style.display = "none";
             }
 
-            // 从其他可能的存储位置获取
-            return localStorage.getItem('yhsbToken') || '';
-        }
+            // 初始化页面,使用自定义结果处理
+            if (typeof initPage === "function") {
+              initPage("autoLogin", this.handleResult);
+            }
 
-        // 等待SS框架加载完成
-        window.SS.ready(function () {
-            // 使用SS框架的方式创建Vue实例
-            window.SS.dom.initializeFormApp({
-                el: '#app',
-                data() {
-                    return {
-                        status: 'loading', // loading, success, error
-                        statusText: '正在自动登录...',
-                        showLoading: false,
-                        loadingText: '',
-                        toast: {
-                            show: false,
-                            message: '',
-                            type: 'info'
-                        }
-                    }
-                },
-
-                mounted() {
-                    // 隐藏页面加载状态
-                    const pageLoading = document.getElementById('page-loading')
-                    if (pageLoading) {
-                        pageLoading.style.display = 'none'
-                    }
-
-                    // 初始化页面,使用自定义结果处理
-                    if (typeof initPage === 'function') {
-                        initPage('autoLogin', this.handleResult)
-                    }
-
-                    // 获取URL参数
-                    const urlParams = this.getUrlParams()
-                    console.log('🔗 H5自动登录页面参数:', urlParams)
-
-                    // 开始自动登录
-                    this.startAutoLogin()
-                },
-
-                methods: {
-                    // 获取URL参数
-                    getUrlParams() {
-                        const params = {}
-                        const urlSearchParams = new URLSearchParams(window.location.search)
-                        for (const [key, value] of urlSearchParams) {
-                            params[key] = decodeURIComponent(value)
-                        }
-                        return params
-                    },
-
-                    // 处理操作结果(来自小程序的回调)
-                    handleResult(data) {
-                        console.log('🎯 收到小程序回调:', data)
-                    },
-
-                    // 显示Loading
-                    showLoadingMask(text = '加载中...') {
-                        this.showLoading = true
-                        this.loadingText = text
-                    },
-
-                    // 隐藏Loading
-                    hideLoadingMask() {
-                        this.showLoading = false
-                        this.loadingText = ''
-                    },
-
-                    
-
-                    // 开始自动登录
-                    async startAutoLogin() {
-                        try {
-                            this.status = 'loading'
-                            this.statusText = '正在自动登录...'
-
-                            // 从URL参数或localStorage获取yhsbToken进行自动登录
-                            const urlParams = this.getUrlParams()
-                            const yhsbToken = urlParams.yhsbToken || getYhsbToken()
-
-                            console.log('🔄 开始自动登录,yhsbToken:', yhsbToken)
-
-                            if (!yhsbToken) {
-                                throw new Error('未找到yhsbToken,无法自动登录')
-                            }
-
-                            // 使用自动登录接口(参考小程序的实现)
-                            const response = await request.post(
-                                `/service?ssServ=ssLogin&wdConfirmationCaptchaService=0&mdToken=${yhsbToken}`,
-                                { mdToken: yhsbToken },
-                                { loading: false }
-                            )
-                            if (response && response.data) {
-                                console.log('✅ 自动登录成功:', JSON.stringify( response.data))
-
-                                // 构建用户数据(参考登录页面的格式)
-                                const userData = {
-                                    devId: response.data.devId,
-                                    sbmc: response.data.sbmc,
-                                    sessId: response.data.sessId ,
-                                    xm: response.data.xm,
-                                    yhsbToken: response.data.yhsbToken,
-                                    onlineToken: response.data.onlineToken,
-                                    syList:response.data.sylist,
-                                    yhid:response.data.yhid,
-                                    yhm:response.data.yhm
-                                }
-
-                                // 保存用户信息到H5本地存储(和登录页面一样)
-                                h5UserApi.saveUserInfo(userData)
-
-                                // 准备返回数据给小程序(和登录页面一样,只是多了isAutoLogin标记)
-                                const loginResult = {
-                                    success: true,
-                                    userInfo: userData,
-                                    isAutoLogin: true // 标记为自动登录,用于区分返回逻辑
-                                }
-
-                                this.status = 'success'
-                                this.statusText = '自动登录成功!'
-
-                                console.log('✅ 自动登录成功,准备返回数据:', loginResult)
-
-                                // 延迟返回结果
-                                setTimeout(() => {
-                                    this.returnLoginResult(loginResult)
-                                }, 1500)
-
-                            } else {
-                                throw new Error('自动登录响应数据无效')
-                            }
-
-                        } catch (error) {
-                            console.error('❌ 自动登录失败:', error)
-                            this.status = 'error'
-                            this.statusText = '自动登录失败: ' + error.message
-                        }
-                    },
-
-                    // 返回登录结果给小程序
-                    returnLoginResult(result) {
-                        console.log('🔄 返回自动登录结果给小程序:', result)
-                        callNative('loginSuccess', '自动登录成功', result)
-                    },
-
-                    // 重试自动登录
-                    handleRetry() {
-                        console.log('🔄 重试自动登录')
-                        this.startAutoLogin()
-                    },
-
-                    // 跳转手动登录
-                    handleManualLogin() {
-                        console.log('👤 跳转手动登录')
-                        // 跳转到登录页面
-                        window.location.href = '/pages/login.html' + window.location.search
-                    }
+            // 获取URL参数
+            const urlParams = this.getUrlParams();
+            console.log("🔗 H5自动登录页面参数:", urlParams);
+
+            // 开始自动登录
+            this.startAutoLogin();
+          },
+
+          methods: {
+            // 获取URL参数
+            getUrlParams() {
+              const params = {};
+              const urlSearchParams = new URLSearchParams(
+                window.location.search
+              );
+              for (const [key, value] of urlSearchParams) {
+                params[key] = decodeURIComponent(value);
+              }
+              return params;
+            },
+
+            // 处理操作结果(来自小程序的回调)
+            handleResult(data) {
+              console.log("🎯 收到小程序回调:", data);
+            },
+
+            // 显示Loading
+            showLoadingMask(text = "加载中...") {
+              this.showLoading = true;
+              this.loadingText = text;
+            },
+
+            // 隐藏Loading
+            hideLoadingMask() {
+              this.showLoading = false;
+              this.loadingText = "";
+            },
+
+            // 开始自动登录
+            async startAutoLogin() {
+              try {
+                this.status = "loading";
+                this.statusText = "正在自动登录...";
+
+                // 从URL参数或localStorage获取yhsbToken进行自动登录
+                const urlParams = this.getUrlParams();
+                const yhsbToken = urlParams.yhsbToken || getYhsbToken();
+                const redirectUrl = urlParams.redirect || "";
+                const entryScene = urlParams.entryScene || "";
+
+                console.log("🔄 开始自动登录,yhsbToken:", yhsbToken);
+
+                if (!yhsbToken) {
+                  throw new Error("未找到yhsbToken,无法自动登录");
                 }
-            })
 
-            console.log('✅ 自动登录页面初始化完成')
-        })
+                // 使用自动登录接口(参考小程序的实现)
+                const response = await request.post(
+                  `/service?ssServ=ssLogin&wdConfirmationCaptchaService=0&mdToken=${yhsbToken}`,
+                  { mdToken: yhsbToken },
+                  { loading: false }
+                );
+                if (response && response.data) {
+                  console.log(
+                    "✅ 自动登录成功:",
+                    JSON.stringify(response.data)
+                  );
+
+                  // 构建用户数据(参考登录页面的格式)
+                  const userData = {
+                    devId: response.data.devId,
+                    sbmc: response.data.sbmc,
+                    sessId: response.data.sessId,
+                    xm: response.data.xm,
+                    yhsbToken: response.data.yhsbToken,
+                    onlineToken: response.data.onlineToken,
+                    syList: response.data.sylist,
+                    yhid: response.data.yhid,
+                    yhm: response.data.yhm,
+                  };
+
+                  // 保存用户信息到H5本地存储(和登录页面一样)
+                  h5UserApi.saveUserInfo(userData);
+
+                  // 准备返回数据给小程序(和登录页面一样,只是多了isAutoLogin标记)
+                  const loginResult = {
+                    success: true,
+                    userInfo: userData,
+                    isAutoLogin: true, // 标记为自动登录,用于区分返回逻辑
+                    redirectUrl,
+                    entryScene,
+                  };
+
+                  this.status = "success";
+                  this.statusText = "自动登录成功!";
+
+                  console.log("✅ 自动登录成功,准备返回数据:", loginResult);
+
+                  // 延迟返回结果
+                  setTimeout(() => {
+                    this.returnLoginResult(loginResult);
+                  }, 1500);
+                } else {
+                  throw new Error("自动登录响应数据无效");
+                }
+              } catch (error) {
+                console.error("❌ 自动登录失败:", error);
+                this.status = "error";
+                this.statusText = "自动登录失败: " + error.message;
+              }
+            },
+
+            // 返回登录结果给小程序
+            returnLoginResult(result) {
+              console.log("🔄 返回自动登录结果给小程序:", result);
+              callNative("loginSuccess", "自动登录成功", result);
+            },
+
+            // 重试自动登录
+            handleRetry() {
+              console.log("🔄 重试自动登录");
+              this.startAutoLogin();
+            },
+
+            // 跳转手动登录
+            handleManualLogin() {
+              console.log("👤 跳转手动登录");
+              // 跳转到登录页面
+              window.location.href =
+                "/page/login.html" + window.location.search;
+            },
+          },
+        });
+
+        console.log("✅ 自动登录页面初始化完成");
+      });
     </script>
 
     <style>
-        /* 防止Vue模板闪烁 */
-        [v-cloak] {
-            display: none !important;
-        }
-
-        /* 页面加载状态 */
-        .page-loading {
-            position: fixed;
-            top: 0;
-            left: 0;
-            right: 0;
-            bottom: 0;
-            background: #f5f5f5;
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            z-index: 9999;
-        }
-
-        .page-loading .loading-content {
-            text-align: center;
-        }
-
-        .page-loading .loading-spinner {
-            width: 40px;
-            height: 40px;
-            border: 4px solid #f3f3f3;
-            border-top: 4px solid #40ac6d;
-            border-radius: 50%;
-            animation: page-spin 1s linear infinite;
-            margin: 0 auto 15px;
-        }
-
-        @keyframes page-spin {
-            0% { transform: rotate(0deg); }
-            100% { transform: rotate(360deg); }
-        }
-
-        .page-loading .loading-text {
-            color: #666;
-            font-size: 14px;
-        }
-
-        * {
-            margin: 0;
-            padding: 0;
-            box-sizing: border-box;
-        }
-
-        body {
-            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-            background: #f5f5f5;
-            min-height: 100vh;
-            display: flex;
-            align-items: center;
-            justify-content: center;
-        }
-
-        .auto-login-container {
-            background: white;
-            border-radius: 12px;
-            padding: 40px 30px;
-            text-align: center;
-            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
-            max-width: 300px;
-            width: 90%;
-        }
-
-        .status-icon {
-            margin-bottom: 20px;
-        }
-
-        .loading-spinner {
-            width: 50px;
-            height: 50px;
-            border: 4px solid #f3f3f3;
-            border-top: 4px solid #40ac6d;
-            border-radius: 50%;
-            animation: spin 1s linear infinite;
-            margin: 0 auto;
-        }
-
-        .success-icon {
-            width: 50px;
-            height: 50px;
-            background: #40ac6d;
-            border-radius: 50%;
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            color: white;
-            font-size: 24px;
-            font-weight: bold;
-            margin: 0 auto;
-        }
-
-        .error-icon {
-            width: 50px;
-            height: 50px;
-            background: #dc3545;
-            border-radius: 50%;
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            color: white;
-            font-size: 24px;
-            font-weight: bold;
-            margin: 0 auto;
-        }
-
-        @keyframes spin {
-            0% { transform: rotate(0deg); }
-            100% { transform: rotate(360deg); }
-        }
-
-        .status-text {
-            font-size: 16px;
-            color: #333;
-            margin-bottom: 20px;
-            line-height: 1.5;    
-            word-break: keep-all;
-            
-        }
-
-        .retry-section {
-            display: flex;
-            gap: 10px;
-            justify-content: center;
-        }
-
-        .retry-btn, .manual-btn {
-            padding: 10px 20px;
-            border: none;
-            border-radius: 6px;
-            font-size: 14px;
-            cursor: pointer;
-            transition: all 0.3s ease;
-        }
-
-        .retry-btn {
-            background: #40ac6d;
-            color: white;
+      /* 防止Vue模板闪烁 */
+      [v-cloak] {
+        display: none !important;
+      }
+
+      /* 页面加载状态 */
+      .page-loading {
+        position: fixed;
+        top: 0;
+        left: 0;
+        right: 0;
+        bottom: 0;
+        background: #f5f5f5;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        z-index: 9999;
+      }
+
+      .page-loading .loading-content {
+        text-align: center;
+      }
+
+      .page-loading .loading-spinner {
+        width: 40px;
+        height: 40px;
+        border: 4px solid #f3f3f3;
+        border-top: 4px solid #40ac6d;
+        border-radius: 50%;
+        animation: page-spin 1s linear infinite;
+        margin: 0 auto 15px;
+      }
+
+      @keyframes page-spin {
+        0% {
+          transform: rotate(0deg);
         }
-
-        .retry-btn:hover {
-            background: #369a5a;
-        }
-
-        .manual-btn {
-            background: #6c757d;
-            color: white;
-        }
-
-        .manual-btn:hover {
-            background: #5a6268;
-        }
-
-        /* Loading 遮罩 */
-        .loading-mask {
-            position: fixed;
-            top: 0;
-            left: 0;
-            right: 0;
-            bottom: 0;
-            background: rgba(0, 0, 0, 0.5);
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            z-index: 10000;
+        100% {
+          transform: rotate(360deg);
         }
-
-        .loading-mask .loading-content {
-            background: white;
-            padding: 20px;
-            border-radius: 8px;
-            text-align: center;
-            min-width: 120px;
+      }
+
+      .page-loading .loading-text {
+        color: #666;
+        font-size: 14px;
+      }
+
+      * {
+        margin: 0;
+        padding: 0;
+        box-sizing: border-box;
+      }
+
+      body {
+        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
+          sans-serif;
+        background: #f5f5f5;
+        min-height: 100vh;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+      }
+
+      .auto-login-container {
+        background: white;
+        border-radius: 12px;
+        padding: 40px 30px;
+        text-align: center;
+        box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
+        max-width: 300px;
+        width: 90%;
+      }
+
+      .status-icon {
+        margin-bottom: 20px;
+      }
+
+      .loading-spinner {
+        width: 50px;
+        height: 50px;
+        border: 4px solid #f3f3f3;
+        border-top: 4px solid #40ac6d;
+        border-radius: 50%;
+        animation: spin 1s linear infinite;
+        margin: 0 auto;
+      }
+
+      .success-icon {
+        width: 50px;
+        height: 50px;
+        background: #40ac6d;
+        border-radius: 50%;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        color: white;
+        font-size: 24px;
+        font-weight: bold;
+        margin: 0 auto;
+      }
+
+      .error-icon {
+        width: 50px;
+        height: 50px;
+        background: #dc3545;
+        border-radius: 50%;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        color: white;
+        font-size: 24px;
+        font-weight: bold;
+        margin: 0 auto;
+      }
+
+      @keyframes spin {
+        0% {
+          transform: rotate(0deg);
         }
-
-        .loading-mask .loading-text {
-            color: #333;
-            font-size: 14px;
-        }
-
-        /* Toast 提示 */
-        .toast {
-            position: fixed;
-            top: 50%;
-            left: 50%;
-            transform: translate(-50%, -50%);
-            background: rgba(0, 0, 0, 0.8);
-            color: white;
-            padding: 12px 20px;
-            border-radius: 6px;
-            font-size: 14px;
-            z-index: 10001;
-            max-width: 80%;
-            text-align: center;
-        }
-
-        .toast.success {
-            background: rgba(40, 167, 69, 0.9);
-        }
-
-        .toast.error {
-            background: rgba(220, 53, 69, 0.9);
+        100% {
+          transform: rotate(360deg);
         }
+      }
+
+      .status-text {
+        font-size: 16px;
+        color: #333;
+        margin-bottom: 20px;
+        line-height: 1.5;
+        word-break: keep-all;
+      }
+
+      .retry-section {
+        display: flex;
+        gap: 10px;
+        justify-content: center;
+      }
+
+      .retry-btn,
+      .manual-btn {
+        padding: 10px 20px;
+        border: none;
+        border-radius: 6px;
+        font-size: 14px;
+        cursor: pointer;
+        transition: all 0.3s ease;
+      }
+
+      .retry-btn {
+        background: #40ac6d;
+        color: white;
+      }
+
+      .retry-btn:hover {
+        background: #369a5a;
+      }
+
+      .manual-btn {
+        background: #6c757d;
+        color: white;
+      }
+
+      .manual-btn:hover {
+        background: #5a6268;
+      }
+
+      /* Loading 遮罩 */
+      .loading-mask {
+        position: fixed;
+        top: 0;
+        left: 0;
+        right: 0;
+        bottom: 0;
+        background: rgba(0, 0, 0, 0.5);
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        z-index: 10000;
+      }
+
+      .loading-mask .loading-content {
+        background: white;
+        padding: 20px;
+        border-radius: 8px;
+        text-align: center;
+        min-width: 120px;
+      }
+
+      .loading-mask .loading-text {
+        color: #333;
+        font-size: 14px;
+      }
+
+      /* Toast 提示 */
+      .toast {
+        position: fixed;
+        top: 50%;
+        left: 50%;
+        transform: translate(-50%, -50%);
+        background: rgba(0, 0, 0, 0.8);
+        color: white;
+        padding: 12px 20px;
+        border-radius: 6px;
+        font-size: 14px;
+        z-index: 10001;
+        max-width: 80%;
+        text-align: center;
+      }
+
+      .toast.success {
+        background: rgba(40, 167, 69, 0.9);
+      }
+
+      .toast.error {
+        background: rgba(220, 53, 69, 0.9);
+      }
     </style>
-</body>
+  </body>
 </html>

+ 4 - 0
page/login.html

@@ -370,6 +370,8 @@
                 // 获取URL参数中的wechatCode
                 const urlParams = this.getUrlParams();
                 const wechatCode = urlParams.wechatCode || "";
+                const redirectUrl = urlParams.redirect || "";
+                const entryScene = urlParams.entryScene || "";
                 console.log("🔗 使用wechatCode:", wechatCode);
 
                 // 使用真实的API进行登录
@@ -427,6 +429,8 @@
                   const loginResult = {
                     success: true,
                     userInfo: userData,
+                    redirectUrl,
+                    entryScene,
                   };
 
                   console.log("✅ 登录成功,准备返回数据:", loginResult);

+ 1 - 1
page/mp_objInp.html

@@ -3,7 +3,7 @@
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
-    <title>公共录入</title>
+    <title>录入</title>
     <script src="/js/mp_base/base.js"></script>
     <style>
         body {

+ 1416 - 1319
page/mp_objList.html

@@ -1,1319 +1,1416 @@
-<!DOCTYPE html>
-<html lang="zh-CN">
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
-    <title>列表</title>
-    <!-- 引入基础依赖(统一由 base.js 动态注入其他依赖) -->
-    <script src="/js/mp_base/base.js"></script>
-    <style>
-        #app {
-            background: #f5f5f5;
-            min-height: 100vh;
-        }
-
-        /* 防止Vue模板闪烁 */
-        [v-cloak] {
-            display: none !important;
-        }
-
-        /* 搜索筛选区域 */
-        .search-filter-container {
-            background: #f5f5f5;
-            padding: 15px;
-            position: sticky;
-            top: 0;
-            z-index: 100;
-            display: flex;
-            justify-content: flex-end;
-            flex-wrap: wrap;
-            gap: 10px;
-        }
-        .search-filter-container .ss-select-container{
-            border: 1px solid #ccc;
-            border-radius: 4px;
-            padding: 0 10px;
-            height: 34px;
-            box-sizing: border-box;
-        }
-        
-        /* 加载状态 */
-        .loading-container {
-            display: flex;
-            flex-direction: column;
-            align-items: center;
-            justify-content: center;
-            height: 200px;
-            color: #666;
-        }
-
-        .loading-spinner {
-            width: 40px;
-            height: 40px;
-            border: 4px solid #f3f3f3;
-            border-top: 4px solid #40ac6d;
-            border-radius: 50%;
-            animation: spin 1s linear infinite;
-            margin-bottom: 15px;
-        }
-
-        @keyframes spin {
-            0% { transform: rotate(0deg); }
-            100% { transform: rotate(360deg); }
-        }
-
-        .loading-text {
-            font-size: 14px;
-        }
-
-        /* 列表容器 */
-        .list-container {
-            padding:0 15px;
-        }
-
-        /* 空状态 */
-        .empty-state {
-            text-align: center;
-            padding: 60px 20px;
-            color: #999;
-        }
-
-        .empty-icon {
-            font-size: 48px;
-            margin-bottom: 15px;
-        }
-
-        .empty-text {
-            font-size: 16px;
-        }
-
-        /* 卡片内容样式 - 按照小程序list.vue转换 */
-        .card-content .card-header {
-            margin-bottom: 10px; /* 20rpx -> 10px */
-        }
-
-        .card-content .card-title {
-            font-size: 16px; /* 32rpx -> 16px */
-            font-weight: bold;
-            color: #333;
-        }
-
-        .card-content .card-description {
-            font-size: 14px; /* 28rpx -> 14px */
-            color: #666;
-            margin-bottom: 8px; /* 15rpx -> 8px */
-        }
-
-        .card-content .attribute-group {
-            display: flex;
-            flex-wrap: wrap;
-            column-gap: 10px; /* 20rpx -> 10px */
-        }
-
-        .card-content .attribute-item {
-            display: flex;
-            margin-bottom: 5px; /* 10rpx -> 5px */
-        }
-
-        .card-content .attr-label {
-            font-size: 13px; /* 26rpx -> 13px */
-            color: #999;
-        }
-
-        .card-content .attr-value {
-            font-size: 13px; /* 26rpx -> 13px */
-            color: #333;
-            flex: 1;
-        }
-
-        /* 状态文本样式 */
-        .status-text {
-            font-weight: bold;
-        }
-
-
-
-        /* 加载更多提示样式 */
-        .load-more-container {
-            text-align: center;
-            padding: 20px;
-            color: #999;
-            font-size: 14px;
-        }
-
-        .load-more-loading {
-            color: #007aff;
-        }
-
-        .load-more-end {
-            color: #999;
-        }
-
-        .load-more-tip {
-            color: #ccc;
-        }
-
-        /* 回到顶部按钮 */
-        .back-to-top-btn {
-            width: 50px;
-            height: 50px;
-            border-radius: 50%;
-            background: rgba(87, 93, 109, 0.5);
-            display: flex;
-            justify-content: center;
-            align-items: center;
-            position: fixed;
-            bottom: 200px;
-            right: 15px;
-            cursor: pointer;
-            transition: all 0.3s ease;
-            z-index: 999;
-        }
-
-        .back-to-top-btn:active {
-            transform: scale(0.9);
-        }
-
-        .back-to-top-inner {
-            width: 42px;
-            height: 42px;
-            border-radius: 50%;
-            background: rgba(87, 93, 109, 0.5);
-            display: flex;
-            justify-content: center;
-            align-items: center;
-        }
-
-        /* 搜索弹窗 */
-        .search-modal-mask {
-            position: fixed;
-            top: 0;
-            left: 0;
-            right: 0;
-            bottom: 0;
-            background: rgba(0, 0, 0, 0.5);
-            z-index: 1000;
-            display: flex;
-            align-items: flex-end;
-        }
-
-        .search-modal-content {
-            width: 100%;
-            background: white;
-            animation: slideUp 0.3s ease-out;
-            /* 让键盘弹起时自动上移 */
-            position: relative;
-        }
-
-        @keyframes slideUp {
-            from {
-                transform: translateY(100%);
-            }
-            to {
-                transform: translateY(0);
-            }
-        }
-
-        .search-input-container {
-            display: flex;
-            align-items: center;
-            height: 50px;
-            background: white;
-            border-top: 1px solid #e5e5e5;
-            /* 适配iPhone底部安全区域 */
-            /* padding-bottom: env(safe-area-inset-bottom); */
-        }
-
-        .search-input {
-            flex: 1;
-            height: 100%;
-            border: none;
-            padding: 0 15px;
-            font-size: 16px;
-            outline: none;
-            background: transparent;
-        }
-
-        .search-divider {
-            width: 1px;
-            height: 30px;
-            background: #d5d8dc;
-        }
-
-        .search-icon-btn {
-            width: 60px;
-            height: 100%;
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            cursor: pointer;
-            background: transparent;
-            border: none;
-            padding: 0;
-        }
-
-        .search-icon-btn:active {
-            background: #f5f5f5;
-        }
-
-    </style>
-</head>
-<body>
-    <div id="app" v-cloak>
-        <!-- 搜索和筛选区域 -->
-        <div class="search-filter-container">
-            <!-- 动态下拉选择器 -->
-            <template v-for="(options, fieldName) in filterSelectOptions" :key="fieldName">
-                <ss-select
-                    v-model="selectedFilters[fieldName]"
-                    :placeholder="`选择${getFieldDesc(fieldName)}`"
-                    :options="options"
-                    @change="handleFilterChange"
-                >
-                </ss-select>
-            </template>
-            <!-- 完全由buttonList决定的动态按钮组 -->
-            <ss-search-button
-                v-for="(button, index) in buttonList"
-                :key="index"
-                :text="getButtonText(button)"
-                
-                @click="handleButtonClick(button)"
-            >
-            </ss-search-button>
-        </div>
-
-        <!-- 加载状态 -->
-        <div v-if="loading" class="loading-container">
-            <div class="loading-spinner"></div>
-            <div class="loading-text">加载中...</div>
-        </div>
-
-        <!-- 列表区域 -->
-        <div v-else class="list-container">
-            <!-- 空状态 -->
-            <div v-if="list.length === 0" class="empty-state">
-                <div class="empty-icon">📋</div>
-                <div class="empty-text">暂无数据</div>
-            </div>
-
-            <!-- 数据列表 -->
-            <div v-else>
-                <ss-card
-                    v-for="(item, index) in list"
-                    :key="index"
-                    :item="item"
-                    @click="handleCardClick(item)"
-                    @button-click="handleCardAction"
-                >
-                    <!-- 卡片内容 - 按照小程序list.vue的结构,使用API数据 -->
-                    <div class="card-content">
-                        <!-- 主标题 (first) -->
-                        <div class="card-header" v-if="item.firstDisplay">
-                            <div class="card-title">{{ item.firstDisplay }}</div>
-                        </div>
-
-                        <!-- 描述 (second) -->
-                        <div class="card-description" v-if="item.secondDisplay">
-                            {{ item.secondDisplay }}
-                        </div>
-
-                        <!-- 属性列表 (third) -->
-                        <div class="card-attributes" v-if="item.thirdDisplay && item.thirdDisplay.length > 0">
-                            <div
-                                v-for="(group, groupIndex) in item.thirdDisplay"
-                                :key="groupIndex"
-                                class="attribute-group"
-                            >
-                                <div
-                                    v-for="(attr, attrIndex) in group"
-                                    :key="attrIndex"
-                                    class="attribute-item"
-                                >
-                                    <span class="attr-label">{{ attr.field.desc }}:</span>
-                                    <span class="attr-value">{{ attr.displayValue }}</span>
-                                </div>
-                            </div>
-                        </div>
-                    </div>
-                </ss-card>
-            </div>
-        </div>
-
-        <!-- 加载更多提示 -->
-        <div class="load-more-container" v-if="list.length > 0">
-            <div v-if="isLoadingMore" class="load-more-loading">
-                <span>正在加载更多...</span>
-            </div>
-            <div v-else-if="!hasMore" class="load-more-end">
-                <span>没有更多数据了</span>
-            </div>
-            <div v-else class="load-more-tip">
-                <span>滚动到底部加载更多</span>
-            </div>
-        </div>
-
-        <!-- 回到顶部按钮 -->
-        <div
-            v-if="showBackToTop"
-            class="back-to-top-btn"
-            @touchstart="handleLongPressStart"
-            @touchend="handleLongPressEnd"
-            @touchcancel="handleLongPressCancel"
-            @click.prevent="handleBackToTopClick"
-        >
-            <div class="back-to-top-inner">
-                <Icon name="icon-huidaodingbu" size="40" color="#fff"></Icon>
-            </div>
-        </div>
-
-        <!-- 搜索弹窗 -->
-        <div v-if="showSearchModal" class="search-modal-mask" @click="closeSearchModal">
-            <div class="search-modal-content" @click.stop>
-                <div class="search-input-container">
-                    <input
-                        ref="searchInput"
-                        v-model="searchKeyword"
-                        type="search"
-                        inputmode="search"
-                        class="search-input"
-                        placeholder="请输入关键词"
-                        @keyup.enter="performSearch"
-                        autocomplete="off"
-                    />
-                    <div class="search-divider"></div>
-                    <button class="search-icon-btn" @click="performSearch">
-                        <Icon name="icon-chazhao" size="24" color="#575d6d"></Icon>
-                    </button>
-                </div>
-            </div>
-        </div>
-
-    </div>
-    <script>
-        // 等待SS框架加载完成
-        window.SS.ready(function () {
-            // 使用SS框架的方式创建Vue实例
-            window.SS.dom.initializeFormApp({
-                el: '#app',
-                data() {
-                    return {
-                        // 加载状态
-                        loading: true,
-
-                        // 列表数据
-                        list: [],
-                        originalList: [], // 原始数据,用于筛选
-
-                        // 分页相关
-                        currentPage: 1,
-                        pageSize: 10,
-                        hasMore: true,
-                        isLoadingMore: false,
-
-                        // 页面参数
-                        pageParams: {},
-                        service: '', // init服务名称
-                        // 功能说明:对接PC两段式接口(init 返回 home/list 服务名) by xu 2026-02-28
-                        ssSearchPobjHomeServName: '',
-                        ssSearchPobjListServName: '',
-
-                        // API返回的动态配置
-                        buttonList: [],
-                        fieldsList: [],
-                        ssPaging: null,
-
-                        // 动态搜索筛选选项
-                        filterOptions: [],
-                        sortOptions: [],
-
-                        // 下拉选择器数据
-                        selectedFilters: {}, // 动态筛选条件
-                        filterSelectOptions: {}, // 各个筛选字段的选项
-
-                        // 字典缓存
-                        dictCache: new Map(),
-
-                        // 显示字段配置 - 根据service动态设置
-                        displayFields: [],
-
-                        // 卡片操作按钮
-                        cardActions: [
-                            { text: '查看', name: 'view' },
-                            { text: '编辑', name: 'edit' }
-                        ],
-
-                        // 回到顶部按钮
-                        showBackToTop: false,
-
-                         // 是否存在关键词搜索
-                        hasKeyWord:false,
-
-                        // 搜索相关
-                        showSearchModal: false,
-                        searchKeyword: '',
-
-                        // 长按相关
-                        longPressTimer: null,
-                        isLongPress: false,
-
-                    }
-                },
-
-                mounted() {
-                    // 页面加载时初始化
-                    this.initPage();
-
-                    // 监听页面刷新通知
-                    this.setupRefreshListener();
-
-                    // 监听滚动事件,实现滚动加载
-                    this.setupScrollListener();
-
-                    // 开发模式:加载Mock数据(测试用)
-                    // this.loadMockData();
-                },
-
-                beforeUnmount() {
-                    // 清理刷新监听器
-                    if (this.refreshCleanup) {
-                        this.refreshCleanup();
-                    }
-
-                    // 清理滚动监听器
-                    if (this.scrollCleanup) {
-                        this.scrollCleanup();
-                    }
-                },
-
-                methods: {
-                    // 加载Mock数据(用于开发测试)
-                    loadMockData() {
-                        console.log('🎭 加载Mock数据...');
-
-                        const mockData = [
-                            {
-                                firstDisplay: '张三 - 2024级计算机科学与技术1班',
-                                secondDisplay: '学号:2024001 | 手机:138****1234',
-                                thirdDisplay: [
-                                    [
-                                        { field: { desc: '性别' }, displayValue: '男' },
-                                        { field: { desc: '年龄' }, displayValue: '20' },
-                                        { field: { desc: '状态' }, displayValue: '在读' }
-                                    ],
-                                    [
-                                        { field: { desc: '入学时间' }, displayValue: '2024-09-01' },
-                                        { field: { desc: '辅导员' }, displayValue: '王老师' }
-                                    ]
-                                ]
-                            },
-                            {
-                                firstDisplay: '李四 - 2024级软件工程2班',
-                                secondDisplay: '学号:2024002 | 手机:139****5678',
-                                thirdDisplay: [
-                                    [
-                                        { field: { desc: '性别' }, displayValue: '女' },
-                                        { field: { desc: '年龄' }, displayValue: '19' },
-                                        { field: { desc: '状态' }, displayValue: '在读' }
-                                    ],
-                                    [
-                                        { field: { desc: '入学时间' }, displayValue: '2024-09-01' },
-                                        { field: { desc: '辅导员' }, displayValue: '李老师' }
-                                    ]
-                                ]
-                            },
-                            {
-                                firstDisplay: '王五 - 2023级人工智能1班',
-                                secondDisplay: '学号:2023003 | 手机:136****9012',
-                                thirdDisplay: [
-                                    [
-                                        { field: { desc: '性别' }, displayValue: '男' },
-                                        { field: { desc: '年龄' }, displayValue: '21' },
-                                        { field: { desc: '状态' }, displayValue: '在读' }
-                                    ],
-                                    [
-                                        { field: { desc: '入学时间' }, displayValue: '2023-09-01' },
-                                        { field: { desc: '辅导员' }, displayValue: '赵老师' }
-                                    ]
-                                ]
-                            },
-                            {
-                                firstDisplay: '赵六 - 2024级数据科学与大数据技术1班',
-                                secondDisplay: '学号:2024004 | 手机:137****3456',
-                                thirdDisplay: [
-                                    [
-                                        { field: { desc: '性别' }, displayValue: '女' },
-                                        { field: { desc: '年龄' }, displayValue: '20' },
-                                        { field: { desc: '状态' }, displayValue: '在读' }
-                                    ],
-                                    [
-                                        { field: { desc: '入学时间' }, displayValue: '2024-09-01' },
-                                        { field: { desc: '辅导员' }, displayValue: '刘老师' }
-                                    ]
-                                ]
-                            },
-                            {
-                                firstDisplay: '钱七 - 2023级网络工程1班',
-                                secondDisplay: '学号:2023005 | 手机:135****7890',
-                                thirdDisplay: [
-                                    [
-                                        { field: { desc: '性别' }, displayValue: '男' },
-                                        { field: { desc: '年龄' }, displayValue: '21' },
-                                        { field: { desc: '状态' }, displayValue: '休学' }
-                                    ],
-                                    [
-                                        { field: { desc: '入学时间' }, displayValue: '2023-09-01' },
-                                        { field: { desc: '辅导员' }, displayValue: '周老师' }
-                                    ]
-                                ]
-                            },
-                            {
-                                firstDisplay: '孙八 - 2024级信息安全1班',
-                                secondDisplay: '学号:2024006 | 手机:133****2468',
-                                thirdDisplay: [
-                                    [
-                                        { field: { desc: '性别' }, displayValue: '女' },
-                                        { field: { desc: '年龄' }, displayValue: '19' },
-                                        { field: { desc: '状态' }, displayValue: '在读' }
-                                    ],
-                                    [
-                                        { field: { desc: '入学时间' }, displayValue: '2024-09-01' },
-                                        { field: { desc: '辅导员' }, displayValue: '吴老师' }
-                                    ]
-                                ]
-                            },
-                            {
-                                firstDisplay: '周九 - 2023级物联网工程1班',
-                                secondDisplay: '学号:2023007 | 手机:188****1357',
-                                thirdDisplay: [
-                                    [
-                                        { field: { desc: '性别' }, displayValue: '男' },
-                                        { field: { desc: '年龄' }, displayValue: '22' },
-                                        { field: { desc: '状态' }, displayValue: '在读' }
-                                    ],
-                                    [
-                                        { field: { desc: '入学时间' }, displayValue: '2023-09-01' },
-                                        { field: { desc: '辅导员' }, displayValue: '郑老师' }
-                                    ]
-                                ]
-                            },
-                            {
-                                firstDisplay: '吴十 - 2024级云计算1班',
-                                secondDisplay: '学号:2024008 | 手机:189****2468',
-                                thirdDisplay: [
-                                    [
-                                        { field: { desc: '性别' }, displayValue: '女' },
-                                        { field: { desc: '年龄' }, displayValue: '20' },
-                                        { field: { desc: '状态' }, displayValue: '在读' }
-                                    ],
-                                    [
-                                        { field: { desc: '入学时间' }, displayValue: '2024-09-01' },
-                                        { field: { desc: '辅导员' }, displayValue: '冯老师' }
-                                    ]
-                                ]
-                            },
-                            {
-                                firstDisplay: '郑十一 - 2023级区块链工程1班',
-                                secondDisplay: '学号:2023009 | 手机:180****3691',
-                                thirdDisplay: [
-                                    [
-                                        { field: { desc: '性别' }, displayValue: '男' },
-                                        { field: { desc: '年龄' }, displayValue: '21' },
-                                        { field: { desc: '状态' }, displayValue: '在读' }
-                                    ],
-                                    [
-                                        { field: { desc: '入学时间' }, displayValue: '2023-09-01' },
-                                        { field: { desc: '辅导员' }, displayValue: '陈老师' }
-                                    ]
-                                ]
-                            },
-                            {
-                                firstDisplay: '王十二 - 2024级电子商务1班',
-                                secondDisplay: '学号:2024010 | 手机:181****4802',
-                                thirdDisplay: [
-                                    [
-                                        { field: { desc: '性别' }, displayValue: '女' },
-                                        { field: { desc: '年龄' }, displayValue: '19' },
-                                        { field: { desc: '状态' }, displayValue: '在读' }
-                                    ],
-                                    [
-                                        { field: { desc: '入学时间' }, displayValue: '2024-09-01' },
-                                        { field: { desc: '辅导员' }, displayValue: '褚老师' }
-                                    ]
-                                ]
-                            },
-                            {
-                                firstDisplay: '陈十三 - 2023级金融科技1班',
-                                secondDisplay: '学号:2023011 | 手机:182****5913',
-                                thirdDisplay: [
-                                    [
-                                        { field: { desc: '性别' }, displayValue: '男' },
-                                        { field: { desc: '年龄' }, displayValue: '22' },
-                                        { field: { desc: '状态' }, displayValue: '毕业' }
-                                    ],
-                                    [
-                                        { field: { desc: '入学时间' }, displayValue: '2023-09-01' },
-                                        { field: { desc: '辅导员' }, displayValue: '卫老师' }
-                                    ]
-                                ]
-                            },
-                            {
-                                firstDisplay: '刘十四 - 2024级数字媒体技术1班',
-                                secondDisplay: '学号:2024012 | 手机:183****6024',
-                                thirdDisplay: [
-                                    [
-                                        { field: { desc: '性别' }, displayValue: '女' },
-                                        { field: { desc: '年龄' }, displayValue: '20' },
-                                        { field: { desc: '状态' }, displayValue: '在读' }
-                                    ],
-                                    [
-                                        { field: { desc: '入学时间' }, displayValue: '2024-09-01' },
-                                        { field: { desc: '辅导员' }, displayValue: '蒋老师' }
-                                    ]
-                                ]
-                            }
-                        ];
-
-                        // 设置数据
-                        this.list = mockData;
-                        this.originalList = [...mockData];
-                        this.loading = false;
-                        this.hasMore = false;
-
-                        console.log('✅ Mock数据加载完成,共', mockData.length, '条');
-                    },
-
-                    // 初始化页面
-                    async initPage() {
-                        try {
-                            console.log('🔄 初始化列表页面...');
-
-                            // 获取URL参数
-                            this.pageParams = this.getUrlParams();
-                            this.service = this.pageParams.service || 'default';
-
-                            console.log('📋 页面参数:', this.pageParams);
-
-                            // 功能说明:先调 init,再按 init 返回的 home/list 服务继续拉取数据 by xu 2026-02-28
-                            await this.loadInitAndHomeData();
-
-                        } catch (error) {
-                            console.log('❌ 页面初始化失败:', error);
-                            // this.showToast('页面初始化失败', 'error');
-                        } finally {
-                            this.loading = false;
-                        }
-                    },
-
-                    // 功能说明:调用 init 接口并解析 ssSearchPobjHomeServName/ssSearchPobjListServName by xu 2026-02-28
-                    async loadInitAndHomeData() {
-                        const initService = (this.service || '').trim();
-                        if (!initService) {
-                            await this.loadData(1, false);
-                            return;
-                        }
-
-                        const initParams = {
-                            pageNo: 1,
-                            rowNumPer: this.pageSize,
-                            management: '1',
-                            isReady: '1'
-                        };
-
-                        const initResult = await request.post(
-                            `/service?ssServ=${initService}&management=1&isReady=1`,
-                            initParams,
-                            {
-                                loading: false,
-                                formData: true
-                            }
-                        );
-
-                        const initPayload = this.unwrapResponseData(initResult?.data);
-                        this.ssSearchPobjHomeServName = String(initPayload?.ssSearchPobjHomeServName || '').trim();
-                        this.ssSearchPobjListServName = String(initPayload?.ssSearchPobjListServName || '').trim();
-
-                        console.log('✅ init返回服务名:', {
-                            initService,
-                            ssSearchPobjHomeServName: this.ssSearchPobjHomeServName,
-                            ssSearchPobjListServName: this.ssSearchPobjListServName
-                        });
-
-                        const homeService = this.ssSearchPobjHomeServName || initService;
-                        await this.loadDataByService(homeService, 1, false);
-                    },
-
-                    // 功能说明:统一处理 /service 返回结构(兼容 {ssData} 与平铺结构) by xu 2026-02-28
-                    unwrapResponseData(data) {
-                        if (!data || typeof data !== 'object') {
-                            return {};
-                        }
-                        if (data.ssData && typeof data.ssData === 'object') {
-                            return data.ssData;
-                        }
-                        return data;
-                    },
-
-                    // 功能说明:封装按指定 ssServ 拉取列表数据(home/list 共用) by xu 2026-02-28
-                    async loadDataByService(ssServ, pageNo = 1, isLoadMore = false) {
-                        const serviceName = String(ssServ || '').trim();
-                        if (!serviceName) {
-                            console.warn('⚠️ 缺少服务名,跳过请求');
-                            return;
-                        }
-
-                        // 防止重复加载:只有在非首次加载且正在加载时才阻止
-                        if (this.loading && !isLoadMore && this.list.length > 0) return;
-                        if (this.isLoadingMore && isLoadMore) return;
-
-                        try {
-                            console.log(`🔄 加载列表数据... 服务: ${serviceName}, 页码: ${pageNo}, 加载更多: ${isLoadMore}`);
-
-                            if (isLoadMore) {
-                                this.isLoadingMore = true;
-                            } else {
-                                this.loading = true;
-                            }
-
-                            const requestParams = {
-                                pageNo: pageNo,
-                                rowNumPer: this.pageSize,
-                                management: '1',
-                                isReady: '1',
-                                // 功能说明:请求参数只保留有效筛选项(避免空值/旧值污染查询) by xu 2026-02-28
-                                ...this.getActiveFilterParams(this.selectedFilters)
-                            };
-
-                            const result = await request.post(
-                                `/service?ssServ=${serviceName}&management=1&isReady=1`,
-                                requestParams,
-                                {
-                                    loading: false,
-                                    formData: true
-                                }
-                            );
-
-                            console.log('✅ API响应数据:', result);
-
-                            if (result && result.data) {
-                                await this.processApiData(result.data, isLoadMore);
-                            } else {
-                                console.warn('⚠️ API返回数据格式异常:', result);
-                            }
-
-                        } catch (error) {
-                            console.error('❌ 数据加载失败:', error);
-                        } finally {
-                            if (isLoadMore) {
-                                this.isLoadingMore = false;
-                            } else {
-                                this.loading = false;
-                            }
-                        }
-                    },
-
-                    // 加载数据
-                    async loadData(pageNo = 1, isLoadMore = false) {
-                        // 功能说明:翻页/筛选/搜索统一走 list 服务;未返回时回退 home/init by xu 2026-02-28
-                        const listService = this.ssSearchPobjListServName || this.ssSearchPobjHomeServName || this.service;
-                        await this.loadDataByService(listService, pageNo, isLoadMore);
-                    },
-
-                    // 处理API返回的数据
-                    async processApiData(data, isLoadMore = false) {
-                        try {
-                            const payload = this.unwrapResponseData(data);
-                            const objectList = Array.isArray(payload.objectList)
-                                ? payload.objectList
-                                : Array.isArray(payload.objList)
-                                    ? payload.objList
-                                    : [];
-                            const draftList = Array.isArray(payload.draftList) ? payload.draftList : [];
-                            const combinedObjectList = draftList.concat(objectList);
-
-                            console.log('🔄 处理API数据...', {
-                                isLoadMore,
-                                objectListLength: combinedObjectList.length,
-                                fromObjList: Array.isArray(payload.objList)
-                            });
-
-                            // 保存API返回的配置信息
-                            if (!isLoadMore) {
-                                // 功能说明:列表接口通常不返回按钮/搜索字段,缺省时保留首屏(home)已加载配置,避免筛选后按钮消失 by xu 2026-02-28
-                                if (Array.isArray(payload.buttonList) || Array.isArray(payload.rootFuncList)) {
-                                    this.buttonList = payload.buttonList || payload.rootFuncList || [];
-                                }
-                                if (Array.isArray(payload.fieldsList) || Array.isArray(payload.searchFieldList)) {
-                                    this.fieldsList = payload.fieldsList || payload.searchFieldList || [];
-                                }
-                            }
-                            // ssPaging信息每次都要更新,因为包含当前页信息
-                            this.ssPaging = payload.ssPaging || this.ssPaging || null;
-                            // 功能说明:list接口未返回 hasKeyword 时沿用首屏值,避免长按搜索入口被错误隐藏 by xu 2026-02-28
-                            if (Object.prototype.hasOwnProperty.call(payload, 'hasKeyWord') || Object.prototype.hasOwnProperty.call(payload, 'hasKeyword')) {
-                                this.hasKeyWord = payload.hasKeyWord || payload.hasKeyword || false;
-                            }
-                            // 处理objectList数据
-                            if (combinedObjectList.length > 0) {
-                                // 使用field-formatter.js格式化列表数据
-                                const formattedList = await window.formatObjectList(combinedObjectList, this.dictCache);
-                                // 功能说明:把后端 chg/chgRootFuncList 映射成卡片左滑操作按钮,标题取 desc,供 ss-card 左滑动作区使用 by xu 2026-03-06
-                                const enhancedList = formattedList.map((item, index) => {
-                                    const rawItem = combinedObjectList[index] || {};
-                                    const rawActions = Array.isArray(rawItem.chgRootFuncList) && rawItem.chgRootFuncList.length > 0
-                                        ? rawItem.chgRootFuncList
-                                        : rawItem.chg
-                                            ? [rawItem.chg]
-                                            : [];
-
-                                    return {
-                                        ...item,
-                                        ssObjId: item.ssObjId || rawItem.ssObjId || '',
-                                        ssObjName: item.ssObjName || rawItem.ssObjName || '',
-                                        swipeActions: rawActions.map(action => ({
-                                            ...action,
-                                            title: action.desc || action.title || '操作'
-                                        }))
-                                    };
-                                });
-
-                                if (isLoadMore) {
-                                    // 加载更多:追加到现有列表
-                                    this.list = [...this.list, ...enhancedList];
-                                    this.originalList = [...this.originalList, ...enhancedList];
-                                } else {
-                                    // 首次加载或刷新:替换列表
-                                    this.originalList = enhancedList;
-                                    this.list = [...this.originalList];
-                                }
-
-                                console.log('✅ 列表数据处理完成:', this.list.length, '条');
-
-                                // 更新分页状态
-                                this.currentPage = isLoadMore ? this.currentPage + 1 : 1;
-
-                                // 根据ssPaging信息判断是否还有更多数据
-                                if (this.ssPaging && this.ssPaging.rowNum !== undefined) {
-                                    const totalRecords = this.ssPaging.rowNum;
-                                    const currentRecords = this.list.length;
-                                    this.hasMore = currentRecords < totalRecords;
-                                    console.log('📊 分页信息:', {
-                                        totalRecords,
-                                        currentRecords,
-                                        hasMore: this.hasMore,
-                                        currentPage: this.currentPage
-                                    });
-                                } else {
-                                    // 降级处理:根据当前页数据量判断
-                                    this.hasMore = formattedList.length >= this.pageSize;
-                                    console.log('⚠️ 使用降级分页判断:', {
-                                        returnedCount: formattedList.length,
-                                        pageSize: this.pageSize,
-                                        hasMore: this.hasMore
-                                    });
-                                }
-                            } else {
-                                // 没有更多数据
-                                this.hasMore = false;
-                                console.log('❌ 没有返回数据,设置hasMore为false');
-                            }
-
-                            // 根据fieldsList生成筛选选项(只在首次加载时生成)
-                            if (!isLoadMore) {
-                                await this.generateFilterOptions();
-                            }
-
-                        } catch (error) {
-                            console.error('❌ 数据处理失败:', error);
-                            throw error;
-                        }
-                    },
-
-
-
-
-
-                    // 获取URL参数
-                    getUrlParams() {
-                        const params = {};
-                        const urlSearchParams = new URLSearchParams(window.location.search);
-                        for (const [key, value] of urlSearchParams) {
-                            params[key] = decodeURIComponent(value);
-                        }
-                        return params;
-                    },
-
-
-
-                    // 处理buttonList按钮点击
-                    handleButtonClick(button) {
-                        console.log('🔘 按钮点击:', button);
-
-                        // 直接跳转到目标页面
-                        const destPage = button.function?.dest || button.dest;
-                        NavigationManager.goToFromButton(button);
-                    },
-
-
-
-                    // 跳转到目标页面
-                    navigateToPage(button, destPage) {
-                        const urlParams = new URLSearchParams(window.location.search);
-
-                        // 添加按钮相关参数
-                        if (button.function) {
-                            urlParams.set('dest', destPage);
-                            urlParams.set('title', encodeURIComponent(button.function.desc || button.buttonName));
-                            urlParams.set('service', button.function.servName || button.service || '');
-                        } else {
-                            urlParams.set('dest', destPage);
-                            urlParams.set('title', encodeURIComponent(button.buttonName));
-                            urlParams.set('service', button.service || '');
-                        }
-                        const newUrl = `${destPage}.html?${urlParams.toString()}`;
-
-                        console.log('� 跳转到:', newUrl);
-                        window.location.href = newUrl;
-                    },
-
-
-
-                    // 根据fieldsList生成筛选选项
-                    async generateFilterOptions() {
-                        if (!this.fieldsList || this.fieldsList.length === 0) {
-                            return;
-                        }
-
-                        for (const field of this.fieldsList) {
-                            // 如果字段有cbName,生成下拉选项
-                            if (field.cbName) {
-                                try {
-                                    const options = await window.getDictOptions(field.cbName, this.dictCache);
-                                    this.filterSelectOptions[field.name] = [
-                                        { n: `全部${field.desc}`, v: '' },
-                                        ...options
-                                    ];
-                                } catch (error) {
-                                    console.error('获取筛选选项失败:', field.cbName, error);
-                                }
-                            }
-                        }
-
-                        console.log('🔽 生成筛选选项:', this.filterSelectOptions);
-                    }, 
-
-                    // 功能说明:统一处理卡片服务跳转,页面文件名按后端 dest 自动补 mp_,但传给业务页/后端的 dest 仍保持原始值 by xu 2026-03-06
-                    openServicePage(action, item = {}) {
-                        const target = action && typeof action === 'object' ? action : null;
-                        if (!target) {
-                            this.showToast('当前记录缺少操作配置', 'warning');
-                            return;
-                        }
-
-                        const serviceName = String(target.servName || target.ssServ || '').trim();
-                        const rawDest = String(target.dest || '').trim();
-                        const normalizedDest = rawDest && rawDest.startsWith('mp_') ? rawDest : (rawDest ? `mp_${rawDest}` : '');
-                        const paramName = String(target.param_name || '').trim();
-                        const paramValue = target.param_value;
-                        const ssToken = String(target.ssToken || '').trim();
-                        const paramText = typeof target.parm === 'string' ? target.parm : '';
-
-                        if (!serviceName) {
-                            this.showToast('缺少服务名', 'warning');
-                            return;
-                        }
-
-                        if (!normalizedDest) {
-                            this.showToast('缺少目标页面', 'warning');
-                            return;
-                        }
-
-                        NavigationManager.goTo(normalizedDest, {
-                            title: target.desc || target.title || '处理',
-                            service: serviceName,
-                            dest: rawDest,
-                            ssDest: rawDest,
-                            param: paramText,
-                            playParamName: paramName,
-                            playParamValue: paramValue,
-                            ssToken: ssToken,
-                            ssObjId: item.ssObjId || '',
-                            ssObjName: item.ssObjName || '',
-                            management: this.pageParams.management || '1',
-                            [paramName || 'param_value']: paramValue
-                        });
-                    },
-
-                    // 卡片点击 - SsCard组件会自动传递item数据
-                    handleCardClick(item) {
-                        console.log('📄 卡片点击事件触发',item);
-                        // 功能说明:卡片点击统一按 play 参数跳转到通用查看页 mp_objplay(兼容 play / service.play 两种结构) by xu 2026-03-04
-                        const play = (item && (item.play || (item.service && item.service.play))) || null;
-                        this.openServicePage(play, item);
-                    },
-
-                    // 卡片操作 - SsCard组件的按钮点击事件
-                    handleCardAction({ button, item, index }) {
-                        console.log('⚡ 卡片操作:', button, item);
-                        // 功能说明:左滑操作按钮按服务配置跳转(如 chg=变动),不再弹 Toast 占位 by xu 2026-03-06
-                        this.openServicePage(button, item);
-                    },
-
-                    // 加载更多数据
-                    async loadMore() {
-                        if (!this.hasMore || this.isLoadingMore) {
-                            console.log('🚫 无法加载更多:', { hasMore: this.hasMore, isLoadingMore: this.isLoadingMore });
-                            return;
-                        }
-
-                        console.log('📄 加载更多数据...');
-                        await this.loadData(this.currentPage + 1, true);
-                    },
-
-                    // 刷新数据
-                    async refreshData() {
-                        console.log('🔄 刷新数据...');
-                        // 重置分页状态
-                        this.currentPage = 1;
-                        this.hasMore = true;
-                        this.list = [];
-                        this.originalList = [];
-
-                        try {
-                            await this.loadData(1, false);
-                            this.showToast('刷新成功', 'success');
-                        } catch (error) {
-                            this.showToast('刷新失败', 'error');
-                        }
-                    },
-
-                    // 获取字段描述
-                    getFieldDesc(fieldName) {
-                        const field = this.fieldsList.find(f => f.name === fieldName);
-                        return field ? field.desc : fieldName;
-                    },
-
-                    // 功能说明:统一按钮文案映射(兼容 rootFuncList/buttonList 不同字段) by xu 2026-02-28
-                    getButtonText(button) {
-                        if (!button || typeof button !== 'object') return '';
-                        return button.desc || button.title || button.buttonName || button.name || '';
-                    },
-
-                    // 功能说明:提取有效筛选参数(过滤空串/null/undefined) by xu 2026-02-28
-                    getActiveFilterParams(source) {
-                        const params = {};
-                        const obj = (source && typeof source === 'object') ? source : {};
-                        Object.entries(obj).forEach(([key, value]) => {
-                            if (value === '' || value === null || value === undefined) return;
-                            params[key] = value;
-                        });
-                        return params;
-                    },
-
-                    // 筛选选择器变化(立即搜索)
-                    handleFilterChange(value) {
-                        console.log('🔽 筛选条件变化,立即搜索:', value);
-                        // 立即执行搜索
-                        this.applyDynamicFilters();
-                    },
-
-                    // 应用动态筛选(重新调用API)
-                    async applyDynamicFilters() {
-                        try {
-                            console.log('🔍 应用筛选条件:', this.selectedFilters);
-
-                            // 功能说明:筛选参数先归一化,清理已取消的筛选条件 by xu 2026-02-28
-                            const filterParams = this.getActiveFilterParams(this.selectedFilters);
-
-                            // 功能说明:筛选后重置分页并走 list 服务 by xu 2026-02-28
-                            this.currentPage = 1;
-                            this.hasMore = true;
-                            this.list = [];
-                            this.originalList = [];
-                            this.selectedFilters = { ...filterParams };
-                            await this.loadData(1, false);
-
-                        } catch (error) {
-                            console.error('❌ 筛选失败:', error);
-                            this.showToast('筛选失败', 'error');
-                        }
-                    },
-
-                    // 生成项目按钮
-                    generateItemButtons(service) {
-                        if (!service || !service.play) return [];
-
-                        return [{
-                            title: service.play.name || '查看',
-                            icon: 'icon-chakan',
-                            onclick: () => {
-                                console.log('点击查看按钮:', service.play);
-                                // 这里可以跳转到详情页面
-                                this.showToast(`查看: ${service.play.title}`, 'info');
-                            }
-                        }];
-                    },
-
-
-
-                    // 设置页面刷新监听
-                    setupRefreshListener() {
-                        // 监听子页面返回时的刷新通知
-                        this.refreshCleanup = NavigationManager.onRefreshNotify((refreshData) => {
-                            console.log('📢 收到刷新通知,重新加载数据');
-                            this.refreshData();
-                        });
-                    },
-
-                    // 设置滚动监听
-                    setupScrollListener() {
-                        let isThrottled = false;
-
-                        const handleScroll = () => {
-                            if (isThrottled) return;
-
-                            isThrottled = true;
-                            setTimeout(() => {
-                                isThrottled = false;
-                            }, 200); // 节流200ms
-
-                            // 检查是否滚动到底部
-                            const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
-                            const windowHeight = window.innerHeight;
-                            const documentHeight = document.documentElement.scrollHeight;
-
-                            // 控制回到顶部按钮显示:滚动超过300px时显示
-                            this.showBackToTop = scrollTop > 300;
-
-                            // 距离底部50px时触发加载更多
-                            if (scrollTop + windowHeight >= documentHeight - 50) {
-                                console.log('📄 滚动到底部,尝试加载更多...');
-                                this.loadMore();
-                            }
-                        };
-
-                        // 添加滚动监听
-                        window.addEventListener('scroll', handleScroll);
-
-                        // 保存清理函数
-                        this.scrollCleanup = () => {
-                            window.removeEventListener('scroll', handleScroll);
-                        };
-                    },
-
-
-
-                    // 返回顶部
-                    scrollToTop() {
-                        window.scrollTo({
-                            top: 0,
-                            behavior: 'smooth'
-                        });
-                    },
-
-                    // 处理回到顶部按钮点击
-                    handleBackToTopClick() {
-                        // 如果不是长按触发的搜索,则执行返回顶部
-                        if (!this.isLongPress) {
-                            this.scrollToTop();
-                        }
-                        // 重置长按标志
-                        this.isLongPress = false;
-                    },
-
-                    // 长按开始
-                    handleLongPressStart(event) {
-                        if(this.hasKeyWord){ // 有输入关键字才显示,否则不处理长按
-                            this.isLongPress = false;
-
-                            // 设置长按定时器(500ms)
-                            this.longPressTimer = setTimeout(() => {
-                                this.isLongPress = true;
-                                this.openSearchModal();
-                                // 震动反馈(如果支持)
-                                if (navigator.vibrate) {
-                                    navigator.vibrate(50);
-                                }
-                            }, 500);
-                        }
-                    },
-
-                    // 长按结束
-                    handleLongPressEnd(event) {
-                        // 清除长按定时器
-                        if (this.longPressTimer) {
-                            clearTimeout(this.longPressTimer);
-                            this.longPressTimer = null;
-                        }
-                    },
-
-                    // 长按取消(手指移出按钮区域)
-                    handleLongPressCancel(event) {
-                        // 清除长按定时器
-                        if (this.longPressTimer) {
-                            clearTimeout(this.longPressTimer);
-                            this.longPressTimer = null;
-                        }
-                        this.isLongPress = false;
-                    },
-
-                    // 打开搜索弹窗
-                    openSearchModal() {
-                        this.showSearchModal = true;
-                        this.searchKeyword = '';
-
-                        // 延迟聚焦输入框,确保DOM已渲染和动画完成
-                        this.$nextTick(() => {
-                            // 使用 setTimeout 确保在移动端也能正常触发键盘
-                            setTimeout(() => {
-                                if (this.$refs.searchInput) {
-                                    // 先点击再聚焦,确保移动端键盘弹出
-                                    this.$refs.searchInput.click();
-                                    this.$refs.searchInput.focus();
-                                    console.log('✅ 输入框已聚焦');
-                                }
-                            }, 100); // 等待动画完成后再聚焦
-                        });
-                    },
-
-                    // 关闭搜索弹窗
-                    closeSearchModal() {
-                        this.showSearchModal = false;
-                        this.searchKeyword = '';
-                    },
-
-                    // 执行搜索
-                    async performSearch() {
-                        const keyword = this.searchKeyword.trim();
-
-                        // 无关键词时,直接关闭弹窗,什么都不做
-                        if (!keyword) {
-                            // this.closeSearchModal();
-                            return;
-                        }
-
-                        console.log('🔍 执行关键词搜索:', keyword);
-
-                        // 关闭搜索弹窗
-                        this.closeSearchModal();
-
-                        // 设置 keyword 到筛选条件中
-                        this.selectedFilters.ssKeyword = keyword;
-
-                        // 调用 API 搜索
-                        await this.applyDynamicFilters();
-                    },
-
-                    // 显示提示
-                    showToast(message, type = 'info') {
-                        console.log(`${type.toUpperCase()}: ${message}`);
-                        // 使用浏览器原生alert,后续可以替换为更好的提示组件
-                        alert(message);
-                    }
-                }
-            })
-        })
-
-    </script>
-</body>
-</html>
+<!DOCTYPE html>
+<html lang="zh-CN">
+  <head>
+    <meta charset="UTF-8" />
+    <meta
+      name="viewport"
+      content="width=device-width, initial-scale=1.0, user-scalable=no"
+    />
+    <title>列表</title>
+    <!-- 引入基础依赖(统一由 base.js 动态注入其他依赖) -->
+    <script src="/js/mp_base/base.js"></script>
+    <style>
+      #app {
+        background: #f5f5f5;
+        min-height: 100vh;
+      }
+
+      /* 防止Vue模板闪烁 */
+      [v-cloak] {
+        display: none !important;
+      }
+
+      /* 搜索筛选区域 */
+      .search-filter-container {
+        background: #f5f5f5;
+        padding: 15px;
+        position: sticky;
+        top: 0;
+        z-index: 100;
+        display: flex;
+        justify-content: flex-end;
+        flex-wrap: wrap;
+        gap: 10px;
+      }
+      .search-filter-container .ss-select-container {
+        border: 1px solid #ccc;
+        border-radius: 4px;
+        padding: 0 10px;
+        height: 34px;
+        box-sizing: border-box;
+      }
+
+      /* 加载状态 */
+      .loading-container {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        height: 200px;
+        color: #666;
+      }
+
+      .loading-spinner {
+        width: 40px;
+        height: 40px;
+        border: 4px solid #f3f3f3;
+        border-top: 4px solid #40ac6d;
+        border-radius: 50%;
+        animation: spin 1s linear infinite;
+        margin-bottom: 15px;
+      }
+
+      @keyframes spin {
+        0% {
+          transform: rotate(0deg);
+        }
+        100% {
+          transform: rotate(360deg);
+        }
+      }
+
+      .loading-text {
+        font-size: 14px;
+      }
+
+      /* 列表容器 */
+      .list-container {
+        padding: 0 15px;
+      }
+
+      /* 空状态 */
+      .empty-state {
+        text-align: center;
+        padding: 60px 20px;
+        color: #999;
+      }
+
+      .empty-icon {
+        font-size: 48px;
+        margin-bottom: 15px;
+      }
+
+      .empty-text {
+        font-size: 16px;
+      }
+
+      /* 卡片内容样式 - 按照小程序list.vue转换 */
+      .card-content .card-header {
+        margin-bottom: 10px; /* 20rpx -> 10px */
+      }
+
+      .card-content .card-title {
+        font-size: 16px; /* 32rpx -> 16px */
+        font-weight: bold;
+        color: #333;
+      }
+
+      .card-content .card-description {
+        font-size: 14px; /* 28rpx -> 14px */
+        color: #666;
+        margin-bottom: 8px; /* 15rpx -> 8px */
+      }
+
+      .card-content .attribute-group {
+        display: flex;
+        flex-wrap: wrap;
+        column-gap: 10px; /* 20rpx -> 10px */
+      }
+
+      .card-content .attribute-item {
+        display: flex;
+        margin-bottom: 5px; /* 10rpx -> 5px */
+      }
+
+      .card-content .attr-label {
+        font-size: 13px; /* 26rpx -> 13px */
+        color: #999;
+      }
+
+      .card-content .attr-value {
+        font-size: 13px; /* 26rpx -> 13px */
+        color: #333;
+        flex: 1;
+      }
+
+      /* 状态文本样式 */
+      .status-text {
+        font-weight: bold;
+      }
+
+      /* 加载更多提示样式 */
+      .load-more-container {
+        text-align: center;
+        padding: 20px;
+        color: #999;
+        font-size: 14px;
+      }
+
+      .load-more-loading {
+        color: #007aff;
+      }
+
+      .load-more-end {
+        color: #999;
+      }
+
+      .load-more-tip {
+        color: #ccc;
+      }
+
+      /* 回到顶部按钮 */
+      .back-to-top-btn {
+        width: 50px;
+        height: 50px;
+        border-radius: 50%;
+        background: rgba(87, 93, 109, 0.5);
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        position: fixed;
+        bottom: 200px;
+        right: 15px;
+        cursor: pointer;
+        transition: all 0.3s ease;
+        z-index: 999;
+      }
+
+      .back-to-top-btn:active {
+        transform: scale(0.9);
+      }
+
+      .back-to-top-inner {
+        width: 42px;
+        height: 42px;
+        border-radius: 50%;
+        background: rgba(87, 93, 109, 0.5);
+        display: flex;
+        justify-content: center;
+        align-items: center;
+      }
+
+      /* 搜索弹窗 */
+      .search-modal-mask {
+        position: fixed;
+        top: 0;
+        left: 0;
+        right: 0;
+        bottom: 0;
+        background: rgba(0, 0, 0, 0.5);
+        z-index: 1000;
+        display: flex;
+        align-items: flex-end;
+      }
+
+      .search-modal-content {
+        width: 100%;
+        background: white;
+        animation: slideUp 0.3s ease-out;
+        /* 让键盘弹起时自动上移 */
+        position: relative;
+      }
+
+      @keyframes slideUp {
+        from {
+          transform: translateY(100%);
+        }
+        to {
+          transform: translateY(0);
+        }
+      }
+
+      .search-input-container {
+        display: flex;
+        align-items: center;
+        height: 50px;
+        background: white;
+        border-top: 1px solid #e5e5e5;
+        /* 适配iPhone底部安全区域 */
+        /* padding-bottom: env(safe-area-inset-bottom); */
+      }
+
+      .search-input {
+        flex: 1;
+        height: 100%;
+        border: none;
+        padding: 0 15px;
+        font-size: 16px;
+        outline: none;
+        background: transparent;
+      }
+
+      .search-divider {
+        width: 1px;
+        height: 30px;
+        background: #d5d8dc;
+      }
+
+      .search-icon-btn {
+        width: 60px;
+        height: 100%;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        cursor: pointer;
+        background: transparent;
+        border: none;
+        padding: 0;
+      }
+
+      .search-icon-btn:active {
+        background: #f5f5f5;
+      }
+    </style>
+  </head>
+  <body>
+    <div id="app" v-cloak>
+      <!-- 搜索和筛选区域 -->
+      <div class="search-filter-container">
+        <!-- 动态下拉选择器 -->
+        <template
+          v-for="(options, fieldName) in filterSelectOptions"
+          :key="fieldName"
+        >
+          <ss-select
+            v-model="selectedFilters[fieldName]"
+            :placeholder="`选择${getFieldDesc(fieldName)}`"
+            :options="options"
+            width="132px"
+            inp="true"
+            @change="handleFilterChange"
+          >
+          </ss-select>
+        </template>
+        <!-- 完全由buttonList决定的动态按钮组 -->
+        <ss-search-button
+          v-for="(button, index) in buttonList"
+          :key="index"
+          :text="getButtonText(button)"
+          @click="handleButtonClick(button)"
+        >
+        </ss-search-button>
+      </div>
+
+      <!-- 加载状态 -->
+      <div v-if="loading" class="loading-container">
+        <div class="loading-spinner"></div>
+        <div class="loading-text">加载中...</div>
+      </div>
+
+      <!-- 列表区域 -->
+      <div v-else class="list-container">
+        <!-- 空状态 -->
+        <div v-if="list.length === 0" class="empty-state">
+          <div class="empty-text">暂无数据</div>
+        </div>
+
+        <!-- 数据列表 -->
+        <div v-else>
+          <ss-card
+            v-for="(item, index) in list"
+            :key="index"
+            :item="item"
+            @click="handleCardClick(item)"
+            @button-click="handleCardAction"
+          >
+            <!-- 卡片内容 - 按照小程序list.vue的结构,使用API数据 -->
+            <div class="card-content">
+              <!-- 主标题 (first) -->
+              <div class="card-header" v-if="item.firstDisplay">
+                <div class="card-title">{{ item.firstDisplay }}</div>
+              </div>
+
+              <!-- 描述 (second) -->
+              <div class="card-description" v-if="item.secondDisplay">
+                {{ item.secondDisplay }}
+              </div>
+
+              <!-- 属性列表 (third) -->
+              <div
+                class="card-attributes"
+                v-if="item.thirdDisplay && item.thirdDisplay.length > 0"
+              >
+                <div
+                  v-for="(group, groupIndex) in item.thirdDisplay"
+                  :key="groupIndex"
+                  class="attribute-group"
+                >
+                  <div
+                    v-for="(attr, attrIndex) in group"
+                    :key="attrIndex"
+                    class="attribute-item"
+                  >
+                    <span class="attr-label">{{ attr.field.desc }}:</span>
+                    <span class="attr-value">{{ attr.displayValue }}</span>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </ss-card>
+        </div>
+      </div>
+
+      <!-- 加载更多提示 -->
+      <div class="load-more-container" v-if="list.length > 0">
+        <div v-if="isLoadingMore" class="load-more-loading">
+          <span>正在加载更多...</span>
+        </div>
+        <div v-else-if="!hasMore" class="load-more-end">
+          <span>没有更多数据了</span>
+        </div>
+        <div v-else class="load-more-tip">
+          <span>滚动到底部加载更多</span>
+        </div>
+      </div>
+
+      <!-- 回到顶部按钮 -->
+      <div
+        v-if="showBackToTop"
+        class="back-to-top-btn"
+        @touchstart="handleLongPressStart"
+        @touchend="handleLongPressEnd"
+        @touchcancel="handleLongPressCancel"
+        @click.prevent="handleBackToTopClick"
+      >
+        <div class="back-to-top-inner">
+          <Icon name="icon-huidaodingbu" size="40" color="#fff"></Icon>
+        </div>
+      </div>
+
+      <!-- 搜索弹窗 -->
+      <div
+        v-if="showSearchModal"
+        class="search-modal-mask"
+        @click="closeSearchModal"
+      >
+        <div class="search-modal-content" @click.stop>
+          <div class="search-input-container">
+            <input
+              ref="searchInput"
+              v-model="searchKeyword"
+              type="search"
+              inputmode="search"
+              class="search-input"
+              placeholder="请输入关键词"
+              @keyup.enter="performSearch"
+              autocomplete="off"
+            />
+            <div class="search-divider"></div>
+            <button class="search-icon-btn" @click="performSearch">
+              <Icon name="icon-chazhao" size="24" color="#575d6d"></Icon>
+            </button>
+          </div>
+        </div>
+      </div>
+    </div>
+    <script>
+      // 等待SS框架加载完成
+      window.SS.ready(function () {
+        // 使用SS框架的方式创建Vue实例
+        window.SS.dom.initializeFormApp({
+          el: "#app",
+          data() {
+            return {
+              // 加载状态
+              loading: true,
+
+              // 列表数据
+              list: [],
+              originalList: [], // 原始数据,用于筛选
+
+              // 分页相关
+              currentPage: 1,
+              pageSize: 10,
+              hasMore: true,
+              isLoadingMore: false,
+
+              // 页面参数
+              pageParams: {},
+              service: "", // init服务名称
+              // 功能说明:对接PC两段式接口(init 返回 home/list 服务名) by xu 2026-02-28
+              ssSearchPobjHomeServName: "",
+              ssSearchPobjListServName: "",
+
+              // API返回的动态配置
+              buttonList: [],
+              fieldsList: [],
+              ssPaging: null,
+
+              // 动态搜索筛选选项
+              filterOptions: [],
+              sortOptions: [],
+
+              // 下拉选择器数据
+              selectedFilters: {}, // 动态筛选条件
+              filterSelectOptions: {}, // 各个筛选字段的选项
+
+              // 字典缓存
+              dictCache: new Map(),
+
+              // 显示字段配置 - 根据service动态设置
+              displayFields: [],
+
+              // 卡片操作按钮
+              cardActions: [
+                { text: "查看", name: "view" },
+                { text: "编辑", name: "edit" },
+              ],
+
+              // 回到顶部按钮
+              showBackToTop: false,
+
+              // 是否存在关键词搜索
+              hasKeyWord: false,
+
+              // 搜索相关
+              showSearchModal: false,
+              searchKeyword: "",
+
+              // 长按相关
+              longPressTimer: null,
+              isLongPress: false,
+            };
+          },
+
+          mounted() {
+            // 页面加载时初始化
+            this.initPage();
+
+            // 监听页面刷新通知
+            this.setupRefreshListener();
+
+            // 监听滚动事件,实现滚动加载
+            this.setupScrollListener();
+
+            // 开发模式:加载Mock数据(测试用)
+            // this.loadMockData();
+          },
+
+          beforeUnmount() {
+            // 清理刷新监听器
+            if (this.refreshCleanup) {
+              this.refreshCleanup();
+            }
+
+            // 清理滚动监听器
+            if (this.scrollCleanup) {
+              this.scrollCleanup();
+            }
+          },
+
+          methods: {
+            // 加载Mock数据(用于开发测试)
+            loadMockData() {
+              console.log("🎭 加载Mock数据...");
+
+              const mockData = [
+                {
+                  firstDisplay: "张三 - 2024级计算机科学与技术1班",
+                  secondDisplay: "学号:2024001 | 手机:138****1234",
+                  thirdDisplay: [
+                    [
+                      { field: { desc: "性别" }, displayValue: "男" },
+                      { field: { desc: "年龄" }, displayValue: "20" },
+                      { field: { desc: "状态" }, displayValue: "在读" },
+                    ],
+                    [
+                      {
+                        field: { desc: "入学时间" },
+                        displayValue: "2024-09-01",
+                      },
+                      { field: { desc: "辅导员" }, displayValue: "王老师" },
+                    ],
+                  ],
+                },
+                {
+                  firstDisplay: "李四 - 2024级软件工程2班",
+                  secondDisplay: "学号:2024002 | 手机:139****5678",
+                  thirdDisplay: [
+                    [
+                      { field: { desc: "性别" }, displayValue: "女" },
+                      { field: { desc: "年龄" }, displayValue: "19" },
+                      { field: { desc: "状态" }, displayValue: "在读" },
+                    ],
+                    [
+                      {
+                        field: { desc: "入学时间" },
+                        displayValue: "2024-09-01",
+                      },
+                      { field: { desc: "辅导员" }, displayValue: "李老师" },
+                    ],
+                  ],
+                },
+                {
+                  firstDisplay: "王五 - 2023级人工智能1班",
+                  secondDisplay: "学号:2023003 | 手机:136****9012",
+                  thirdDisplay: [
+                    [
+                      { field: { desc: "性别" }, displayValue: "男" },
+                      { field: { desc: "年龄" }, displayValue: "21" },
+                      { field: { desc: "状态" }, displayValue: "在读" },
+                    ],
+                    [
+                      {
+                        field: { desc: "入学时间" },
+                        displayValue: "2023-09-01",
+                      },
+                      { field: { desc: "辅导员" }, displayValue: "赵老师" },
+                    ],
+                  ],
+                },
+                {
+                  firstDisplay: "赵六 - 2024级数据科学与大数据技术1班",
+                  secondDisplay: "学号:2024004 | 手机:137****3456",
+                  thirdDisplay: [
+                    [
+                      { field: { desc: "性别" }, displayValue: "女" },
+                      { field: { desc: "年龄" }, displayValue: "20" },
+                      { field: { desc: "状态" }, displayValue: "在读" },
+                    ],
+                    [
+                      {
+                        field: { desc: "入学时间" },
+                        displayValue: "2024-09-01",
+                      },
+                      { field: { desc: "辅导员" }, displayValue: "刘老师" },
+                    ],
+                  ],
+                },
+                {
+                  firstDisplay: "钱七 - 2023级网络工程1班",
+                  secondDisplay: "学号:2023005 | 手机:135****7890",
+                  thirdDisplay: [
+                    [
+                      { field: { desc: "性别" }, displayValue: "男" },
+                      { field: { desc: "年龄" }, displayValue: "21" },
+                      { field: { desc: "状态" }, displayValue: "休学" },
+                    ],
+                    [
+                      {
+                        field: { desc: "入学时间" },
+                        displayValue: "2023-09-01",
+                      },
+                      { field: { desc: "辅导员" }, displayValue: "周老师" },
+                    ],
+                  ],
+                },
+                {
+                  firstDisplay: "孙八 - 2024级信息安全1班",
+                  secondDisplay: "学号:2024006 | 手机:133****2468",
+                  thirdDisplay: [
+                    [
+                      { field: { desc: "性别" }, displayValue: "女" },
+                      { field: { desc: "年龄" }, displayValue: "19" },
+                      { field: { desc: "状态" }, displayValue: "在读" },
+                    ],
+                    [
+                      {
+                        field: { desc: "入学时间" },
+                        displayValue: "2024-09-01",
+                      },
+                      { field: { desc: "辅导员" }, displayValue: "吴老师" },
+                    ],
+                  ],
+                },
+                {
+                  firstDisplay: "周九 - 2023级物联网工程1班",
+                  secondDisplay: "学号:2023007 | 手机:188****1357",
+                  thirdDisplay: [
+                    [
+                      { field: { desc: "性别" }, displayValue: "男" },
+                      { field: { desc: "年龄" }, displayValue: "22" },
+                      { field: { desc: "状态" }, displayValue: "在读" },
+                    ],
+                    [
+                      {
+                        field: { desc: "入学时间" },
+                        displayValue: "2023-09-01",
+                      },
+                      { field: { desc: "辅导员" }, displayValue: "郑老师" },
+                    ],
+                  ],
+                },
+                {
+                  firstDisplay: "吴十 - 2024级云计算1班",
+                  secondDisplay: "学号:2024008 | 手机:189****2468",
+                  thirdDisplay: [
+                    [
+                      { field: { desc: "性别" }, displayValue: "女" },
+                      { field: { desc: "年龄" }, displayValue: "20" },
+                      { field: { desc: "状态" }, displayValue: "在读" },
+                    ],
+                    [
+                      {
+                        field: { desc: "入学时间" },
+                        displayValue: "2024-09-01",
+                      },
+                      { field: { desc: "辅导员" }, displayValue: "冯老师" },
+                    ],
+                  ],
+                },
+                {
+                  firstDisplay: "郑十一 - 2023级区块链工程1班",
+                  secondDisplay: "学号:2023009 | 手机:180****3691",
+                  thirdDisplay: [
+                    [
+                      { field: { desc: "性别" }, displayValue: "男" },
+                      { field: { desc: "年龄" }, displayValue: "21" },
+                      { field: { desc: "状态" }, displayValue: "在读" },
+                    ],
+                    [
+                      {
+                        field: { desc: "入学时间" },
+                        displayValue: "2023-09-01",
+                      },
+                      { field: { desc: "辅导员" }, displayValue: "陈老师" },
+                    ],
+                  ],
+                },
+                {
+                  firstDisplay: "王十二 - 2024级电子商务1班",
+                  secondDisplay: "学号:2024010 | 手机:181****4802",
+                  thirdDisplay: [
+                    [
+                      { field: { desc: "性别" }, displayValue: "女" },
+                      { field: { desc: "年龄" }, displayValue: "19" },
+                      { field: { desc: "状态" }, displayValue: "在读" },
+                    ],
+                    [
+                      {
+                        field: { desc: "入学时间" },
+                        displayValue: "2024-09-01",
+                      },
+                      { field: { desc: "辅导员" }, displayValue: "褚老师" },
+                    ],
+                  ],
+                },
+                {
+                  firstDisplay: "陈十三 - 2023级金融科技1班",
+                  secondDisplay: "学号:2023011 | 手机:182****5913",
+                  thirdDisplay: [
+                    [
+                      { field: { desc: "性别" }, displayValue: "男" },
+                      { field: { desc: "年龄" }, displayValue: "22" },
+                      { field: { desc: "状态" }, displayValue: "毕业" },
+                    ],
+                    [
+                      {
+                        field: { desc: "入学时间" },
+                        displayValue: "2023-09-01",
+                      },
+                      { field: { desc: "辅导员" }, displayValue: "卫老师" },
+                    ],
+                  ],
+                },
+                {
+                  firstDisplay: "刘十四 - 2024级数字媒体技术1班",
+                  secondDisplay: "学号:2024012 | 手机:183****6024",
+                  thirdDisplay: [
+                    [
+                      { field: { desc: "性别" }, displayValue: "女" },
+                      { field: { desc: "年龄" }, displayValue: "20" },
+                      { field: { desc: "状态" }, displayValue: "在读" },
+                    ],
+                    [
+                      {
+                        field: { desc: "入学时间" },
+                        displayValue: "2024-09-01",
+                      },
+                      { field: { desc: "辅导员" }, displayValue: "蒋老师" },
+                    ],
+                  ],
+                },
+              ];
+
+              // 设置数据
+              this.list = mockData;
+              this.originalList = [...mockData];
+              this.loading = false;
+              this.hasMore = false;
+
+              console.log("✅ Mock数据加载完成,共", mockData.length, "条");
+            },
+
+            // 初始化页面
+            async initPage() {
+              try {
+                console.log("🔄 初始化列表页面...");
+
+                // 获取URL参数
+                this.pageParams = this.getUrlParams();
+                this.service = this.pageParams.service || "default";
+
+                console.log("📋 页面参数:", this.pageParams);
+
+                // 功能说明:先调 init,再按 init 返回的 home/list 服务继续拉取数据 by xu 2026-02-28
+                await this.loadInitAndHomeData();
+              } catch (error) {
+                console.log("❌ 页面初始化失败:", error);
+                // this.showToast('页面初始化失败', 'error');
+              } finally {
+                this.loading = false;
+              }
+            },
+
+            // 功能说明:调用 init 接口并解析 ssSearchPobjHomeServName/ssSearchPobjListServName by xu 2026-02-28
+            async loadInitAndHomeData() {
+              const initService = (this.service || "").trim();
+              if (!initService) {
+                await this.loadData(1, false);
+                return;
+              }
+
+              const initParams = {
+                pageNo: 1,
+                rowNumPer: this.pageSize,
+                management: "1",
+                isReady: "1",
+              };
+
+              const initResult = await request.post(
+                `/service?ssServ=${initService}&management=1&isReady=1`,
+                initParams,
+                {
+                  loading: false,
+                  formData: true,
+                }
+              );
+
+              const initPayload = this.unwrapResponseData(initResult?.data);
+              this.ssSearchPobjHomeServName = String(
+                initPayload?.ssSearchPobjHomeServName || ""
+              ).trim();
+              this.ssSearchPobjListServName = String(
+                initPayload?.ssSearchPobjListServName || ""
+              ).trim();
+
+              console.log("✅ init返回服务名:", {
+                initService,
+                ssSearchPobjHomeServName: this.ssSearchPobjHomeServName,
+                ssSearchPobjListServName: this.ssSearchPobjListServName,
+              });
+
+              const homeService = this.ssSearchPobjHomeServName || initService;
+              await this.loadDataByService(homeService, 1, false);
+            },
+
+            // 功能说明:统一处理 /service 返回结构(兼容 {ssData} 与平铺结构) by xu 2026-02-28
+            unwrapResponseData(data) {
+              if (!data || typeof data !== "object") {
+                return {};
+              }
+              if (data.ssData && typeof data.ssData === "object") {
+                return data.ssData;
+              }
+              return data;
+            },
+
+            // 功能说明:封装按指定 ssServ 拉取列表数据(home/list 共用) by xu 2026-02-28
+            async loadDataByService(ssServ, pageNo = 1, isLoadMore = false) {
+              const serviceName = String(ssServ || "").trim();
+              if (!serviceName) {
+                console.warn("⚠️ 缺少服务名,跳过请求");
+                return;
+              }
+
+              // 防止重复加载:只有在非首次加载且正在加载时才阻止
+              if (this.loading && !isLoadMore && this.list.length > 0) return;
+              if (this.isLoadingMore && isLoadMore) return;
+
+              try {
+                console.log(
+                  `🔄 加载列表数据... 服务: ${serviceName}, 页码: ${pageNo}, 加载更多: ${isLoadMore}`
+                );
+
+                if (isLoadMore) {
+                  this.isLoadingMore = true;
+                } else {
+                  this.loading = true;
+                }
+
+                const requestParams = {
+                  pageNo: pageNo,
+                  rowNumPer: this.pageSize,
+                  management: "1",
+                  isReady: "1",
+                  // 功能说明:请求参数只保留有效筛选项(避免空值/旧值污染查询) by xu 2026-02-28
+                  ...this.getActiveFilterParams(this.selectedFilters),
+                };
+
+                const result = await request.post(
+                  `/service?ssServ=${serviceName}&management=1&isReady=1`,
+                  requestParams,
+                  {
+                    loading: false,
+                    formData: true,
+                  }
+                );
+
+                console.log("✅ API响应数据:", result);
+
+                if (result && result.data) {
+                  await this.processApiData(result.data, isLoadMore);
+                } else {
+                  console.warn("⚠️ API返回数据格式异常:", result);
+                }
+              } catch (error) {
+                console.error("❌ 数据加载失败:", error);
+              } finally {
+                if (isLoadMore) {
+                  this.isLoadingMore = false;
+                } else {
+                  this.loading = false;
+                }
+              }
+            },
+
+            // 加载数据
+            async loadData(pageNo = 1, isLoadMore = false) {
+              // 功能说明:翻页/筛选/搜索统一走 list 服务;未返回时回退 home/init by xu 2026-02-28
+              const listService =
+                this.ssSearchPobjListServName ||
+                this.ssSearchPobjHomeServName ||
+                this.service;
+              await this.loadDataByService(listService, pageNo, isLoadMore);
+            },
+
+            // 处理API返回的数据
+            async processApiData(data, isLoadMore = false) {
+              try {
+                const payload = this.unwrapResponseData(data);
+                const objectList = Array.isArray(payload.objectList)
+                  ? payload.objectList
+                  : Array.isArray(payload.objList)
+                  ? payload.objList
+                  : [];
+                const draftList = Array.isArray(payload.draftList)
+                  ? payload.draftList
+                  : [];
+                const combinedObjectList = draftList.concat(objectList);
+
+                console.log("🔄 处理API数据...", {
+                  isLoadMore,
+                  objectListLength: combinedObjectList.length,
+                  fromObjList: Array.isArray(payload.objList),
+                });
+
+                // 保存API返回的配置信息
+                if (!isLoadMore) {
+                  // 功能说明:列表接口通常不返回按钮/搜索字段,缺省时保留首屏(home)已加载配置,避免筛选后按钮消失 by xu 2026-02-28
+                  if (
+                    Array.isArray(payload.buttonList) ||
+                    Array.isArray(payload.rootFuncList)
+                  ) {
+                    this.buttonList =
+                      payload.buttonList || payload.rootFuncList || [];
+                  }
+                  if (
+                    Array.isArray(payload.fieldsList) ||
+                    Array.isArray(payload.searchFieldList)
+                  ) {
+                    this.fieldsList =
+                      payload.fieldsList || payload.searchFieldList || [];
+                  }
+                }
+                // ssPaging信息每次都要更新,因为包含当前页信息
+                this.ssPaging = payload.ssPaging || this.ssPaging || null;
+                // 功能说明:list接口未返回 hasKeyword 时沿用首屏值,避免长按搜索入口被错误隐藏 by xu 2026-02-28
+                if (
+                  Object.prototype.hasOwnProperty.call(payload, "hasKeyWord") ||
+                  Object.prototype.hasOwnProperty.call(payload, "hasKeyword")
+                ) {
+                  this.hasKeyWord =
+                    payload.hasKeyWord || payload.hasKeyword || false;
+                }
+                // 处理objectList数据
+                if (combinedObjectList.length > 0) {
+                  // 使用field-formatter.js格式化列表数据
+                  const formattedList = await window.formatObjectList(
+                    combinedObjectList,
+                    this.dictCache
+                  );
+                  // 功能说明:把后端 chg/chgRootFuncList 映射成卡片左滑操作按钮,标题取 desc,供 ss-card 左滑动作区使用 by xu 2026-03-06
+                  const enhancedList = formattedList.map((item, index) => {
+                    const rawItem = combinedObjectList[index] || {};
+                    const rawActions =
+                      Array.isArray(rawItem.chgRootFuncList) &&
+                      rawItem.chgRootFuncList.length > 0
+                        ? rawItem.chgRootFuncList
+                        : rawItem.chg
+                        ? [rawItem.chg]
+                        : [];
+
+                    return {
+                      ...item,
+                      ssObjId: item.ssObjId || rawItem.ssObjId || "",
+                      ssObjName: item.ssObjName || rawItem.ssObjName || "",
+                      swipeActions: rawActions.map((action) => ({
+                        ...action,
+                        title: action.desc || action.title || "操作",
+                      })),
+                    };
+                  });
+
+                  if (isLoadMore) {
+                    // 加载更多:追加到现有列表
+                    this.list = [...this.list, ...enhancedList];
+                    this.originalList = [...this.originalList, ...enhancedList];
+                  } else {
+                    // 首次加载或刷新:替换列表
+                    this.originalList = enhancedList;
+                    this.list = [...this.originalList];
+                  }
+
+                  console.log("✅ 列表数据处理完成:", this.list.length, "条");
+
+                  // 更新分页状态
+                  this.currentPage = isLoadMore ? this.currentPage + 1 : 1;
+
+                  // 根据ssPaging信息判断是否还有更多数据
+                  if (this.ssPaging && this.ssPaging.rowNum !== undefined) {
+                    const totalRecords = this.ssPaging.rowNum;
+                    const currentRecords = this.list.length;
+                    this.hasMore = currentRecords < totalRecords;
+                    console.log("📊 分页信息:", {
+                      totalRecords,
+                      currentRecords,
+                      hasMore: this.hasMore,
+                      currentPage: this.currentPage,
+                    });
+                  } else {
+                    // 降级处理:根据当前页数据量判断
+                    this.hasMore = formattedList.length >= this.pageSize;
+                    console.log("⚠️ 使用降级分页判断:", {
+                      returnedCount: formattedList.length,
+                      pageSize: this.pageSize,
+                      hasMore: this.hasMore,
+                    });
+                  }
+                } else {
+                  // 没有更多数据
+                  this.hasMore = false;
+                  console.log("❌ 没有返回数据,设置hasMore为false");
+                }
+
+                // 根据fieldsList生成筛选选项(只在首次加载时生成)
+                if (!isLoadMore) {
+                  await this.generateFilterOptions();
+                }
+              } catch (error) {
+                console.error("❌ 数据处理失败:", error);
+                throw error;
+              }
+            },
+
+            // 获取URL参数
+            getUrlParams() {
+              const params = {};
+              const urlSearchParams = new URLSearchParams(
+                window.location.search
+              );
+              for (const [key, value] of urlSearchParams) {
+                params[key] = decodeURIComponent(value);
+              }
+              return params;
+            },
+
+            // 处理buttonList按钮点击
+            handleButtonClick(button) {
+              console.log("🔘 按钮点击:", button);
+
+              // 直接跳转到目标页面
+              const destPage = button.function?.dest || button.dest;
+              NavigationManager.goToFromButton(button);
+            },
+
+            // 跳转到目标页面
+            navigateToPage(button, destPage) {
+              const urlParams = new URLSearchParams(window.location.search);
+
+              // 添加按钮相关参数
+              if (button.function) {
+                urlParams.set("dest", destPage);
+                urlParams.set(
+                  "title",
+                  encodeURIComponent(button.function.desc || button.buttonName)
+                );
+                urlParams.set(
+                  "service",
+                  button.function.servName || button.service || ""
+                );
+              } else {
+                urlParams.set("dest", destPage);
+                urlParams.set("title", encodeURIComponent(button.buttonName));
+                urlParams.set("service", button.service || "");
+              }
+              const newUrl = `${destPage}.html?${urlParams.toString()}`;
+
+              console.log("� 跳转到:", newUrl);
+              window.location.href = newUrl;
+            },
+
+            // 根据fieldsList生成筛选选项
+            async generateFilterOptions() {
+              if (!this.fieldsList || this.fieldsList.length === 0) {
+                return;
+              }
+
+              for (const field of this.fieldsList) {
+                // 如果字段有cbName,生成下拉选项
+                if (field.cbName) {
+                  try {
+                    const options = await window.getDictOptions(
+                      field.cbName,
+                      this.dictCache
+                    );
+                    this.filterSelectOptions[field.name] = [
+                      { n: "", v: "" },
+                      ...options,
+                    ];
+                  } catch (error) {
+                    console.error("获取筛选选项失败:", field.cbName, error);
+                  }
+                }
+              }
+
+              console.log("🔽 生成筛选选项:", this.filterSelectOptions);
+            },
+
+            // 功能说明:统一处理卡片服务跳转,页面文件名按后端 dest 自动补 mp_,但传给业务页/后端的 dest 仍保持原始值 by xu 2026-03-06
+            openServicePage(action, item = {}) {
+              const target =
+                action && typeof action === "object" ? action : null;
+              if (!target) {
+                this.showToast("当前记录缺少操作配置", "warning");
+                return;
+              }
+
+              const serviceName = String(
+                target.servName || target.ssServ || ""
+              ).trim();
+              const rawDest = String(target.dest || "").trim();
+              const normalizedDest =
+                rawDest && rawDest.startsWith("mp_")
+                  ? rawDest
+                  : rawDest
+                  ? `mp_${rawDest}`
+                  : "";
+              const paramName = String(target.param_name || "").trim();
+              const paramValue = target.param_value;
+              const ssToken = String(target.ssToken || "").trim();
+              const paramText =
+                typeof target.parm === "string" ? target.parm : "";
+
+              if (!serviceName) {
+                this.showToast("缺少服务名", "warning");
+                return;
+              }
+
+              if (!normalizedDest) {
+                this.showToast("缺少目标页面", "warning");
+                return;
+              }
+
+              NavigationManager.goTo(normalizedDest, {
+                title: target.desc || target.title || "处理",
+                service: serviceName,
+                dest: rawDest,
+                ssDest: rawDest,
+                param: paramText,
+                playParamName: paramName,
+                playParamValue: paramValue,
+                ssToken: ssToken,
+                ssObjId: item.ssObjId || "",
+                ssObjName: item.ssObjName || "",
+                management: this.pageParams.management || "1",
+                [paramName || "param_value"]: paramValue,
+              });
+            },
+
+            // 卡片点击 - SsCard组件会自动传递item数据
+            handleCardClick(item) {
+              console.log("📄 卡片点击事件触发", item);
+              // 功能说明:卡片点击统一按 play 参数跳转到通用查看页 mp_objplay(兼容 play / service.play 两种结构) by xu 2026-03-04
+              const play =
+                (item && (item.play || (item.service && item.service.play))) ||
+                null;
+              this.openServicePage(play, item);
+            },
+
+            // 卡片操作 - SsCard组件的按钮点击事件
+            handleCardAction({ button, item, index }) {
+              console.log("⚡ 卡片操作:", button, item);
+              // 功能说明:左滑操作按钮按服务配置跳转(如 chg=变动),不再弹 Toast 占位 by xu 2026-03-06
+              this.openServicePage(button, item);
+            },
+
+            // 加载更多数据
+            async loadMore() {
+              if (!this.hasMore || this.isLoadingMore) {
+                console.log("🚫 无法加载更多:", {
+                  hasMore: this.hasMore,
+                  isLoadingMore: this.isLoadingMore,
+                });
+                return;
+              }
+
+              console.log("📄 加载更多数据...");
+              await this.loadData(this.currentPage + 1, true);
+            },
+
+            // 刷新数据
+            async refreshData() {
+              console.log("🔄 刷新数据...");
+              // 重置分页状态
+              this.currentPage = 1;
+              this.hasMore = true;
+              this.list = [];
+              this.originalList = [];
+
+              try {
+                await this.loadData(1, false);
+                this.showToast("刷新成功", "success");
+              } catch (error) {
+                this.showToast("刷新失败", "error");
+              }
+            },
+
+            // 获取字段描述
+            getFieldDesc(fieldName) {
+              const field = this.fieldsList.find((f) => f.name === fieldName);
+              return field ? field.desc : fieldName;
+            },
+
+            // 功能说明:统一按钮文案映射(兼容 rootFuncList/buttonList 不同字段) by xu 2026-02-28
+            getButtonText(button) {
+              if (!button || typeof button !== "object") return "";
+              return (
+                button.desc ||
+                button.title ||
+                button.buttonName ||
+                button.name ||
+                ""
+              );
+            },
+
+            // 功能说明:提取有效筛选参数(过滤空串/null/undefined) by xu 2026-02-28
+            getActiveFilterParams(source) {
+              const params = {};
+              const obj = source && typeof source === "object" ? source : {};
+              Object.entries(obj).forEach(([key, value]) => {
+                if (value === "" || value === null || value === undefined)
+                  return;
+                params[key] = value;
+              });
+              return params;
+            },
+
+            // 筛选选择器变化(立即搜索)
+            handleFilterChange(value) {
+              console.log("🔽 筛选条件变化,立即搜索:", value);
+              // 立即执行搜索
+              this.applyDynamicFilters();
+            },
+
+            // 应用动态筛选(重新调用API)
+            async applyDynamicFilters() {
+              try {
+                console.log("🔍 应用筛选条件:", this.selectedFilters);
+
+                // 功能说明:筛选参数先归一化,清理已取消的筛选条件 by xu 2026-02-28
+                const filterParams = this.getActiveFilterParams(
+                  this.selectedFilters
+                );
+
+                // 功能说明:筛选后重置分页并走 list 服务 by xu 2026-02-28
+                this.currentPage = 1;
+                this.hasMore = true;
+                this.list = [];
+                this.originalList = [];
+                this.selectedFilters = { ...filterParams };
+                await this.loadData(1, false);
+              } catch (error) {
+                console.error("❌ 筛选失败:", error);
+                this.showToast("筛选失败", "error");
+              }
+            },
+
+            // 生成项目按钮
+            generateItemButtons(service) {
+              if (!service || !service.play) return [];
+
+              return [
+                {
+                  title: service.play.name || "查看",
+                  icon: "icon-chakan",
+                  onclick: () => {
+                    console.log("点击查看按钮:", service.play);
+                    // 这里可以跳转到详情页面
+                    this.showToast(`查看: ${service.play.title}`, "info");
+                  },
+                },
+              ];
+            },
+
+            // 设置页面刷新监听
+            setupRefreshListener() {
+              // 监听子页面返回时的刷新通知
+              this.refreshCleanup = NavigationManager.onRefreshNotify(
+                (refreshData) => {
+                  console.log("📢 收到刷新通知,重新加载数据");
+                  this.refreshData();
+                }
+              );
+            },
+
+            // 设置滚动监听
+            setupScrollListener() {
+              let isThrottled = false;
+
+              const handleScroll = () => {
+                if (isThrottled) return;
+
+                isThrottled = true;
+                setTimeout(() => {
+                  isThrottled = false;
+                }, 200); // 节流200ms
+
+                // 检查是否滚动到底部
+                const scrollTop =
+                  window.pageYOffset || document.documentElement.scrollTop;
+                const windowHeight = window.innerHeight;
+                const documentHeight = document.documentElement.scrollHeight;
+
+                // 控制回到顶部按钮显示:滚动超过300px时显示
+                this.showBackToTop = scrollTop > 300;
+
+                // 距离底部50px时触发加载更多
+                if (scrollTop + windowHeight >= documentHeight - 50) {
+                  console.log("📄 滚动到底部,尝试加载更多...");
+                  this.loadMore();
+                }
+              };
+
+              // 添加滚动监听
+              window.addEventListener("scroll", handleScroll);
+
+              // 保存清理函数
+              this.scrollCleanup = () => {
+                window.removeEventListener("scroll", handleScroll);
+              };
+            },
+
+            // 返回顶部
+            scrollToTop() {
+              window.scrollTo({
+                top: 0,
+                behavior: "smooth",
+              });
+            },
+
+            // 处理回到顶部按钮点击
+            handleBackToTopClick() {
+              // 如果不是长按触发的搜索,则执行返回顶部
+              if (!this.isLongPress) {
+                this.scrollToTop();
+              }
+              // 重置长按标志
+              this.isLongPress = false;
+            },
+
+            // 长按开始
+            handleLongPressStart(event) {
+              if (this.hasKeyWord) {
+                // 有输入关键字才显示,否则不处理长按
+                this.isLongPress = false;
+
+                // 设置长按定时器(500ms)
+                this.longPressTimer = setTimeout(() => {
+                  this.isLongPress = true;
+                  this.openSearchModal();
+                  // 震动反馈(如果支持)
+                  if (navigator.vibrate) {
+                    navigator.vibrate(50);
+                  }
+                }, 500);
+              }
+            },
+
+            // 长按结束
+            handleLongPressEnd(event) {
+              // 清除长按定时器
+              if (this.longPressTimer) {
+                clearTimeout(this.longPressTimer);
+                this.longPressTimer = null;
+              }
+            },
+
+            // 长按取消(手指移出按钮区域)
+            handleLongPressCancel(event) {
+              // 清除长按定时器
+              if (this.longPressTimer) {
+                clearTimeout(this.longPressTimer);
+                this.longPressTimer = null;
+              }
+              this.isLongPress = false;
+            },
+
+            // 打开搜索弹窗
+            openSearchModal() {
+              this.showSearchModal = true;
+              this.searchKeyword = "";
+
+              // 延迟聚焦输入框,确保DOM已渲染和动画完成
+              this.$nextTick(() => {
+                // 使用 setTimeout 确保在移动端也能正常触发键盘
+                setTimeout(() => {
+                  if (this.$refs.searchInput) {
+                    // 先点击再聚焦,确保移动端键盘弹出
+                    this.$refs.searchInput.click();
+                    this.$refs.searchInput.focus();
+                    console.log("✅ 输入框已聚焦");
+                  }
+                }, 100); // 等待动画完成后再聚焦
+              });
+            },
+
+            // 关闭搜索弹窗
+            closeSearchModal() {
+              this.showSearchModal = false;
+              this.searchKeyword = "";
+            },
+
+            // 执行搜索
+            async performSearch() {
+              const keyword = this.searchKeyword.trim();
+
+              // 无关键词时,直接关闭弹窗,什么都不做
+              if (!keyword) {
+                // this.closeSearchModal();
+                return;
+              }
+
+              console.log("🔍 执行关键词搜索:", keyword);
+
+              // 关闭搜索弹窗
+              this.closeSearchModal();
+
+              // 设置 keyword 到筛选条件中
+              this.selectedFilters.ssKeyword = keyword;
+
+              // 调用 API 搜索
+              await this.applyDynamicFilters();
+            },
+
+            // 显示提示
+            showToast(message, type = "info") {
+              console.log(`${type.toUpperCase()}: ${message}`);
+              // 使用浏览器原生alert,后续可以替换为更好的提示组件
+              alert(message);
+            },
+          },
+        });
+      });
+    </script>
+  </body>
+</html>

+ 86 - 74
skin/mp_easy/base.css

@@ -63,7 +63,7 @@ td {
   position: relative; /* 为伪元素定位 */
 }
 
-i{
+i {
   font-style: normal;
 }
 /* ss-input 智能输入组件样式 */
@@ -85,7 +85,7 @@ i{
   line-height: 35px;
   color: #333;
   background-color: transparent;
-  border:  none;
+  border: none;
   border-radius: 4px;
   outline: none;
   box-sizing: border-box;
@@ -97,7 +97,6 @@ i{
   font-size: 16px;
 }
 
-
 .ss-input__field:invalid,
 .ss-input__field.error {
   border-color: #ff3b30;
@@ -117,7 +116,7 @@ i{
 
 /* 校验状态样式 - 作用在td上 */
 td.td-required::before {
-  content: '';
+  content: "";
   position: absolute;
   left: 0;
   top: 0;
@@ -128,7 +127,7 @@ td.td-required::before {
 }
 
 td.td-error::after {
-  content: '';
+  content: "";
   position: absolute;
   left: 0;
   right: 0;
@@ -139,7 +138,7 @@ td.td-error::after {
 }
 
 td.td-error::before {
-  content: '';
+  content: "";
   position: absolute;
   left: 0;
   top: 0;
@@ -234,8 +233,6 @@ td.td-error::before {
   transition: background-color 0.2s ease;
 }
 
-
-
 .ss-bottom__button:active {
   background-color: #585e6e;
   color: #fff;
@@ -253,10 +250,10 @@ td.td-error::before {
   align-items: center;
   justify-content: center;
   padding: 4px 12px;
-  border: 1px solid #BFC1C6;
+  border: 1px solid #bfc1c6;
   border-radius: 2px;
   background-color: #fff;
-  color: #C3C6CA;
+  color: #c3c6ca;
   font-size: 16px;
   cursor: pointer;
   transition: all 0.3s ease;
@@ -264,8 +261,8 @@ td.td-error::before {
 }
 
 .ss-onoff-button.checked {
-  color: #393D51;
-  border-color: #393D51;
+  color: #393d51;
+  border-color: #393d51;
 }
 
 /* 禁用状态 */
@@ -337,8 +334,6 @@ td.td-error::before {
   cursor: pointer;
 }
 
-
-
 .datetime-picker-value {
   flex: 1;
   font-size: 16px;
@@ -466,8 +461,6 @@ body.modal-open {
   background: #e8e9ea;
 }
 
-
-
 /* ========== SsCard 卡片组件样式 ========== */
 
 .ss-card-swipe__indicator {
@@ -527,7 +520,7 @@ body.modal-open {
 }
 
 .ss-card {
-  background: #FFFFFF;
+  background: #ffffff;
   border-radius: 4px;
   overflow: visible;
   padding: 0;
@@ -553,7 +546,7 @@ body.modal-open {
 }
 
 .ss-card--swipeable .ss-card-swipe__content {
-  background: #FFFFFF;
+  background: #ffffff;
 }
 
 /* 右上角设置按钮区域 */
@@ -583,7 +576,6 @@ body.modal-open {
   background: rgba(0, 0, 0, 0.1);
 }
 
-
 /* ========== SsSearchButton 搜索按钮组件样式 ========== */
 .ss-search-button-container {
   position: relative;
@@ -655,10 +647,10 @@ body.modal-open {
   right: 0;
   left: auto;
   width: max-content;
-  background-color: #393D51;
+  background-color: #393d51;
   z-index: 1003;
   color: #fff;
-  border: 2px solid #393D51;
+  border: 2px solid #393d51;
   box-sizing: border-box;
   border-radius: 10px;
   overflow: hidden;
@@ -694,7 +686,7 @@ body.modal-open {
 
 .option-item:active {
   background-color: #fff;
-  color: #393D51;
+  color: #393d51;
 }
 
 .option-item:active::after {
@@ -721,7 +713,7 @@ body.modal-open {
 
 /* ========== SsCard 卡片组件样式 ========== */
 .ss-card {
-  background: #FFFFFF;
+  background: #ffffff;
   border-radius: 4px;
   overflow: visible;
   padding: 0px;
@@ -793,10 +785,10 @@ body.modal-open {
   position: absolute;
   top: 100%;
   right: 0;
-  background-color: #393D51;
+  background-color: #393d51;
   z-index: 10000;
   color: #fff;
-  border: 2px solid #393D51;
+  border: 2px solid #393d51;
   box-sizing: border-box;
   border-radius: 10px;
   overflow: hidden;
@@ -830,7 +822,7 @@ body.modal-open {
 
 .menu-item:active {
   background-color: #fff;
-  color: #393D51;
+  color: #393d51;
 }
 
 .menu-item:active::after {
@@ -891,7 +883,7 @@ body.modal-open {
   display: flex;
   align-items: center;
   justify-content: space-between;
-  /* min-height: 40px; */
+  min-height: 24px;
   box-sizing: border-box;
   width: 100%;
   min-width: 0;
@@ -908,14 +900,29 @@ body.modal-open {
 
 .select-text {
   flex: 1;
+  display: flex;
+  align-items: center;
   color: #333;
   font-size: 16px;
+  line-height: 24px;
+  min-height: 24px;
   min-width: 0;
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;
 }
 
+.select-text > span {
+  display: block;
+  width: 100%;
+  min-height: 24px;
+}
+
+.select-text > input {
+  min-height: 24px;
+  line-height: 24px;
+}
+
 .select-text.placeholder {
   color: #999;
 }
@@ -941,10 +948,10 @@ body.modal-open {
   /* 功能说明:下拉层宽度跟随组件稳定宽度,不再随 placeholder/选中项来回变化 by xu 2026-03-06 */
   width: 100%;
   min-width: 100%;
-  background-color: #393D51;
+  background-color: #393d51;
   z-index: 1000;
   color: #fff;
-  border: 1px solid #393D51;
+  border: 1px solid #393d51;
   box-sizing: border-box;
   border-radius: 10px;
   overflow: hidden;
@@ -982,12 +989,12 @@ body.modal-open {
 
 .option-item:hover {
   background-color: #fff;
-  color: #393D51;
+  color: #393d51;
 }
 
 .option-item.selected {
   background-color: #fff;
-  color: #393D51;
+  color: #393d51;
 }
 
 .option-item.selected::before {
@@ -997,7 +1004,7 @@ body.modal-open {
   height: 100%;
   left: 5px;
   top: 0;
-  color: #393D51;
+  color: #393d51;
   display: flex;
   align-items: center;
 }
@@ -1009,7 +1016,7 @@ body.modal-open {
 }
 
 .option-item.no-options:hover {
-  background-color: #393D51;
+  background-color: #393d51;
   color: #999;
 }
 
@@ -1020,7 +1027,7 @@ body.modal-open {
 }
 
 .option-item.loading-item:hover {
-  background-color: #393D51;
+  background-color: #393d51;
   color: #999;
 }
 
@@ -1029,8 +1036,13 @@ body.modal-open {
 }
 
 @keyframes loading-pulse {
-  0%, 100% { opacity: 0.6; }
-  50% { opacity: 1; }
+  0%,
+  100% {
+    opacity: 0.6;
+  }
+  50% {
+    opacity: 1;
+  }
 }
 /* ========== SsUploadImage 图片上传裁剪组件样式 ========== */
 .ss-upload-image {
@@ -1384,7 +1396,7 @@ body.modal-open {
 
 /* ========== SsCarCard 车辆卡片组件样式 ========== */
 .car-card {
-  background: #FFFFFF;
+  background: #ffffff;
   border-radius: 4px;
   overflow: visible;
   padding: 17px;
@@ -1537,7 +1549,7 @@ body.modal-open {
   /* padding: 16px 0; */
 }
 
-.verify-node-container>.info {
+.verify-node-container > .info {
   padding: 0 10px;
   display: flex;
   flex-direction: row;
@@ -1546,7 +1558,7 @@ body.modal-open {
   height: 60px;
 }
 
-.verify-node-container>.info>.avatar {
+.verify-node-container > .info > .avatar {
   width: 50px;
   height: 50px;
   display: flex;
@@ -1555,7 +1567,7 @@ body.modal-open {
   align-items: center;
 }
 
-.verify-node-container>.info>.desc {
+.verify-node-container > .info > .desc {
   width: calc(100% - 60px - 60px);
   color: #333333;
   font-size: 16px;
@@ -1566,17 +1578,17 @@ body.modal-open {
   justify-content: flex-end;
 }
 
-.verify-node-container>.info>.desc>div {
+.verify-node-container > .info > .desc > div {
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;
 }
 
-.verify-node-container>.info>.link {
+.verify-node-container > .info > .link {
   width: 60px;
 }
 
-.verify-node-container>.info>.link>div {
+.verify-node-container > .info > .link > div {
   padding-top: 30px;
   display: flex;
   flex-direction: row;
@@ -1625,7 +1637,7 @@ body.modal-open {
   z-index: 1;
 }
 
-.verify-node-container .description>div {
+.verify-node-container .description > div {
   width: 100%;
   background: #fff;
   border-radius: 3px;
@@ -1635,7 +1647,7 @@ body.modal-open {
   font-size: 16px;
 }
 
-.verify-node-container .description>div::before {
+.verify-node-container .description > div::before {
   content: "";
   width: 10px;
   height: 10px;
@@ -1661,135 +1673,135 @@ body.modal-open {
 
 /* 图标相关开始 */
 /* 通用icon */
-.common-icon{
+.common-icon {
   font-size: 22px;
   font-family: "iconfont";
 }
-.common-icon-add::before{
+.common-icon-add::before {
   content: "\e60d";
   color: #999999;
 }
-.common-icon-setting::before{
+.common-icon-setting::before {
   content: "\e630";
   /* color: #999999; */
 }
 /* 减少 */
-.common-icon-reduce::before{
+.common-icon-reduce::before {
   content: "\e6a2";
-  color: #EB6100;
+  color: #eb6100;
 }
 /* 附件 */
-.common-icon-paper-clip::before{
+.common-icon-paper-clip::before {
   content: "\e676";
   color: #999999;
 }
-.common-icon-change::before{
+.common-icon-change::before {
   font-size: 16px;
   content: "→";
   color: red;
 }
 /* 表情 */
-.common-icon-smile::before{
+.common-icon-smile::before {
   content: "\e616";
   color: #999999;
 }
 /* 常用语 */
-.common-icon-common-words::before{
+.common-icon-common-words::before {
   content: "\e69d";
   color: #999999;
 }
 /* 手写 */
-.common-icon-handwriting::before{
+.common-icon-handwriting::before {
   content: "\e6ab";
   color: #999999;
 }
 /* 视频播放 */
-.common-icon-video::before{
+.common-icon-video::before {
   content: "\e699";
   color: #999999;
 }
 /* 文件夹-关闭 */
-.common-icon-folder-close::before{
+.common-icon-folder-close::before {
   content: "\e60b";
   color: #999999;
 }
 /* 文件夹-打开 */
-.common-icon-folder-open::before{
+.common-icon-folder-open::before {
   content: "\e60c";
   color: #999999;
 }
 /* 文件 */
-.common-icon-file::before{
+.common-icon-file::before {
   content: "\e69e";
   color: #999999;
 }
 /* 刷新 */
-.common-icon-refresh::before{
+.common-icon-refresh::before {
   content: "\e635";
   color: #999;
 }
 /* 待办 */
-.common-icon-todo::before{
+.common-icon-todo::before {
   content: "\e65c";
   color: #999;
 }
 /* 催办 */
-.common-icon-urge::before{
+.common-icon-urge::before {
   content: "\e664";
   color: #999;
 }
 /* 快捷发起 */
-.common-icon-quick-start::before{
+.common-icon-quick-start::before {
   content: "\e6a6";
   color: #999;
 }
 /* 公告 */
-.common-icon-notice::before{
+.common-icon-notice::before {
   content: "\e6ac";
   color: #999;
 }
 /* 统计图 */
-.common-icon-chart::before{
+.common-icon-chart::before {
   content: "\e6a5";
   color: #999;
 }
 /* 请假 */
-.common-icon-leave-big::before{
+.common-icon-leave-big::before {
   font-size: 36px;
   content: "\e6af";
   color: #999999;
 }
 /* 收付款 */
-.common-icon-receipt-big::before{
+.common-icon-receipt-big::before {
   font-size: 36px;
   content: "\e68e";
   color: #999999;
 }
 /* 考勤 */
-.common-icon-attendance-big::before{
+.common-icon-attendance-big::before {
   font-size: 36px;
   content: "\e694";
   color: #999999;
 }
 /* 会议 */
-.common-icon-meeting::before{
+.common-icon-meeting::before {
   content: "\e692";
   color: #999999;
 }
 /* 一卡通 */
-.common-icon-card::before{
+.common-icon-card::before {
   content: "\e6a0";
   color: #999999;
 }
 /* 个人网站 */
-.common-icon-site::before{
+.common-icon-site::before {
   content: "\e6ae";
   color: #999999;
 }
-.common-icon-arrow-right{
+.common-icon-arrow-right {
   font-size: 14px;
 }
-.common-icon-arrow-right::before{
+.common-icon-arrow-right::before {
   content: "\e640";
 }
 /* 通用icon结束 */
@@ -1835,7 +1847,7 @@ body.modal-open {
 }
 
 .ss-sub-tab__item.ss-sub-tab__item--active::after {
-  content: '';
+  content: "";
   position: absolute;
   bottom: 0;
   left: -5px;