index.js 12 KB

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