diff --git a/miniprogram/src/pages-extra/content-detail/index.config.js b/miniprogram/src/pages-extra/content-detail/index.config.js
index a4049ce..6d44e84 100644
--- a/miniprogram/src/pages-extra/content-detail/index.config.js
+++ b/miniprogram/src/pages-extra/content-detail/index.config.js
@@ -1 +1,5 @@
-export default { navigationStyle: 'custom' };
+export default {
+ navigationStyle: 'custom',
+ enableShareAppMessage: true,
+ enableShareTimeline: true,
+};
diff --git a/miniprogram/src/pages-extra/content-detail/index.jsx b/miniprogram/src/pages-extra/content-detail/index.jsx
index 9cdd186..0293492 100644
--- a/miniprogram/src/pages-extra/content-detail/index.jsx
+++ b/miniprogram/src/pages-extra/content-detail/index.jsx
@@ -1,5 +1,5 @@
import { View, Text, Image, Video, ScrollView, Input, Button, RichText } from '@tarojs/components';
-import Taro, { useShareAppMessage } from '@tarojs/taro';
+import Taro, { useShareAppMessage, useShareTimeline } from '@tarojs/taro';
import { useState, useEffect } from 'react';
import { getContent, getComments, addComment, toggleLike, reportShare } from '@/utils/content';
import { isAuthed } from '@/utils/auth';
@@ -40,16 +40,22 @@ export default function ContentDetail() {
const [replyTo, setReplyTo] = useState(null); // 被回复的评论 {id, name}(hooks 必须在提前 return 之前)
const [expanded, setExpanded] = useState({}); // 展开全部回复的评论 id
- // 微信分享:转发标题/封面/路径(配合 Button openType="share");转发落库 share_count +1
+ // 微信分享:会话(好友/群聊 5:4)+ 朋友圈(1:1),分享图 admin 端可单独配置,未配置回退封面;
+ // 转发落库 share_count +1(仅会话转发计数)
useShareAppMessage(() => {
const sid = (item && item.id) || '';
if (sid) reportShare(sid).then((r) => { if (r && r.share_count) setShareCount(r.share_count); }).catch(() => {});
return {
title: (item && item.title) || '云超服 · 资讯',
path: `/pages-extra/content-detail/index?id=${sid}`,
- imageUrl: (item && (item.cover || item.video_cover)) || '',
+ imageUrl: (item && (item.share_img_message || item.cover || item.video_cover)) || '',
};
});
+ useShareTimeline(() => ({
+ title: (item && item.title) || '云超服 · 资讯',
+ query: (item && item.id) ? `id=${item.id}` : '',
+ imageUrl: (item && (item.share_img_timeline || item.cover || item.video_cover)) || '',
+ }));
useEffect(() => {
const inst = Taro.getCurrentInstance();
diff --git a/miniprogram/src/pages-extra/event-detail/index.jsx b/miniprogram/src/pages-extra/event-detail/index.jsx
index 0071ccf..62c376a 100644
--- a/miniprogram/src/pages-extra/event-detail/index.jsx
+++ b/miniprogram/src/pages-extra/event-detail/index.jsx
@@ -1,7 +1,7 @@
import { View, Text, Input, Textarea, Picker, Button, Image, RichText } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { useState, useEffect } from 'react';
-import { getEventDetail, submitBooking, getMyBookingForEvent, fmtEvt, payBooking, bookingPayStatus, requestWxPay, priceText } from '@/utils/booking';
+import { getEventDetail, submitBooking, getMyBookingForEvent, fmtEvt, fmtEvtRange, payBooking, bookingPayStatus, requestWxPay, priceText } from '@/utils/booking';
/* rich-text 子树不吃外部 CSS:传入前对 HTML 内联约束,杜绝图片/表格横向溢出 */
const sanitizeRichHtml = (html) => String(html || '')
@@ -141,6 +141,12 @@ export default function EventDetail() {
finally { setBusy(false); }
};
+ const refreshMyBook = () => {
+ if (isAuthed() && id) {
+ getMyBookingForEvent(id).then((d) => setMyBook(d || null)).catch(() => {});
+ }
+ };
+
/* 微信一键登录:wx.login + 昵称头像 → 后端建/查 wxid 账号 */
const onWxLogin = async () => {
if (!agree) { Taro.showToast({ title: '请先阅读并同意《用户服务协议》和《隐私政策》', icon: 'none' }); return; }
@@ -152,6 +158,7 @@ export default function EventDetail() {
const d = await wxLogin({ code: loginCode, nickName, avatarUrl });
setAuthed(true);
setPhoneBound(!!d.phoneBound);
+ refreshMyBook(); // 登录成功立即刷新「我的报名」状态,已报名用户直接展示报名卡
} catch (e) { setErr((e && e.message) || '微信登录失败'); }
finally { setBusy(false); }
};
@@ -166,6 +173,7 @@ export default function EventDetail() {
const d = await wxPhoneBind(pcode);
setAuthed(true);
setPhoneBound(!!d.phoneBound);
+ refreshMyBook(); // 绑号成功刷新报名状态(已报名用户直接进入「我的报名」卡)
} catch (err) { setErr((err && err.message) || '绑定失败'); }
finally { setBusy(false); }
};
@@ -186,6 +194,7 @@ export default function EventDetail() {
}
setAuthed(true);
setPhoneBound(true);
+ refreshMyBook(); // 登录/绑号成功后同步报名状态
} catch (e) { setErr((e && e.message) || '登录失败'); }
finally { setBusy(false); }
};
@@ -366,12 +375,12 @@ export default function EventDetail() {
const manual = ev.auditMode === 'manual';
return (
-
+
{manual ? '报名已提交' : '报名成功'}
{paid && ev.priceFen > 0 && 已支付 {priceText(ev.priceFen)}}
场次:{ev.title}
- 时间:{fmtEvt(ev.startAt)}
+ 时间:{fmtEvtRange(ev.startAt, ev.endAt)}
{ev.mode === 'online' ? `线上 · ${ev.location}` : `线下 · ${ev.location}`}
预约编号:{done.id}
@@ -390,7 +399,7 @@ export default function EventDetail() {
return (
-
+
{/* 宣传图 / 头图 */}
{ev.subtitle}}
- 时间{fmtEvt(ev.startAt)}
+ 时间{fmtEvtRange(ev.startAt, ev.endAt)}
费用{priceText(ev.priceFen)}
{ev.mode === 'online' ? '入口' : '地点'}{ev.location}
{ev.host && 主办{ev.host}}
{ev.signupDeadline && (
- 报名截止
+ 报名截止
{fmtDeadline(ev.signupDeadline)}{signupClosed ? '(已截止)' : ''}
)}
@@ -428,7 +437,7 @@ export default function EventDetail() {
)}
- {ev.canManage && (
+ {(ev.canReview || ev.canManage) && (
@@ -564,16 +573,19 @@ export default function EventDetail() {
)}
{authed && phoneBound && (
- 报名本场
+ {/* 报名本场 */}
将使用你的个人资料报名({user && user.name ? user.name : '已登录账号'}
{user && user.status ? ` · ${user.status}` : ''}),可在「我的」页修改。
{/* 自定义报名表单(按活动定义渲染;必填/选填/隐藏字段均需填写) */}
{(ev.formFields || []).map((f) => renderField(f))}
- 最想解决的问题(选填)
- setQuestion(e.detail.value)} placeholder="例:有设计技能但接单不稳定,想把它做成产品" className="input" />
-
+ {/* 默认问卷「最想解决的问题」:admin 可控制是否显示(默认显示,兼容旧数据) */}
+ {ev.showDefaultQuestion !== false && (
+ 最想解决的问题(选填)
+ setQuestion(e.detail.value)} placeholder="例:有设计技能但接单不稳定,想把它做成产品" className="input" />
+
+ )}
)}
{debug && {debug}}
diff --git a/miniprogram/src/pages-extra/event-detail/index.scss b/miniprogram/src/pages-extra/event-detail/index.scss
index fd8073e..d2217ac 100644
--- a/miniprogram/src/pages-extra/event-detail/index.scss
+++ b/miniprogram/src/pages-extra/event-detail/index.scss
@@ -154,6 +154,9 @@
/* ── 报名体系升级:自定义表单 / 截止 / 隐藏信息 / 报名管理入口 ── */
.ed-v.closed { color: #f07575; }
+/* 报名截止行:标题与内容均强制单行,不换行 */
+.ed-line.deadline .ed-k { width: auto; flex-shrink: 0; white-space: nowrap; }
+.ed-line.deadline .ed-v { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ed-manage { margin-top: 20rpx; }
.manage-btn { width: 100%; font-size: 26rpx; }
diff --git a/miniprogram/src/pages-extra/my-bookings/index.jsx b/miniprogram/src/pages-extra/my-bookings/index.jsx
index 9d1da32..44246f1 100644
--- a/miniprogram/src/pages-extra/my-bookings/index.jsx
+++ b/miniprogram/src/pages-extra/my-bookings/index.jsx
@@ -2,7 +2,7 @@ import { View, Text, Button, Image } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { useState } from 'react';
import { isAuthed } from '@/utils/auth';
-import { getMyBookings, payBooking, requestWxPay, bookingPayStatus, priceText, cancelBooking, deleteBooking } from '@/utils/booking';
+import { getMyBookings, payBooking, requestWxPay, bookingPayStatus, priceText, cancelBooking, deleteBooking, fmtEvtRange } from '@/utils/booking';
import { PageHeader } from '@/components/TopInset';
import Skeleton from '@/components/skeleton';
import './index.scss';
@@ -199,7 +199,7 @@ export default function MyBookings() {
{b.eventTitle || b.want || '报名'}
{p.text}
- {b.eventStart && 场次时间:{fmtTime(b.eventStart)}}
+ {b.eventStart && 场次时间:{fmtEvtRange(b.eventStart, b.eventEnd)}}
报名时间:{fmtTime(b.createdAt)}
编号 {b.id} · {b.name || '—'}
{renderFormData(b)}
diff --git a/miniprogram/src/pages-extra/my-events/index.jsx b/miniprogram/src/pages-extra/my-events/index.jsx
index d2a5a54..7d9b08c 100644
--- a/miniprogram/src/pages-extra/my-events/index.jsx
+++ b/miniprogram/src/pages-extra/my-events/index.jsx
@@ -1,7 +1,7 @@
import { View, Text, Button } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { useState } from 'react';
-import { myEvents, managedEvents, priceText, fmtEvt } from '@/utils/booking';
+import { myEvents, managedEvents, priceText, fmtEvt, fmtEvtRange } from '@/utils/booking';
import { PageHeader } from '@/components/TopInset';
import './index.scss';
@@ -74,7 +74,7 @@ export default function MyEvents() {
{e.category || '未分类'} · {e.region || '其他'} · {e.mode === 'online' ? '线上' : e.mode === 'both' ? '线上/线下' : '线下'}
- {fmtEvt(e.startAt)}
+ {fmtEvtRange(e.startAt, e.endAt)}
{priceText(e.priceFen)}
{e.reviewStatus === 'rejected' && e.reviewComment ? 驳回原因:{e.reviewComment} : null}
diff --git a/miniprogram/src/pages/booking/index.jsx b/miniprogram/src/pages/booking/index.jsx
index 27d2d34..73d76c5 100644
--- a/miniprogram/src/pages/booking/index.jsx
+++ b/miniprogram/src/pages/booking/index.jsx
@@ -1,7 +1,7 @@
import { View, Text, Button, Image, ScrollView } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { useState, useEffect } from 'react';
-import { getBookableEvents, fmtEvt, priceText } from '@/utils/booking';
+import { getBookableEvents, fmtEvt, fmtEvtRange, priceText } from '@/utils/booking';
import { cachedRequest } from '@/utils/api';
import { useTabIndex } from '@/utils/tab';
import Skeleton from '@/components/skeleton';
@@ -78,7 +78,7 @@ export default function Booking() {
{e.title}
{e.desc}
- {fmtEvt(e.startAt)}
+ {(() => { const t = fmtEvtRange(e.startAt, e.endAt); return t.length > 22 ? fmtEvt(e.startAt) : t; })()}
{e.location}
{e.showCapacity && e.capacity > 0 && (
名额 {e.enrolled || 0} / {e.capacity}
diff --git a/miniprogram/src/utils/booking.js b/miniprogram/src/utils/booking.js
index d6e4b07..301c3b4 100644
--- a/miniprogram/src/utils/booking.js
+++ b/miniprogram/src/utils/booking.js
@@ -86,6 +86,23 @@ export function fmtEvt(iso) {
return `${d.getMonth() + 1}月${d.getDate()}日 周${week} ${p(d.getHours())}:${p(d.getMinutes())}`;
}
+/** 活动时间区间:开始-结束(同一天只显示一次日期;无结束时间则只显示开始)。 */
+export function fmtEvtRange(start, end) {
+ const s = parseIso(start);
+ if (!s) return '';
+ const e = parseIso(end);
+ const p = (n) => String(n).padStart(2, '0');
+ const week = ['日', '一', '二', '三', '四', '五', '六'][s.getDay()];
+ const dateStr = `${s.getMonth() + 1}月${s.getDate()}日 周${week}`;
+ const startStr = `${p(s.getHours())}:${p(s.getMinutes())}`;
+ if (!e) return `${dateStr} ${startStr}`;
+ if (s.getFullYear() === e.getFullYear() && s.getMonth() === e.getMonth() && s.getDate() === e.getDate()) {
+ return `${dateStr} ${startStr}-${p(e.getHours())}:${p(e.getMinutes())}`;
+ }
+ const eweek = ['日', '一', '二', '三', '四', '五', '六'][e.getDay()];
+ return `${dateStr} ${startStr} - ${e.getMonth() + 1}月${e.getDate()}日 周${eweek} ${p(e.getHours())}:${p(e.getMinutes())}`;
+}
+
/** 查当前登录用户是否已报名某活动及其审核状态(详情页判断用) */
export async function getMyBookingForEvent(eventId) {
return request('GET', `/api/bookings/by-event/${eventId}`, {}, true);
diff --git a/miniprogram/src/utils/share.js b/miniprogram/src/utils/share.js
index 4a76016..6e01366 100644
--- a/miniprogram/src/utils/share.js
+++ b/miniprogram/src/utils/share.js
@@ -3,22 +3,25 @@ import Taro, { useShareAppMessage, useShareTimeline } from '@tarojs/taro';
/**
* 活动双通道分享:对话(onShareAppMessage)+ 朋友圈(onShareTimeline)。
*
- * - 对话分享:标题 + 活动封面 + 落地页(带 id),群聊/单聊均可直达活动详情。
- * - 朋友圈分享:标题 + 活动封面 + query 带 id(需页面 config 开启 enableShareTimeline)。
+ * - 对话分享:标题 + 分享图 + 落地页(带 id),群聊/单聊均可直达活动详情。
+ * - 朋友圈分享:标题 + 分享图 + query 带 id(需页面 config 开启 enableShareTimeline)。
+ * - 分享图规范(admin 端可单独配置,未配置自动回退活动封面):
+ * 朋友圈 1:1(ev.shareImgTimeline)| 微信好友/群聊 5:4(ev.shareImgMessage)。
+ * 活动封面为 3.35:1 宽幅,直接使用会被微信按 1:1/5:4 裁剪导致展示不完整。
*
* 用法:在组件内先持有 ev 状态,再调用 useEventShare(() => ev)。
*/
export function useEventShare(getEv) {
const ev = (typeof getEv === 'function' && getEv()) || null;
useShareAppMessage(() => ({
- title: ev ? `${ev.title} · 云超服活动` : '云超服活动',
+ title: ev ? ev.title : '',
path: `/pages-extra/event-detail/index?id=${ev ? ev.id : ''}`,
- imageUrl: ev && ev.image ? ev.image : '',
+ imageUrl: ev ? (ev.shareImgMessage || ev.image || '') : '',
}));
useShareTimeline(() => ({
- title: ev ? ev.title : '云超服活动',
+ title: ev ? ev.title : '',
query: `id=${ev ? ev.id : ''}`,
- imageUrl: ev && ev.image ? ev.image : '',
+ imageUrl: ev ? (ev.shareImgTimeline || ev.image || '') : '',
}));
}