common.js 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. /**
  2. * H5公共工具函数
  3. * 避免在多个文件中重复定义相同的函数
  4. */
  5. const ENTRY_SOURCE_KEY = "entrySource";
  6. const ENTRY_SOURCE_MY = "my";
  7. const AUTH_REDIRECT_DONE_KEY = "authRedirectDone";
  8. // 获取URL参数的工具函数
  9. function getUrlParams() {
  10. const params = {};
  11. const urlSearchParams = new URLSearchParams(window.location.search);
  12. for (const [key, value] of urlSearchParams) {
  13. params[key] = decodeURIComponent(value);
  14. }
  15. return params;
  16. }
  17. function isAuthPage(pathname = window.location.pathname) {
  18. return /\/(login|autoLogin)\.html$/i.test(pathname || "");
  19. }
  20. function getCurrentPageUrl() {
  21. return `${window.location.origin}${window.location.pathname}${window.location.search}`;
  22. }
  23. function getStoredAuthToken() {
  24. const userInfo = localStorage.getItem("userInfo");
  25. if (userInfo) {
  26. try {
  27. const parsed = JSON.parse(userInfo);
  28. const token = parsed?.yhsbToken || parsed?.onlineToken || "";
  29. if (token) {
  30. return token;
  31. }
  32. } catch (error) {
  33. console.warn("解析 userInfo 获取 token 失败:", error);
  34. }
  35. }
  36. return (
  37. localStorage.getItem("yhsbToken") ||
  38. localStorage.getItem("onlineToken") ||
  39. ""
  40. );
  41. }
  42. // 小程序进入 H5 时,只有 my 页面主动点击进入的场景才会带 entrySource=my。
  43. // 因此这里把“from=miniprogram 且不是认证页 且没有 entrySource=my”的页面
  44. // 统一视为消息直达等需先认证的入口,进入登录/自动登录分流。
  45. // 但如果已经带了 authRedirectDone=1,说明当前页是登录成功后回跳回来的,
  46. // 本次不应再重复进入登录分流,否则会在业务页和 autoLogin.html 之间循环跳转。
  47. function isMiniProgramMessageEntry(params = getUrlParams()) {
  48. return (
  49. params.from === "miniprogram" &&
  50. params[ENTRY_SOURCE_KEY] !== ENTRY_SOURCE_MY &&
  51. params[AUTH_REDIRECT_DONE_KEY] !== "1" &&
  52. !isAuthPage()
  53. );
  54. }
  55. // 公共层在每个业务 H5 页面初始化前都会先调用这个函数。
  56. // 一旦命中消息直达分流,就立即跳到 login.html 或 autoLogin.html,
  57. // 并把当前业务页完整地址放到 redirect 参数里,登录成功后再尝试回到这里。
  58. function handleMessageEntryAuthRedirect() {
  59. const params = getUrlParams();
  60. if (!isMiniProgramMessageEntry(params)) {
  61. return false;
  62. }
  63. const redirectUrl = getCurrentPageUrl();
  64. const token = getStoredAuthToken();
  65. const authPage = token ? "autoLogin" : "login";
  66. const authUrl = `/page/${authPage}.html?entryScene=message&redirect=${encodeURIComponent(
  67. redirectUrl
  68. )}`;
  69. console.log("🔐 检测到消息直达页面,准备跳转认证页:", {
  70. authPage,
  71. redirectUrl,
  72. hasToken: !!token,
  73. });
  74. window.location.replace(authUrl);
  75. return true;
  76. }
  77. // 获取设备信息 (优先从URL参数获取,备用从localStorage获取)
  78. function getDeviceInfo() {
  79. const urlParams = getUrlParams();
  80. // 优先从URL参数获取
  81. let deviceId = urlParams.devId || "";
  82. let model = urlParams.sbmc || "";
  83. // 如果URL参数中没有,尝试从localStorage获取
  84. if (!deviceId || !model) {
  85. const cachedDeviceInfo = localStorage.getItem("deviceInfo");
  86. if (cachedDeviceInfo) {
  87. try {
  88. const parsed = JSON.parse(cachedDeviceInfo);
  89. deviceId = deviceId || parsed.deviceId || "";
  90. model = model || parsed.model || "";
  91. console.log("🔄 从localStorage获取设备信息:", { deviceId, model });
  92. } catch (e) {
  93. console.warn("解析localStorage中的设备信息失败:", e);
  94. }
  95. }
  96. }
  97. // 如果获取到了设备信息,保存到localStorage作为备用
  98. if (deviceId && model) {
  99. const deviceInfo = { deviceId, model };
  100. localStorage.setItem("deviceInfo", JSON.stringify(deviceInfo));
  101. }
  102. return {
  103. deviceId: deviceId,
  104. model: model,
  105. };
  106. }
  107. // 获取JSESSIONID (多源获取,确保可用性)
  108. function getJSessionId() {
  109. // 优先级:localStorage > URL参数 > Cookie
  110. let jsessionId = localStorage.getItem("JSESSIONID");
  111. if (!jsessionId) {
  112. const urlParams = getUrlParams();
  113. jsessionId = urlParams.JSESSIONID;
  114. }
  115. if (!jsessionId) {
  116. // 尝试从document.cookie中获取
  117. const cookies = document.cookie.split(";");
  118. for (let cookie of cookies) {
  119. const [name, value] = cookie.trim().split("=");
  120. if (name === "JSESSIONID") {
  121. jsessionId = value;
  122. break;
  123. }
  124. }
  125. }
  126. console.log("🔍 获取JSESSIONID:", {
  127. fromLocalStorage: localStorage.getItem("JSESSIONID"),
  128. fromURL: getUrlParams().JSESSIONID,
  129. final: jsessionId,
  130. });
  131. return jsessionId || "";
  132. }
  133. // 保存JSESSIONID到localStorage
  134. function saveJSessionId(jsessionId) {
  135. if (jsessionId) {
  136. localStorage.setItem("JSESSIONID", jsessionId);
  137. console.log("✅ H5 JSESSIONID已保存:", jsessionId);
  138. }
  139. }
  140. // 模拟微信授权码获取 (H5环境下的处理)
  141. function getMockWechatCode() {
  142. // 在H5环境下,我们可以生成一个模拟的code
  143. // 实际项目中可能需要接入微信H5授权
  144. return "h5_mock_code_" + Date.now();
  145. }
  146. // 用户信息管理
  147. const userManager = {
  148. // 保存用户信息
  149. saveUserInfo(userInfo) {
  150. console.log("💾 H5保存用户信息:", userInfo);
  151. localStorage.setItem("userInfo", JSON.stringify(userInfo));
  152. },
  153. // 获取保存的用户信息
  154. getSavedUserInfo() {
  155. const userInfo = localStorage.getItem("userInfo");
  156. return userInfo ? JSON.parse(userInfo) : null;
  157. },
  158. // 检查登录状态
  159. checkLoginStatus() {
  160. const userInfo = localStorage.getItem("userInfo");
  161. const jsessionId = localStorage.getItem("JSESSIONID");
  162. console.log("🔍 H5检查登录状态:", {
  163. hasUserInfo: !!userInfo,
  164. hasJSessionId: !!jsessionId,
  165. });
  166. return {
  167. isLoggedIn: !!(userInfo && jsessionId),
  168. userInfo: userInfo ? JSON.parse(userInfo) : null,
  169. jsessionId: jsessionId,
  170. };
  171. },
  172. // 清除登录信息
  173. clearLoginInfo() {
  174. localStorage.removeItem("userInfo");
  175. localStorage.removeItem("JSESSIONID");
  176. localStorage.removeItem("deviceInfo");
  177. console.log("🧹 已清除登录信息");
  178. },
  179. };
  180. // 获取图片URL (用于图片回显)
  181. function getImageUrl(path) {
  182. if (!path) return "";
  183. // 如果已经是完整URL,直接返回
  184. if (
  185. path.startsWith("http://") ||
  186. path.startsWith("https://") ||
  187. path.startsWith("blob:")
  188. ) {
  189. return path;
  190. }
  191. // 获取baseUrl (从request中获取或使用默认值)
  192. const baseUrl = window.request?.defaults?.baseURL || window.location.origin;
  193. // 构造图片下载URL
  194. return baseUrl + "/service?ssServ=dlByHttp&type=img&path=" + path;
  195. }
  196. // 获取文件URL (用于文件下载)
  197. function getFileUrl(path) {
  198. if (!path) return "";
  199. // 如果已经是完整URL,直接返回
  200. if (path.startsWith("http://") || path.startsWith("https://")) {
  201. return path;
  202. }
  203. // 获取baseUrl
  204. const baseUrl = window.request?.defaults?.baseURL || window.location.origin;
  205. // 构造文件下载URL
  206. return baseUrl + "/service?ssServ=dlByHttp&type=file&path=" + path;
  207. }
  208. // 格式化日期时间 (使用 dayjs)
  209. function formatDate(dateStr, format = "YYYY-MM-DD HH:mm:ss") {
  210. if (!dateStr) {
  211. console.log("formatDate: 时间字符串为空");
  212. return "";
  213. }
  214. console.log("formatDate 输入:", dateStr, "格式要求:", format);
  215. // 检查 dayjs 是否可用
  216. if (typeof dayjs === "undefined") {
  217. console.error("❌ dayjs 未加载,无法格式化时间");
  218. return dateStr;
  219. }
  220. // 清理字符串:移除特殊空格字符(如 \u202F),替换为普通空格
  221. const cleanedDateStr = String(dateStr)
  222. .replace(/[\u202F\u00A0]/g, " ") // 替换不间断空格
  223. .replace(/\s+/g, " ") // 多个空格合并为一个
  224. .trim();
  225. console.log("清理后的字符串:", cleanedDateStr);
  226. // 尝试使用原生 Date 解析(兼容性最好)
  227. let date = null;
  228. try {
  229. const jsDate = new Date(cleanedDateStr);
  230. if (!isNaN(jsDate.getTime())) {
  231. date = dayjs(jsDate);
  232. console.log("✅ 使用 Date 解析成功");
  233. }
  234. } catch (e) {
  235. console.warn("Date 解析失败:", e);
  236. }
  237. // 如果 Date 解析失败,尝试直接用 dayjs
  238. if (!date || !date.isValid()) {
  239. date = dayjs(cleanedDateStr);
  240. console.log("尝试使用 dayjs 直接解析");
  241. }
  242. console.log("dayjs 解析结果 isValid:", date ? date.isValid() : "null");
  243. if (!date || !date.isValid()) {
  244. console.warn("⚠️ 无效的日期格式:", dateStr);
  245. return ""; // 解析失败返回空字符串
  246. }
  247. // dayjs 的格式化 (支持常见格式)
  248. // dayjs 使用大写的格式标记: YYYY-MM-DD HH:mm:ss
  249. const result = date.format(format);
  250. console.log("格式化结果:", result);
  251. return result;
  252. }
  253. window.ENTRY_SOURCE_KEY = ENTRY_SOURCE_KEY;
  254. window.ENTRY_SOURCE_MY = ENTRY_SOURCE_MY;
  255. window.getUrlParams = getUrlParams;
  256. window.isAuthPage = isAuthPage;
  257. window.getCurrentPageUrl = getCurrentPageUrl;
  258. window.getStoredAuthToken = getStoredAuthToken;
  259. window.isMiniProgramMessageEntry = isMiniProgramMessageEntry;
  260. window.handleMessageEntryAuthRedirect = handleMessageEntryAuthRedirect;
  261. window.getDeviceInfo = getDeviceInfo;
  262. window.getJSessionId = getJSessionId;
  263. window.saveJSessionId = saveJSessionId;
  264. window.getMockWechatCode = getMockWechatCode;
  265. window.userManager = userManager;
  266. window.getImageUrl = getImageUrl;
  267. window.getFileUrl = getFileUrl;
  268. window.formatDate = formatDate;
  269. // 同时挂载到SS.utils下(兼容组件调用)
  270. if (!window.SS.utils) {
  271. window.SS.utils = {};
  272. }
  273. window.SS.utils.getImageUrl = getImageUrl;
  274. window.SS.utils.getFileUrl = getFileUrl;
  275. window.SS.utils.formatDate = formatDate;
  276. console.log("✅ H5公共工具已加载");