|
@@ -0,0 +1,449 @@
|
|
|
+// pages/exhibition/activeDetails/index.js
|
|
|
+const { museumApi } = require('../../../utils/api.js');
|
|
|
+
|
|
|
+Page({
|
|
|
+ /**
|
|
|
+ * 页面的初始数据
|
|
|
+ */
|
|
|
+ data: {
|
|
|
+ loading: false,
|
|
|
+ detailData: null,
|
|
|
+ contentItems: [],
|
|
|
+ fromtype: '',
|
|
|
+ isFrom: '',
|
|
|
+ shouldShowBackButton: true,
|
|
|
+ isPreviewMode: false,
|
|
|
+ formattedPublishTime: ''
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生命周期函数--监听页面加载
|
|
|
+ */
|
|
|
+ onLoad(options) {
|
|
|
+ // 检查是否来自微信,如果是则隐藏返回按钮
|
|
|
+ if (options.isfrom === 'weixin') {
|
|
|
+ this.setData({
|
|
|
+ shouldShowBackButton: false,
|
|
|
+ isFrom: 'weixin'
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否为预览模式
|
|
|
+ if (options.preview === '1') {
|
|
|
+ this.setData({
|
|
|
+ isPreviewMode: true
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置页面参数
|
|
|
+ this.setData({
|
|
|
+ fromtype: options.type || '',
|
|
|
+ isFrom: options.isFrom || ''
|
|
|
+ });
|
|
|
+
|
|
|
+ // 加载详情数据
|
|
|
+ this.loadDetailData(options);
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 返回上一页
|
|
|
+ */
|
|
|
+ goBack() {
|
|
|
+ wx.navigateBack({
|
|
|
+ delta: 1
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 跳转到线上观展
|
|
|
+ */
|
|
|
+ goToOnlineExhibition() {
|
|
|
+ const { detailData, isPreviewMode } = this.data;
|
|
|
+ let targetUrl;
|
|
|
+
|
|
|
+ // 如果是预览模式,优先使用webSiteB
|
|
|
+ if (isPreviewMode && detailData.webSiteB) {
|
|
|
+ targetUrl = detailData.webSiteB;
|
|
|
+ } else {
|
|
|
+ targetUrl = detailData.webSite;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (targetUrl) {
|
|
|
+ // 小程序中使用web-view或复制链接
|
|
|
+ wx.setClipboardData({
|
|
|
+ data: targetUrl,
|
|
|
+ success: function() {
|
|
|
+ wx.showToast({
|
|
|
+ title: '链接已复制',
|
|
|
+ icon: 'success'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 跳转到活动预约页面
|
|
|
+ */
|
|
|
+ goToActivePreview() {
|
|
|
+ const { detailData } = this.data;
|
|
|
+ const activityId = detailData.activityId || detailData.id;
|
|
|
+
|
|
|
+ wx.navigateTo({
|
|
|
+ url: `/pages/index/active-page/active-page?activityId=${activityId}&type=2`
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理链接点击
|
|
|
+ */
|
|
|
+ onLinkTap(e) {
|
|
|
+ const { url } = e.currentTarget.dataset;
|
|
|
+ if (url) {
|
|
|
+ // 复制链接到剪贴板
|
|
|
+ wx.setClipboardData({
|
|
|
+ data: url,
|
|
|
+ success: function() {
|
|
|
+ wx.showToast({
|
|
|
+ title: '链接已复制',
|
|
|
+ icon: 'success'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理rich-text中的链接点击
|
|
|
+ */
|
|
|
+ onRichTextTap(e) {
|
|
|
+ const { links } = e.currentTarget.dataset;
|
|
|
+ if (links && links.length > 0) {
|
|
|
+ // 如果只有一个链接,直接打开
|
|
|
+ if (links.length === 1) {
|
|
|
+ const url = links[0].url;
|
|
|
+ this.openLink(url);
|
|
|
+ } else {
|
|
|
+ // 如果有多个链接,显示选择列表
|
|
|
+ const itemList = links.map(link => link.text);
|
|
|
+ wx.showActionSheet({
|
|
|
+ itemList: itemList,
|
|
|
+ success: (res) => {
|
|
|
+ const selectedLink = links[res.tapIndex];
|
|
|
+ this.openLink(selectedLink.url);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 打开链接
|
|
|
+ */
|
|
|
+ openLink(url) {
|
|
|
+ if (url) {
|
|
|
+ // 复制链接到剪贴板
|
|
|
+ wx.setClipboardData({
|
|
|
+ data: url,
|
|
|
+ success: function() {
|
|
|
+ wx.showToast({
|
|
|
+ title: '链接已复制',
|
|
|
+ icon: 'success'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 格式化时间(横线分隔)
|
|
|
+ */
|
|
|
+ formatTimeWithDash(timeStr) {
|
|
|
+ console.log('formatTimeWithDash:', timeStr);
|
|
|
+ if (!timeStr) return '';
|
|
|
+ const date = new Date(timeStr);
|
|
|
+ const year = date.getFullYear();
|
|
|
+ const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
|
+ const day = String(date.getDate()).padStart(2, '0');
|
|
|
+ return `${year}-${month}-${day}`;
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取字段值(支持预览模式)
|
|
|
+ */
|
|
|
+ getFieldValue(fieldName) {
|
|
|
+ const { detailData, isPreviewMode } = this.data;
|
|
|
+ if (!detailData) return '';
|
|
|
+
|
|
|
+ if (isPreviewMode) {
|
|
|
+ // 预览模式下优先读取带B后缀的字段
|
|
|
+ const bFieldName = fieldName + 'B';
|
|
|
+ return detailData[bFieldName] || detailData[fieldName] || '';
|
|
|
+ } else {
|
|
|
+ return detailData[fieldName] || '';
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 加载详情数据
|
|
|
+ */
|
|
|
+ loadDetailData(options) {
|
|
|
+ const { id, type } = options;
|
|
|
+
|
|
|
+ if (!id) {
|
|
|
+ console.error('缺少ID参数');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.setData({
|
|
|
+ loading: true
|
|
|
+ });
|
|
|
+
|
|
|
+ let apiPromise;
|
|
|
+
|
|
|
+ // 根据类型调用不同的接口
|
|
|
+ switch (type) {
|
|
|
+ case 'information':
|
|
|
+ apiPromise = museumApi.getInformationDetail(id);
|
|
|
+ break;
|
|
|
+ case 'exhibition':
|
|
|
+ apiPromise = museumApi.getExhibitDetail(id);
|
|
|
+ break;
|
|
|
+ case 'activity':
|
|
|
+ apiPromise = museumApi.getActivityDetail(id);
|
|
|
+ break;
|
|
|
+ case 'news':
|
|
|
+ apiPromise = museumApi.getNewsDetail(id);
|
|
|
+ break;
|
|
|
+ case 'museum':
|
|
|
+ apiPromise = museumApi.getMuseumDetail(1);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ console.error('未知的详情类型:', type);
|
|
|
+ this.setData({ loading: false });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ apiPromise
|
|
|
+ .then(response => {
|
|
|
+ if (response) {
|
|
|
+ // 获取内容字段
|
|
|
+ const content = this.getContentForParsing(response);
|
|
|
+ const parsedContent = this.parseContent(content);
|
|
|
+
|
|
|
+ // 格式化发布时间
|
|
|
+ const formattedTime = this.formatTimeWithDash(response.publish);
|
|
|
+
|
|
|
+ this.setData({
|
|
|
+ detailData: response,
|
|
|
+ contentItems: parsedContent,
|
|
|
+ formattedPublishTime: formattedTime
|
|
|
+ });
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch(error => {
|
|
|
+ console.error('获取详情数据失败:', error);
|
|
|
+ this.setData({
|
|
|
+ detailData: null
|
|
|
+ });
|
|
|
+ wx.showToast({
|
|
|
+ title: '加载失败',
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ this.setData({
|
|
|
+ loading: false
|
|
|
+ });
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取用于解析的内容
|
|
|
+ */
|
|
|
+ getContentForParsing(detailData) {
|
|
|
+ const { isPreviewMode } = this.data;
|
|
|
+
|
|
|
+ if (isPreviewMode) {
|
|
|
+ // 预览模式下优先使用带B后缀的字段
|
|
|
+ return detailData.contextB || detailData.context || '';
|
|
|
+ } else {
|
|
|
+ return detailData.context || '';
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析HTML内容为不同类型的内容项(使用map页面的解析规则)
|
|
|
+ */
|
|
|
+ parseContent(content) {
|
|
|
+ if (!content) return [];
|
|
|
+
|
|
|
+ const contentItems = [];
|
|
|
+ const matches = [];
|
|
|
+
|
|
|
+ // 定义所有匹配规则
|
|
|
+ const patterns = [
|
|
|
+ {
|
|
|
+ regex: /<p[^>]*>(.*?)<\/p>/gi,
|
|
|
+ type: 'text',
|
|
|
+ handler: (match) => {
|
|
|
+ let content = match[1];
|
|
|
+ // 检查是否包含缩进样式
|
|
|
+ const hasIndent = match[0].includes('text-indent:2em') || match[0].includes('text-indent: 2em');
|
|
|
+
|
|
|
+ // 检查是否包含a标签链接
|
|
|
+ const linkRegex = /<a[^>]*href="([^"]+)"[^>]*>(.*?)<\/a>/gi;
|
|
|
+ const hasLinks = linkRegex.test(content);
|
|
|
+
|
|
|
+ if (hasLinks) {
|
|
|
+ // 如果包含链接,转换为rich-text节点格式
|
|
|
+ let processedContent = content;
|
|
|
+ const links = [];
|
|
|
+ let linkMatch;
|
|
|
+ linkRegex.lastIndex = 0; // 重置正则表达式
|
|
|
+
|
|
|
+ // 收集所有链接信息
|
|
|
+ while ((linkMatch = linkRegex.exec(content)) !== null) {
|
|
|
+ links.push({
|
|
|
+ url: linkMatch[1],
|
|
|
+ text: linkMatch[2].replace(/<[^>]*>/g, '')
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 将a标签转换为带样式的span标签
|
|
|
+ processedContent = processedContent.replace(
|
|
|
+ /<a[^>]*href="([^"]+)"[^>]*>(.*?)<\/a>/gi,
|
|
|
+ '<span style="color: #007bff; text-decoration: underline;" data-url="$1">$2</span>'
|
|
|
+ );
|
|
|
+
|
|
|
+ return {
|
|
|
+ type: 'text_with_links',
|
|
|
+ content: processedContent,
|
|
|
+ links: links,
|
|
|
+ indent: hasIndent
|
|
|
+ };
|
|
|
+ } else {
|
|
|
+ // 普通文本处理
|
|
|
+ let text = content.replace(/<[^>]*>/g, '');
|
|
|
+
|
|
|
+ // 将HTML实体编码的空格转换为对应数量的
|
|
|
+ text = text.replace(/ /g, ' ');
|
|
|
+ text = text.replace(/ /g, ' ');
|
|
|
+ text = text.replace(/ /g, ' ');
|
|
|
+ text = text.replace(/ /g, ' ');
|
|
|
+ // 将普通空格也转换为
|
|
|
+ text = text.replace(/ /g, ' ');
|
|
|
+
|
|
|
+ // 如果有缩进样式,在文本前添加缩进
|
|
|
+ if (hasIndent) {
|
|
|
+ text = ' ' + text;
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ type: 'text',
|
|
|
+ content: text,
|
|
|
+ indent: hasIndent
|
|
|
+ };
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ regex: /<div[^>]*class="[^"]*media-wrap[^"]*image-wrap[^"]*"[^>]*>.*?<img[^>]*src="([^"]+)"[^>]*(?:alt="([^"]*)")?\/??>.*?<\/div>/gi,
|
|
|
+ type: 'image',
|
|
|
+ handler: (match) => ({
|
|
|
+ type: 'image',
|
|
|
+ src: match[1],
|
|
|
+ alt: match[2] || '图片'
|
|
|
+ })
|
|
|
+ },
|
|
|
+ {
|
|
|
+ regex: /<div[^>]*class="[^"]*media-wrap[^"]*video-wrap[^"]*"[^>]*>.*?<video[^>]*src="([^"]+)"[^>]*(?:poster="([^"]*)")?\/??>.*?<\/div>/gi,
|
|
|
+ type: 'video',
|
|
|
+ handler: (match) => ({
|
|
|
+ type: 'video',
|
|
|
+ src: match[1],
|
|
|
+ poster: match[2] || ''
|
|
|
+ })
|
|
|
+ },
|
|
|
+ {
|
|
|
+ regex: /<div[^>]*class="[^"]*media-wrap[^"]*audio-wrap[^"]*"[^>]*>.*?<audio[^>]*src="([^"]+)"[^>]*(?:title="([^"]*)")?\/??>.*?<\/div>/gi,
|
|
|
+ type: 'audio',
|
|
|
+ handler: (match) => ({
|
|
|
+ type: 'audio',
|
|
|
+ src: match[1],
|
|
|
+ title: match[2] || '音频'
|
|
|
+ })
|
|
|
+ }
|
|
|
+ ];
|
|
|
+
|
|
|
+ // 收集所有匹配项及其位置
|
|
|
+ patterns.forEach(pattern => {
|
|
|
+ let match;
|
|
|
+ pattern.regex.lastIndex = 0; // 重置正则表达式的lastIndex
|
|
|
+ while ((match = pattern.regex.exec(content)) !== null) {
|
|
|
+ const item = pattern.handler(match);
|
|
|
+ if ((item.type === 'text' && item.content.trim()) || item.type !== 'text') {
|
|
|
+ matches.push({
|
|
|
+ index: match.index,
|
|
|
+ item: item
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 按照在原HTML中的位置排序
|
|
|
+ matches.sort((a, b) => a.index - b.index);
|
|
|
+
|
|
|
+ // 提取排序后的内容项
|
|
|
+ return matches.map(match => match.item);
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生命周期函数--监听页面初次渲染完成
|
|
|
+ */
|
|
|
+ onReady() {
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生命周期函数--监听页面显示
|
|
|
+ */
|
|
|
+ onShow() {
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生命周期函数--监听页面隐藏
|
|
|
+ */
|
|
|
+ onHide() {
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生命周期函数--监听页面卸载
|
|
|
+ */
|
|
|
+ onUnload() {
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 页面相关事件处理函数--监听用户下拉动作
|
|
|
+ */
|
|
|
+ onPullDownRefresh() {
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 页面上拉触底事件的处理函数
|
|
|
+ */
|
|
|
+ onReachBottom() {
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 用户点击右上角分享
|
|
|
+ */
|
|
|
+ onShareAppMessage() {
|
|
|
+
|
|
|
+ }
|
|
|
+});
|