mp_chgChkTab.html 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta
  6. name="viewport"
  7. content="width=device-width, initial-scale=1.0, user-scalable=no"
  8. />
  9. <title>变动情况</title>
  10. <script src="/js/mp_base/base.js"></script>
  11. <style>
  12. [v-cloak] {
  13. display: none !important;
  14. }
  15. #app {
  16. background: #f5f5f5;
  17. min-height: 100vh;
  18. }
  19. .loading {
  20. min-height: 40vh;
  21. display: flex;
  22. align-items: center;
  23. justify-content: center;
  24. padding: 32px 16px;
  25. }
  26. .loading-spinner {
  27. width: 30px;
  28. height: 30px;
  29. border: 3px solid #e8f3ed;
  30. border-top-color: #40ac6d;
  31. border-radius: 50%;
  32. animation: page-spin 0.9s linear infinite;
  33. }
  34. @keyframes page-spin {
  35. from {
  36. transform: rotate(0deg);
  37. }
  38. to {
  39. transform: rotate(360deg);
  40. }
  41. }
  42. .status-wrap {
  43. padding: 16px;
  44. }
  45. .status-card {
  46. background: #fff;
  47. border-radius: 8px;
  48. padding: 16px;
  49. box-sizing: border-box;
  50. }
  51. .status-title {
  52. font-size: 15px;
  53. font-weight: 600;
  54. color: #2d3748;
  55. }
  56. .status-text {
  57. margin-top: 8px;
  58. line-height: 1.6;
  59. color: #4a5568;
  60. font-size: 13px;
  61. word-break: break-all;
  62. }
  63. .status-retry {
  64. margin-top: 12px;
  65. height: 34px;
  66. padding: 0 14px;
  67. border: none;
  68. border-radius: 4px;
  69. background: #4a5568;
  70. color: #fff;
  71. font-size: 13px;
  72. }
  73. .ss-sub-tab {
  74. min-height: 100vh;
  75. }
  76. .ss-sub-tab__content {
  77. background: #fff;
  78. }
  79. .ss-sub-tab__content iframe {
  80. width: 100%;
  81. min-height: calc(100vh - 56px);
  82. }
  83. /* 变动页签页补充 URL 调试浮层,便于直接查看初始化请求与返回 by xu 2026-03-08 */
  84. .url-log-fab {
  85. position: fixed;
  86. top: 12px;
  87. right: 12px;
  88. z-index: 999999;
  89. min-width: 56px;
  90. height: 32px;
  91. border: none;
  92. border-radius: 16px;
  93. background: #ff7a00;
  94. color: #fff;
  95. font-size: 12px;
  96. font-weight: 600;
  97. line-height: 32px;
  98. padding: 0 12px;
  99. box-shadow: 0 4px 12px rgba(255, 122, 0, 0.35);
  100. }
  101. .url-log-panel {
  102. position: fixed;
  103. left: 12px;
  104. right: 12px;
  105. bottom: 116px;
  106. z-index: 1200;
  107. background: rgba(36, 40, 53, 0.96);
  108. color: #fff;
  109. border-radius: 8px;
  110. padding: 10px;
  111. max-height: 42vh;
  112. overflow: auto;
  113. box-sizing: border-box;
  114. }
  115. .url-log-panel__head {
  116. display: flex;
  117. align-items: center;
  118. justify-content: space-between;
  119. margin-bottom: 8px;
  120. font-size: 12px;
  121. }
  122. .url-log-panel__close {
  123. border: none;
  124. background: transparent;
  125. color: #fff;
  126. font-size: 14px;
  127. line-height: 1;
  128. }
  129. .url-log-panel__actions {
  130. display: flex;
  131. flex-wrap: wrap;
  132. gap: 8px;
  133. margin-bottom: 8px;
  134. }
  135. .url-log-action {
  136. border: none;
  137. border-radius: 4px;
  138. padding: 5px 10px;
  139. font-size: 12px;
  140. color: #fff;
  141. background: rgba(255, 255, 255, 0.16);
  142. }
  143. .url-log-panel__body {
  144. margin: 0;
  145. white-space: pre-wrap;
  146. word-break: break-all;
  147. font-size: 12px;
  148. line-height: 1.5;
  149. }
  150. </style>
  151. </head>
  152. <body>
  153. <div id="app" v-cloak>
  154. <div v-if="loading" class="loading">
  155. <div class="loading-spinner"></div>
  156. </div>
  157. <div v-else-if="error" class="status-wrap">
  158. <div class="status-card">
  159. <div class="status-title">页面加载失败</div>
  160. <div class="status-text">{{ error }}</div>
  161. <button class="status-retry" type="button" @click="loadTabList">
  162. 重新加载
  163. </button>
  164. </div>
  165. </div>
  166. <div v-else-if="!tabList.length" class="status-wrap">
  167. <div class="status-card">
  168. <div class="status-title">暂无变动页签</div>
  169. <div class="status-text">当前接口未返回可展示的页签数据。</div>
  170. </div>
  171. </div>
  172. <!-- 功能说明:临时改为直接走 chkChg,方便对比 child 与非 child 返回差异 by xu 2026-03-08 -->
  173. <ss-sub-tab
  174. v-if="!loading && !error && tabList.length > 0"
  175. :tab-list="tabList"
  176. :base-params="baseParams"
  177. @tab-change="handleTabChange"
  178. >
  179. </ss-sub-tab>
  180. <!-- 功能说明:URL 浮层固定独立渲染,避免打断 v-else 链 by xu 2026-03-08 -->
  181. <button class="url-log-fab" type="button" @click="handleUrlFabClick">
  182. URL
  183. </button>
  184. <div class="url-log-panel" v-if="urlLogVisible">
  185. <div class="url-log-panel__head">
  186. <span>初始化信息</span>
  187. <div>
  188. <button class="url-log-action" type="button" @click="showCurrentUrls">
  189. 刷新信息
  190. </button>
  191. <button class="url-log-panel__close" type="button" @click="urlLogVisible = false">
  192. ×
  193. </button>
  194. </div>
  195. </div>
  196. <div class="url-log-panel__actions">
  197. <button class="url-log-action" type="button" @click="openCurrentUrl('page')">打开页面</button>
  198. <button class="url-log-action" type="button" @click="copyCurrentUrl('page')">复制页面</button>
  199. <button class="url-log-action" type="button" @click="openCurrentUrl('tab')">打开Tab</button>
  200. <button class="url-log-action" type="button" @click="copyCurrentUrl('tab')">复制Tab</button>
  201. </div>
  202. <pre class="url-log-panel__body">{{ urlLogText }}</pre>
  203. </div>
  204. </div>
  205. <script>
  206. window.SS.ready(function () {
  207. window.SS.dom.initializeFormApp({
  208. el: '#app',
  209. data() {
  210. return {
  211. pageParams: {},
  212. loading: false,
  213. error: '',
  214. tabList: [],
  215. baseParams: {},
  216. urlLogVisible: false,
  217. urlLogText: '',
  218. currentPageUrl: '',
  219. currentTabUrl: '',
  220. initRequestUrl: '',
  221. initRequestDataText: '',
  222. initResponseDataText: '',
  223. initTabListText: '',
  224. initBaseParamsText: '',
  225. initErrorText: '',
  226. };
  227. },
  228. mounted() {
  229. this.pageParams = this.getUrlParams();
  230. // 功能说明:补充 chgChkTab 初始化日志,方便直接核对和 PC 的差异 by xu 2026-03-08
  231. console.log('[mp_chgChkTab] mounted pageParams =', this.pageParams);
  232. this.loadTabList();
  233. },
  234. methods: {
  235. getUrlParams() {
  236. const params = {};
  237. const aliasMap = {
  238. ssobjname: 'ssObjName',
  239. ssobjid: 'ssObjId',
  240. datatype: 'dataType',
  241. };
  242. const urlSearchParams = new URLSearchParams(window.location.search);
  243. for (const [rawKey, rawValue] of urlSearchParams) {
  244. const decodedValue = this.safeDecode(rawValue);
  245. params[rawKey] = decodedValue;
  246. const normalizedKey = aliasMap[String(rawKey).toLowerCase()];
  247. if (normalizedKey) {
  248. params[normalizedKey] = decodedValue;
  249. }
  250. }
  251. console.log('[mp_chgChkTab] getUrlParams result =', params);
  252. return params;
  253. },
  254. safeDecode(text) {
  255. if (text === undefined || text === null || text === '') return '';
  256. try {
  257. return decodeURIComponent(String(text));
  258. } catch (_) {
  259. return String(text);
  260. }
  261. },
  262. formatDebugValue(value) {
  263. if (value === undefined) return '(undefined)';
  264. if (value === null) return '(null)';
  265. if (typeof value === 'string') return value || '(empty)';
  266. try {
  267. return JSON.stringify(value, null, 2);
  268. } catch (_) {
  269. return String(value);
  270. }
  271. },
  272. buildBaseParams() {
  273. return {
  274. sqid: this.pageParams.sqid || '',
  275. shid: this.pageParams.shid || '',
  276. shyjm: this.pageParams.shyjm || '',
  277. bdlbm: this.pageParams.bdlbm || '',
  278. dataType: 'bdplay',
  279. encode_shid: this.pageParams.encode_shid || '',
  280. ssObjName: this.pageParams.ssObjName || this.pageParams.ssobjname || '',
  281. ssObjId: this.pageParams.ssObjId || this.pageParams.ssobjid || '',
  282. jdmc: this.pageParams.jdmc || '',
  283. ssToken: this.pageParams.ssToken || '',
  284. };
  285. },
  286. pickTabList(data) {
  287. if (!data) return [];
  288. if (Array.isArray(data.tabList)) return data.tabList;
  289. if (data.data && Array.isArray(data.data.tabList)) return data.data.tabList;
  290. if (data.ssData && Array.isArray(data.ssData.tabList)) return data.ssData.tabList;
  291. return [];
  292. },
  293. async loadTabList() {
  294. this.loading = true;
  295. this.error = '';
  296. this.tabList = [];
  297. this.baseParams = this.buildBaseParams();
  298. this.initErrorText = '';
  299. try {
  300. const tagQuery = new URLSearchParams({
  301. ssServ: 'tabTag',
  302. ssDest: 'data',
  303. // 功能说明:临时去掉 child,直接看 chkChg 返回结果 by xu 2026-03-08
  304. name: 'chkChg',
  305. ssObjName: this.baseParams.ssObjName || '',
  306. ssObjId: this.baseParams.ssObjId || '',
  307. sqid: this.baseParams.sqid || '',
  308. shid: this.baseParams.shid || '',
  309. shyjm: this.baseParams.shyjm || '',
  310. bdlbm: this.baseParams.bdlbm || '',
  311. dataType: 'bdplay',
  312. encode_shid: this.baseParams.encode_shid || '',
  313. jdmc: this.baseParams.jdmc || '',
  314. ssToken: this.baseParams.ssToken || '',
  315. service: this.pageParams.service || this.pageParams.ssServ || '',
  316. dest: this.pageParams.dest || this.pageParams.ssDest || '',
  317. param: this.pageParams.param || '',
  318. });
  319. const requestUrl = `/service?${tagQuery.toString()}`;
  320. this.initRequestUrl = requestUrl;
  321. this.initRequestDataText = this.formatDebugValue({});
  322. this.initBaseParamsText = this.formatDebugValue(this.baseParams);
  323. console.log('[mp_chgChkTab] requestUrl =', requestUrl);
  324. console.log('[mp_chgChkTab] baseParams =', this.baseParams);
  325. const response = await request.post(
  326. requestUrl,
  327. {},
  328. { loading: false, formData: true }
  329. );
  330. console.log('[mp_chgChkTab] raw response =', response);
  331. const responseData = response && response.data ? response.data : response;
  332. this.initResponseDataText = this.formatDebugValue(responseData);
  333. this.tabList = this.pickTabList(responseData);
  334. this.initTabListText = this.formatDebugValue(this.tabList);
  335. console.log('[mp_chgChkTab] responseData =', responseData);
  336. console.log('[mp_chgChkTab] picked tabList =', this.tabList);
  337. if (!this.tabList.length) {
  338. console.warn('[mp_chgChkTab] tabTag 未返回 tabList =', responseData);
  339. }
  340. } catch (error) {
  341. console.error('[mp_chgChkTab] loadTabList failed =', error);
  342. this.error = (error && error.message) || '加载失败,请稍后重试';
  343. this.initErrorText = this.formatDebugValue({
  344. message: this.error,
  345. stack: error && error.stack,
  346. raw: error,
  347. });
  348. } finally {
  349. this.loading = false;
  350. console.log('[mp_chgChkTab] loading finished, error =', this.error || '(none)');
  351. }
  352. },
  353. getCurrentTabIframeSrc() {
  354. const frame = document.querySelector('.ss-sub-tab__content iframe');
  355. return (frame && frame.src) || '';
  356. },
  357. getCurrentUrlMap() {
  358. return {
  359. page: window.location.href,
  360. tab: this.getCurrentTabIframeSrc(),
  361. };
  362. },
  363. // 功能说明:右上角 URL 按钮点击后直接展开面板,并复制当前页面地址 by xu 2026-03-08
  364. async handleUrlFabClick() {
  365. this.showCurrentUrls();
  366. try {
  367. await this.copyText(this.getUrlByType('page'));
  368. if (typeof showToastEffect === 'function') showToastEffect('当前页面 URL 已复制', 1600, 'success');
  369. } catch (_) {
  370. if (typeof showToastEffect === 'function') showToastEffect('当前页面 URL 获取失败', 1800, 'error');
  371. }
  372. },
  373. // 功能说明:点 URL 时同时展示页面 URL 和初始化请求/返回,便于直接截图排查 by xu 2026-03-08
  374. showCurrentUrls() {
  375. const currentUrls = this.getCurrentUrlMap();
  376. this.currentPageUrl = currentUrls.page || '';
  377. this.currentTabUrl = currentUrls.tab || '';
  378. this.urlLogText = [
  379. `page: ${this.currentPageUrl || '(empty)'}`,
  380. `tab: ${this.currentTabUrl || '(empty)'}`,
  381. '',
  382. '[init requestUrl]',
  383. this.initRequestUrl || '(empty)',
  384. '',
  385. '[init requestData]',
  386. this.initRequestDataText || '(empty)',
  387. '',
  388. '[init responseData]',
  389. this.initResponseDataText || '(empty)',
  390. '',
  391. '[init tabList]',
  392. this.initTabListText || '(empty)',
  393. '',
  394. '[init baseParams]',
  395. this.initBaseParamsText || '(empty)',
  396. '',
  397. '[init error]',
  398. this.initErrorText || '(none)',
  399. ].join('\n');
  400. this.urlLogVisible = true;
  401. },
  402. getUrlByType(type) {
  403. const currentUrls = this.getCurrentUrlMap();
  404. return String(currentUrls[type] || '').trim();
  405. },
  406. async copyText(text) {
  407. const value = String(text || '').trim();
  408. if (!value) throw new Error('empty');
  409. if (navigator.clipboard && navigator.clipboard.writeText) {
  410. await navigator.clipboard.writeText(value);
  411. return;
  412. }
  413. const textarea = document.createElement('textarea');
  414. textarea.value = value;
  415. textarea.style.position = 'fixed';
  416. textarea.style.opacity = '0';
  417. document.body.appendChild(textarea);
  418. textarea.focus();
  419. textarea.select();
  420. document.execCommand('copy');
  421. document.body.removeChild(textarea);
  422. },
  423. async copyCurrentUrl(type) {
  424. try {
  425. await this.copyText(this.getUrlByType(type));
  426. if (typeof showToastEffect === 'function') showToastEffect('URL 已复制', 1400, 'success');
  427. } catch (_) {
  428. if (typeof showToastEffect === 'function') showToastEffect('复制失败', 1800, 'error');
  429. }
  430. },
  431. openCurrentUrl(type) {
  432. const url = this.getUrlByType(type);
  433. if (!url) {
  434. if (typeof showToastEffect === 'function') showToastEffect('当前没有可打开的 URL', 1800, 'warning');
  435. return;
  436. }
  437. const newWindow = window.open(url, '_blank');
  438. if (!newWindow) {
  439. window.location.href = url;
  440. }
  441. },
  442. handleTabChange({ index, tab }) {
  443. console.log('[mp_chgChkTab] 切换页签 =', {
  444. index,
  445. tab,
  446. baseParams: this.baseParams,
  447. });
  448. if (this.urlLogVisible) {
  449. setTimeout(() => this.showCurrentUrls(), 80);
  450. }
  451. },
  452. },
  453. });
  454. });
  455. </script>
  456. </body>
  457. </html>