index.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. // pages/exhibition/activeDetails/index.js
  2. const { museumApi } = require('../../../utils/api.js');
  3. const WxParse = require('../../../utils/wxParse/wxParse.js');
  4. Page({
  5. /**
  6. * 页面的初始数据
  7. */
  8. data: {
  9. loading: false,
  10. detailData: null,
  11. contentItems: [],
  12. fromtype: '',
  13. isFrom: '',
  14. shouldShowBackButton: true,
  15. isPreviewMode: false,
  16. formattedPublishTime: '',
  17. isLoggedIn: false
  18. },
  19. /**
  20. * 生命周期函数--监听页面加载
  21. */
  22. onLoad(options) {
  23. // 检查登录状态
  24. this.checkLoginStatus();
  25. // 检查是否来自微信,如果是则隐藏返回按钮
  26. if (options.isfrom === 'weixin') {
  27. this.setData({
  28. shouldShowBackButton: false,
  29. isFrom: 'weixin'
  30. });
  31. }
  32. // 检查是否为预览模式
  33. if (options.preview === '1') {
  34. this.setData({
  35. isPreviewMode: true
  36. });
  37. }
  38. // 设置页面参数
  39. this.setData({
  40. fromtype: options.type || '',
  41. isFrom: options.isFrom || ''
  42. });
  43. // 加载详情数据
  44. this.loadDetailData(options);
  45. },
  46. /**
  47. * 返回上一页
  48. */
  49. goBack() {
  50. wx.navigateBack({
  51. delta: 1
  52. });
  53. },
  54. /**
  55. * 跳转到线上观展
  56. */
  57. goToOnlineExhibition() {
  58. const { detailData, isPreviewMode } = this.data;
  59. let targetUrl;
  60. // 如果是预览模式,优先使用webSiteB
  61. if (isPreviewMode && detailData.webSiteB) {
  62. targetUrl = detailData.webSiteB;
  63. } else {
  64. targetUrl = detailData.webSite;
  65. }
  66. if (targetUrl) {
  67. // 小程序中使用web-view或复制链接
  68. wx.setClipboardData({
  69. data: targetUrl,
  70. success: function() {
  71. wx.showToast({
  72. title: '链接已复制',
  73. icon: 'success'
  74. });
  75. }
  76. });
  77. }
  78. },
  79. /**
  80. * 跳转到活动预约页面
  81. */
  82. goToActivePreview() {
  83. const { detailData } = this.data;
  84. const activityId = detailData.activityId || detailData.id;
  85. wx.navigateTo({
  86. url: `/pages/index/active-page/active-page?activityId=${activityId}&type=2`
  87. });
  88. },
  89. /**
  90. * 处理链接点击
  91. */
  92. onLinkTap(e) {
  93. const { url } = e.currentTarget.dataset;
  94. if (url) {
  95. // 复制链接到剪贴板
  96. wx.setClipboardData({
  97. data: url,
  98. success: function() {
  99. wx.showToast({
  100. title: '链接已复制',
  101. icon: 'success'
  102. });
  103. }
  104. });
  105. }
  106. },
  107. /**
  108. * 处理rich-text中的链接点击
  109. */
  110. onRichTextTap(e) {
  111. const { links } = e.currentTarget.dataset;
  112. if (links && links.length > 0) {
  113. // 如果只有一个链接,直接打开
  114. if (links.length === 1) {
  115. const url = links[0].url;
  116. this.openLink(url);
  117. } else {
  118. // 如果有多个链接,显示选择列表
  119. const itemList = links.map(link => link.text);
  120. wx.showActionSheet({
  121. itemList: itemList,
  122. success: (res) => {
  123. const selectedLink = links[res.tapIndex];
  124. this.openLink(selectedLink.url);
  125. }
  126. });
  127. }
  128. }
  129. },
  130. /**
  131. * 打开链接
  132. */
  133. openLink(url) {
  134. if (url) {
  135. // 复制链接到剪贴板
  136. wx.setClipboardData({
  137. data: url,
  138. success: function() {
  139. wx.showToast({
  140. title: '链接已复制',
  141. icon: 'success'
  142. });
  143. }
  144. });
  145. }
  146. },
  147. /**
  148. * 格式化时间(横线分隔)
  149. */
  150. formatTimeWithDash(timeStr) {
  151. console.log('formatTimeWithDash:', timeStr);
  152. if (!timeStr) return '';
  153. const date = new Date(timeStr);
  154. const year = date.getFullYear();
  155. const month = String(date.getMonth() + 1).padStart(2, '0');
  156. const day = String(date.getDate()).padStart(2, '0');
  157. return `${year}-${month}-${day}`;
  158. },
  159. /**
  160. * 获取字段值(支持预览模式)
  161. */
  162. getFieldValue(fieldName) {
  163. const { detailData, isPreviewMode } = this.data;
  164. if (!detailData) return '';
  165. if (isPreviewMode) {
  166. // 预览模式下优先读取带B后缀的字段
  167. const bFieldName = fieldName + 'B';
  168. return detailData[bFieldName] || detailData[fieldName] || '';
  169. } else {
  170. return detailData[fieldName] || '';
  171. }
  172. },
  173. /**
  174. * 加载详情数据
  175. */
  176. loadDetailData(options) {
  177. const { id, type } = options;
  178. const that = this;
  179. if (!id) {
  180. console.error('缺少ID参数');
  181. return;
  182. }
  183. this.setData({
  184. loading: true
  185. });
  186. let apiPromise;
  187. // 根据类型调用不同的接口
  188. switch (type) {
  189. case 'information':
  190. apiPromise = museumApi.getInformationDetail(id);
  191. break;
  192. case 'exhibition':
  193. apiPromise = museumApi.getExhibitDetail(id);
  194. break;
  195. case 'activity':
  196. apiPromise = museumApi.getActivityDetail(id);
  197. break;
  198. case 'news':
  199. apiPromise = museumApi.getNewsDetail(id);
  200. break;
  201. case 'museum':
  202. apiPromise = museumApi.getMuseumDetail(1);
  203. break;
  204. default:
  205. console.error('未知的详情类型:', type);
  206. this.setData({ loading: false });
  207. return;
  208. }
  209. apiPromise
  210. .then(response => {
  211. if (response) {
  212. // 获取内容字段
  213. // const content = this.getContentForParsing(response);
  214. // const parsedContent = this.parseContent(content);
  215. let article = response.context;
  216. WxParse.wxParse('article', 'html', article, that, 5);
  217. // 格式化发布时间
  218. const formattedTime = this.formatTimeWithDash(response.publish);
  219. this.setData({
  220. detailData: response,
  221. // contentItems: parsedContent,
  222. formattedPublishTime: formattedTime
  223. });
  224. }
  225. })
  226. .catch(error => {
  227. console.error('获取详情数据失败:', error);
  228. this.setData({
  229. detailData: null
  230. });
  231. wx.showToast({
  232. title: '加载失败',
  233. icon: 'none'
  234. });
  235. })
  236. .finally(() => {
  237. this.setData({
  238. loading: false
  239. });
  240. });
  241. },
  242. /**
  243. * 获取用于解析的内容
  244. */
  245. getContentForParsing(detailData) {
  246. const { isPreviewMode } = this.data;
  247. if (isPreviewMode) {
  248. // 预览模式下优先使用带B后缀的字段
  249. return detailData.contextB || detailData.context || '';
  250. } else {
  251. return detailData.context || '';
  252. }
  253. },
  254. /**
  255. * 解析HTML内容为不同类型的内容项(使用map页面的解析规则)
  256. */
  257. parseContent(content) {
  258. if (!content) return [];
  259. const contentItems = [];
  260. const matches = [];
  261. // 定义所有匹配规则
  262. const patterns = [
  263. {
  264. regex: /<p[^>]*>(.*?)<\/p>/gi,
  265. type: 'text',
  266. handler: (match) => {
  267. let content = match[1];
  268. // 检查是否包含缩进样式
  269. const hasIndent = match[0].includes('text-indent:2em') || match[0].includes('text-indent: 2em');
  270. // 检查是否包含a标签链接
  271. const linkRegex = /<a[^>]*href="([^"]+)"[^>]*>(.*?)<\/a>/gi;
  272. const hasLinks = linkRegex.test(content);
  273. if (hasLinks) {
  274. // 如果包含链接,转换为rich-text节点格式
  275. let processedContent = content;
  276. const links = [];
  277. let linkMatch;
  278. linkRegex.lastIndex = 0; // 重置正则表达式
  279. // 收集所有链接信息
  280. while ((linkMatch = linkRegex.exec(content)) !== null) {
  281. links.push({
  282. url: linkMatch[1],
  283. text: linkMatch[2].replace(/<[^>]*>/g, '')
  284. });
  285. }
  286. // 将a标签转换为带样式的span标签
  287. processedContent = processedContent.replace(
  288. /<a[^>]*href="([^"]+)"[^>]*>(.*?)<\/a>/gi,
  289. '<span style="color: #007bff; text-decoration: underline;" data-url="$1">$2</span>'
  290. );
  291. return {
  292. type: 'text_with_links',
  293. content: processedContent,
  294. links: links,
  295. indent: hasIndent
  296. };
  297. } else {
  298. // 普通文本处理
  299. let text = content.replace(/<[^>]*>/g, '');
  300. // 将HTML实体编码的空格转换为对应数量的&nbsp;
  301. text = text.replace(/&nbsp;/g, '&nbsp;');
  302. text = text.replace(/&ensp;/g, '&nbsp;&nbsp;');
  303. text = text.replace(/&emsp;/g, '&nbsp;&nbsp;&nbsp;&nbsp;');
  304. text = text.replace(/&#160;/g, '&nbsp;');
  305. // 将普通空格也转换为&nbsp;
  306. text = text.replace(/ /g, '&nbsp;');
  307. // 如果有缩进样式,在文本前添加缩进
  308. if (hasIndent) {
  309. text = '&nbsp;&nbsp;&nbsp;&nbsp;' + text;
  310. }
  311. return {
  312. type: 'text',
  313. content: text,
  314. indent: hasIndent
  315. };
  316. }
  317. }
  318. },
  319. {
  320. regex: /<div[^>]*class="[^"]*media-wrap[^"]*image-wrap[^"]*"[^>]*>.*?<img[^>]*src="([^"]+)"[^>]*(?:alt="([^"]*)")?\/??>.*?<\/div>/gi,
  321. type: 'image',
  322. handler: (match) => ({
  323. type: 'image',
  324. src: match[1],
  325. alt: match[2] || '图片'
  326. })
  327. },
  328. {
  329. regex: /<div[^>]*class="[^"]*media-wrap[^"]*video-wrap[^"]*"[^>]*>.*?<video[^>]*src="([^"]+)"[^>]*(?:poster="([^"]*)")?\/??>.*?<\/div>/gi,
  330. type: 'video',
  331. handler: (match) => ({
  332. type: 'video',
  333. src: match[1],
  334. poster: match[2] || ''
  335. })
  336. },
  337. {
  338. regex: /<div[^>]*class="[^"]*media-wrap[^"]*audio-wrap[^"]*"[^>]*>.*?<audio[^>]*src="([^"]+)"[^>]*(?:title="([^"]*)")?\/??>.*?<\/div>/gi,
  339. type: 'audio',
  340. handler: (match) => ({
  341. type: 'audio',
  342. src: match[1],
  343. title: match[2] || '音频'
  344. })
  345. }
  346. ];
  347. // 收集所有匹配项及其位置
  348. patterns.forEach(pattern => {
  349. let match;
  350. pattern.regex.lastIndex = 0; // 重置正则表达式的lastIndex
  351. while ((match = pattern.regex.exec(content)) !== null) {
  352. const item = pattern.handler(match);
  353. if ((item.type === 'text' && item.content.trim()) || item.type !== 'text') {
  354. matches.push({
  355. index: match.index,
  356. item: item
  357. });
  358. }
  359. }
  360. });
  361. // 按照在原HTML中的位置排序
  362. matches.sort((a, b) => a.index - b.index);
  363. // 提取排序后的内容项
  364. return matches.map(match => match.item);
  365. },
  366. /**
  367. * 生命周期函数--监听页面初次渲染完成
  368. */
  369. onReady() {
  370. },
  371. /**
  372. * 生命周期函数--监听页面显示
  373. */
  374. onShow() {
  375. // 每次显示页面时检查登录状态
  376. this.checkLoginStatus();
  377. },
  378. /**
  379. * 检查登录状态
  380. */
  381. checkLoginStatus() {
  382. const app = getApp();
  383. const token = wx.getStorageSync('token');
  384. const isLoggedIn = !!(token && !app.globalData.isGuest);
  385. this.setData({
  386. isLoggedIn: isLoggedIn
  387. });
  388. },
  389. /**
  390. * 生命周期函数--监听页面隐藏
  391. */
  392. onHide() {
  393. },
  394. /**
  395. * 生命周期函数--监听页面卸载
  396. */
  397. onUnload() {
  398. },
  399. /**
  400. * 页面相关事件处理函数--监听用户下拉动作
  401. */
  402. onPullDownRefresh() {
  403. },
  404. /**
  405. * 页面上拉触底事件的处理函数
  406. */
  407. onReachBottom() {
  408. },
  409. /**
  410. * 用户点击右上角分享
  411. */
  412. onShareAppMessage() {
  413. }
  414. });