|
|
@@ -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,165 @@ 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&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&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公共工具已加载");
|