mp_miniInfo.html 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  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. padding: 8px;
  19. box-sizing: border-box;
  20. }
  21. /* 新增概要信息页纯转圈加载态 by xu 2026-03-06 */
  22. .loading {
  23. min-height: 40vh;
  24. display: flex;
  25. align-items: center;
  26. justify-content: center;
  27. }
  28. .loading-spinner {
  29. width: 30px;
  30. height: 30px;
  31. border: 3px solid #e8f3ed;
  32. border-top-color: #40ac6d;
  33. border-radius: 50%;
  34. animation: page-spin 0.9s linear infinite;
  35. }
  36. @keyframes page-spin {
  37. from { transform: rotate(0deg); }
  38. to { transform: rotate(360deg); }
  39. }
  40. .error-card,
  41. .empty-card,
  42. .mini-card {
  43. background: #fff;
  44. border-radius: 10px;
  45. overflow: hidden;
  46. }
  47. .error-card,
  48. .empty-card {
  49. padding: 16px;
  50. }
  51. .mini-card {
  52. display: flex;
  53. gap: 12px;
  54. padding: 14px;
  55. margin-bottom: 10px;
  56. }
  57. .mini-thumb {
  58. width: 72px;
  59. height: 72px;
  60. border-radius: 10px;
  61. overflow: hidden;
  62. flex-shrink: 0;
  63. background: #eef0f3;
  64. }
  65. .mini-thumb img {
  66. width: 100%;
  67. height: 100%;
  68. object-fit: cover;
  69. display: block;
  70. }
  71. .mini-body {
  72. flex: 1;
  73. min-width: 0;
  74. }
  75. .mini-title {
  76. font-size: 16px;
  77. line-height: 1.5;
  78. color: #242835;
  79. font-weight: 600;
  80. word-break: break-all;
  81. }
  82. .mini-second {
  83. margin-top: 6px;
  84. font-size: 13px;
  85. line-height: 1.6;
  86. color: #6b7280;
  87. white-space: pre-wrap;
  88. word-break: break-all;
  89. }
  90. .mini-attrs {
  91. margin-top: 8px;
  92. display: flex;
  93. flex-direction: column;
  94. gap: 6px;
  95. }
  96. .mini-attr-group {
  97. display: flex;
  98. flex-wrap: wrap;
  99. gap: 6px 10px;
  100. }
  101. .mini-attr {
  102. font-size: 12px;
  103. line-height: 1.6;
  104. color: #4b5563;
  105. }
  106. .mini-attr-label {
  107. color: #9ca3af;
  108. }
  109. .mini-card--clickable {
  110. cursor: pointer;
  111. }
  112. </style>
  113. </head>
  114. <body>
  115. <div id="app" v-cloak>
  116. <div v-if="loading" class="loading">
  117. <div class="loading-spinner"></div>
  118. </div>
  119. <div v-else-if="error" class="error-card">{{ error }}</div>
  120. <div v-else-if="list.length === 0" class="empty-card">暂无概要信息</div>
  121. <!-- 新增概要信息卡片列表 by xu 2026-03-06 -->
  122. <div
  123. v-else
  124. v-for="(item, index) in list"
  125. :key="`${index}-${item.ssObjId || item.id || item.firstDisplay || ''}`"
  126. class="mini-card"
  127. :class="{ 'mini-card--clickable': !!item.__play }"
  128. @click="handleCardClick(item)"
  129. >
  130. <div class="mini-thumb" v-if="item.__thumbUrl">
  131. <img :src="item.__thumbUrl" alt="thumb" />
  132. </div>
  133. <div class="mini-body">
  134. <div class="mini-title">{{ item.firstDisplay || '未命名记录' }}</div>
  135. <div class="mini-second" v-if="item.secondDisplay">{{ item.secondDisplay }}</div>
  136. <div class="mini-attrs" v-if="item.thirdDisplay && item.thirdDisplay.length">
  137. <div v-for="(group, groupIndex) in item.thirdDisplay" :key="groupIndex" class="mini-attr-group">
  138. <div v-for="(attr, attrIndex) in group" :key="attrIndex" class="mini-attr">
  139. <span class="mini-attr-label">{{ (attr.field && (attr.field.desc || attr.field.name)) || '字段' }}:</span>
  140. <span>{{ attr.displayValue || '-' }}</span>
  141. </div>
  142. </div>
  143. </div>
  144. </div>
  145. </div>
  146. </div>
  147. <script>
  148. window.SS.ready(function () {
  149. window.SS.dom.initializeFormApp({
  150. el: '#app',
  151. data() {
  152. return {
  153. pageParams: {},
  154. loading: false,
  155. error: '',
  156. dictCache: new Map(),
  157. list: [],
  158. };
  159. },
  160. mounted() {
  161. this.pageParams = this.getUrlParams();
  162. this.loadData();
  163. },
  164. methods: {
  165. getUrlParams() {
  166. const params = {};
  167. const aliasMap = {
  168. ssobjname: 'ssObjName',
  169. ssobjid: 'ssObjId',
  170. };
  171. const urlSearchParams = new URLSearchParams(window.location.search);
  172. for (const [rawKey, rawValue] of urlSearchParams) {
  173. let decodedValue = '';
  174. try {
  175. decodedValue = decodeURIComponent(rawValue);
  176. } catch (_) {
  177. decodedValue = String(rawValue);
  178. }
  179. params[rawKey] = decodedValue;
  180. const normalizedKey = aliasMap[String(rawKey).toLowerCase()];
  181. if (normalizedKey) params[normalizedKey] = decodedValue;
  182. }
  183. return params;
  184. },
  185. parseParamObject(paramStr) {
  186. if (!paramStr) return {};
  187. if (typeof paramStr === 'object') return paramStr;
  188. try {
  189. return JSON.parse(paramStr);
  190. } catch (_) {
  191. try {
  192. return JSON.parse(
  193. String(paramStr)
  194. .replace(/([{,]\s*)([A-Za-z0-9_]+)\s*:/g, '$1"$2":')
  195. .replace(/'/g, '"')
  196. );
  197. } catch (error) {
  198. console.error('解析param失败:', error);
  199. return {};
  200. }
  201. }
  202. },
  203. async loadData() {
  204. // 加载 miniInfo 概要卡片数据 by xu 2026-03-06
  205. this.loading = true;
  206. this.error = '';
  207. try {
  208. const explicitService = String(this.pageParams.service || this.pageParams.ssServ || '').trim();
  209. const explicitDest = String(this.pageParams.dest || this.pageParams.ssDest || '').trim();
  210. const requestData = {
  211. ...this.parseParamObject(this.pageParams.param),
  212. };
  213. if (!requestData.sqid && this.pageParams.sqid) requestData.sqid = this.pageParams.sqid;
  214. if (!requestData.shid && this.pageParams.shid) requestData.shid = this.pageParams.shid;
  215. if (!requestData.ssObjName && this.pageParams.ssObjName) requestData.ssObjName = this.pageParams.ssObjName;
  216. if (!requestData.ssObjId && this.pageParams.ssObjId) requestData.ssObjId = this.pageParams.ssObjId;
  217. let response = null;
  218. if (explicitService && explicitDest) {
  219. response = await request.post(
  220. `/service?ssServ=${encodeURIComponent(explicitService)}&ssDest=${encodeURIComponent(explicitDest)}`,
  221. requestData,
  222. { loading: false, formData: true }
  223. );
  224. } else {
  225. // 新增 miniInfo dataTag 兜底请求 by xu 2026-03-06
  226. response = await request.post(
  227. `/service?ssServ=dataTag&ssDest=data&name=miniInfo&ssObjName=${encodeURIComponent(this.pageParams.ssObjName || '')}&ssObjId=${encodeURIComponent(this.pageParams.ssObjId || '')}&sqid=${encodeURIComponent(this.pageParams.sqid || '')}&shid=${encodeURIComponent(this.pageParams.shid || '')}`,
  228. requestData,
  229. { loading: false, formData: true }
  230. );
  231. }
  232. const data = response && response.data ? response.data : response;
  233. const rawList = Array.isArray(data && data.objList)
  234. ? data.objList
  235. : Array.isArray(data && data.objectList)
  236. ? data.objectList
  237. : Array.isArray(data && data.data)
  238. ? data.data
  239. : [];
  240. const formattedList = typeof window.formatObjectList === 'function'
  241. ? await window.formatObjectList(rawList, this.dictCache)
  242. : rawList;
  243. this.list = formattedList.map((item, index) => {
  244. const rawItem = rawList[index] || {};
  245. return {
  246. ...item,
  247. __thumbUrl: this.buildThumbUrl(rawItem.thn || rawItem.thumbnail || rawItem.path || ''),
  248. __play: rawItem.play || null,
  249. };
  250. });
  251. } catch (error) {
  252. console.error('加载概要信息失败:', error);
  253. this.error = (error && error.message) || '加载失败,请稍后重试';
  254. } finally {
  255. this.loading = false;
  256. }
  257. },
  258. buildThumbUrl(path) {
  259. if (!path) return '';
  260. if (typeof window.getImageUrl === 'function') {
  261. return window.getImageUrl(path);
  262. }
  263. return `/service?ssServ=dlByHttp&type=img&path=${encodeURIComponent(path)}`;
  264. },
  265. handleCardClick(item) {
  266. if (!item || !item.__play) return;
  267. const play = item.__play;
  268. const query = new URLSearchParams();
  269. if (play.service) query.set('service', play.service);
  270. if (play.dest) query.set('dest', play.dest);
  271. if (play.service) query.set('ssServ', play.service);
  272. if (play.dest) query.set('ssDest', play.dest);
  273. if (play.parm) query.set('param', typeof play.parm === 'string' ? play.parm : JSON.stringify(play.parm));
  274. if (play.title) query.set('title', play.title);
  275. const targetUrl = `/page/mp_objplay.html?${query.toString()}`;
  276. if (window.NavigationManager && typeof window.NavigationManager.goToUrl === 'function') {
  277. window.NavigationManager.goToUrl(targetUrl);
  278. return;
  279. }
  280. window.location.href = targetUrl;
  281. },
  282. },
  283. });
  284. });
  285. </script>
  286. </body>
  287. </html>