diff --git a/pages/archive/edit-archive.vue b/pages/archive/edit-archive.vue
index e7908fd..9cf07f3 100644
--- a/pages/archive/edit-archive.vue
+++ b/pages/archive/edit-archive.vue
@@ -194,8 +194,12 @@ async function getResponsiblePerson() {
return res && res.data ? res.data : ''
}
+function shouldBackAfterArchiveBound() {
+ return ['experienceCoupon', 'appointmentRegistration'].includes(source.value)
+}
+
function backAfterArchiveBound() {
- if (source.value === 'experienceCoupon') {
+ if (shouldBackAfterArchiveBound()) {
uni.navigateBack()
return;
}
@@ -228,8 +232,8 @@ async function init() {
const res = bindCustomerId.value ? await getExperienceCouponBindArchive() : await getArchives();
if (res.length > 0) {
visible.value = true;
- } else if (source.value === 'experienceCoupon') {
- await toast('体验券发放档案不存在或已绑定');
+ } else if (bindCustomerId.value || shouldBackAfterArchiveBound()) {
+ await toast('指定绑定档案不存在或已绑定');
uni.navigateBack();
return;
}
diff --git a/pages/experience-coupon/claim.vue b/pages/experience-coupon/claim.vue
index 4d4c539..b034eab 100644
--- a/pages/experience-coupon/claim.vue
+++ b/pages/experience-coupon/claim.vue
@@ -211,14 +211,21 @@ function openArchiveBindPage() {
uni.navigateTo({ url: buildArchiveBindUrl() });
}
+function toTimestamp(value) {
+ if (!value) return 0;
+ const numeric = Number(value);
+ if (Number.isFinite(numeric)) return numeric;
+ const parsed = new Date(value).getTime();
+ return Number.isNaN(parsed) ? 0 : parsed;
+}
+
+function getIssueExpireAt() {
+ return toTimestamp(issue.value?.activityEndTime || issue.value?.displayExpireTime);
+}
+
function isExpiredIssue() {
- const expireAt = Number(issue.value?.projectExpireTime || 0);
- if (expireAt && Date.now() > expireAt) return true;
- const rule = issue.value?.projectValidRuleSnapshot || {};
- if (rule.type === "fixedDate" && rule.fixedDate && Date.now() > Number(rule.fixedDate)) {
- return true;
- }
- return false;
+ const expireAt = getIssueExpireAt();
+ return Boolean(expireAt && Date.now() > expireAt);
}
async function acceptCoupon() {
diff --git a/pages/experience-coupon/my-coupons.vue b/pages/experience-coupon/my-coupons.vue
index 19f8092..d3f1d9e 100644
--- a/pages/experience-coupon/my-coupons.vue
+++ b/pages/experience-coupon/my-coupons.vue
@@ -9,12 +9,13 @@
>
加载中...
- 暂无待领取体验券
+ 暂无体验券
体验券
- {{ item.activityName || "体验券" }}
+
+ {{ item.activityName || "体验券" }}
+
+
+
+ {{ item.displayStatusText }}
+
+
项目
@@ -43,14 +51,20 @@
项目有效期
- {{ item.validText }}
+ {{ item.projectValidText }}
- 活动有效期
- {{ item.activityValidText }}
+ 体验券有效期
+ {{ item.couponValidText }}
+
+
+ 发送时间
+ {{ item.issueTimeText }}
- 点击查看海报领取
+
+ {{ item.actionText }}
+
@@ -102,9 +116,18 @@ const posterVisible = ref(false);
const current = ref(null);
const claiming = ref(false);
+function toTimestamp(value) {
+ if (!value) return 0;
+ const numeric = Number(value);
+ if (Number.isFinite(numeric)) return numeric;
+ const parsed = new Date(value).getTime();
+ return Number.isNaN(parsed) ? 0 : parsed;
+}
+
function formatDate(ts) {
- if (!ts) return "";
- const d = new Date(Number(ts));
+ const time = toTimestamp(ts);
+ if (!time) return "";
+ const d = new Date(time);
if (Number.isNaN(d.getTime())) return "";
const y = d.getFullYear();
const m = String(d.getMonth() + 1).padStart(2, "0");
@@ -112,15 +135,26 @@ function formatDate(ts) {
return `${y}-${m}-${day}`;
}
+function formatDateTime(ts) {
+ const time = toTimestamp(ts);
+ if (!time) return "-";
+ const d = new Date(time);
+ if (Number.isNaN(d.getTime())) return "-";
+ const h = String(d.getHours()).padStart(2, "0");
+ const min = String(d.getMinutes()).padStart(2, "0");
+ return `${formatDate(time)} ${h}:${min}`;
+}
+
function projectText(projects) {
const arr = Array.isArray(projects) ? projects : [];
if (!arr.length) return "未配置项目";
return arr.map((p) => `${p.projectName || "项目"}×${p.usageCount || 1}`).join("、");
}
-function buildValidText(item) {
+function buildProjectValidText(item) {
const rule = item.projectValidRuleSnapshot || {};
- if (item.projectExpireTime) return `有效期至 ${formatDate(item.projectExpireTime)}`;
+ const projectExpireAt = toTimestamp(item.projectExpireTime);
+ if (projectExpireAt) return `有效期至 ${formatDate(projectExpireAt)}`;
if (rule.type === "fixedDate" && rule.fixedDate) {
return `有效期至 ${formatDate(rule.fixedDate)}`;
}
@@ -130,23 +164,20 @@ function buildValidText(item) {
return "领取后按活动规则生效";
}
-function buildActivityValidText(item) {
+function getCouponExpireAt(item) {
+ return toTimestamp(item.activityEndTime || item.displayExpireTime);
+}
+
+function buildCouponValidText(item) {
const start = formatDate(item.activityStartTime) || "-";
- const end = formatDate(item.activityEndTime) || "-";
+ const expireAt = getCouponExpireAt(item);
+ const end = formatDate(expireAt) || "-";
+ if (start === "-" && end === "-") return "未配置有效期";
return `${start} 至 ${end}`;
}
-function getExpireAt(item) {
- if (item.projectExpireTime) return Number(item.projectExpireTime);
- const rule = item.projectValidRuleSnapshot || {};
- if (rule.type === "fixedDate" && rule.fixedDate) {
- return Number(rule.fixedDate);
- }
- return 0;
-}
-
function isExpired(item) {
- const expireAt = getExpireAt(item);
+ const expireAt = getCouponExpireAt(item);
if (!expireAt) return false;
return Date.now() > expireAt;
}
@@ -184,20 +215,33 @@ async function resolveContext(options = {}) {
return !!customerId.value;
}
-async function voidExpired(item) {
- try {
- await api(
- "voidExperienceCouponIssue",
- {
- corpId: corpId.value,
- issueId: item._id,
- voidReason: "有效期内未领取,自动失效",
- },
- false
- );
- } catch (e) {
- console.warn("auto void failed", e);
+function resolveDisplayStatus(item) {
+ const status = item.displayStatus || item.status || "issued";
+ if (status === "claimed") {
+ return {
+ displayStatus: "claimed",
+ displayStatusText: "已领取",
+ actionText: "已领取",
+ cardClass: "coupon-card--claimed",
+ statusClass: "coupon-status--claimed",
+ };
}
+ if (status === "expired" || status === "void" || isExpired(item)) {
+ return {
+ displayStatus: "expired",
+ displayStatusText: "已过期",
+ actionText: "已过期",
+ cardClass: "coupon-card--expired",
+ statusClass: "coupon-status--expired",
+ };
+ }
+ return {
+ displayStatus: "issued",
+ displayStatusText: "待领取",
+ actionText: "点击查看海报领取",
+ cardClass: "coupon-card--issued",
+ statusClass: "coupon-status--issued",
+ };
}
async function loadCoupons(options = {}) {
@@ -213,7 +257,6 @@ async function loadCoupons(options = {}) {
{
corpId: corpId.value,
customerId: customerId.value,
- status: "issued",
page: 1,
pageSize: 100,
},
@@ -225,20 +268,19 @@ async function loadCoupons(options = {}) {
return;
}
const raw = res.list || res.data?.list || [];
- const valid = [];
- for (const item of raw) {
- if (isExpired(item)) {
- voidExpired(item);
- continue;
- }
- valid.push({
- ...item,
- projectText: projectText(item.projectSnapshot),
- validText: buildValidText(item),
- activityValidText: buildActivityValidText(item),
- });
- }
- list.value = valid;
+ list.value = raw
+ .map((item) => {
+ const status = resolveDisplayStatus(item);
+ return {
+ ...item,
+ ...status,
+ projectText: projectText(item.projectSnapshot),
+ projectValidText: buildProjectValidText(item),
+ couponValidText: buildCouponValidText(item),
+ issueTimeText: formatDateTime(item.issueTime || item.createTime),
+ };
+ })
+ .sort((a, b) => Number(b.issueTime || b.createTime || 0) - Number(a.issueTime || a.createTime || 0));
} finally {
loading.value = false;
}
@@ -255,6 +297,10 @@ async function refreshCoupons() {
}
function openPoster(item) {
+ if (item?.displayStatus !== "issued") {
+ toast(item?.displayStatusText || "当前体验券不可领取");
+ return;
+ }
if (!item?.posterUrl) {
toast("该体验券暂无海报");
return;
@@ -338,6 +384,11 @@ onShow(async () => {
box-shadow: 0 8rpx 10rpx 0 rgba(60, 169, 145, 0.06);
}
+.coupon-card--claimed,
+.coupon-card--expired {
+ opacity: 0.78;
+}
+
.coupon-poster {
width: 160rpx;
height: 160rpx;
@@ -359,11 +410,48 @@ onShow(async () => {
flex: 1;
}
+.coupon-title-row {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 16rpx;
+ margin-bottom: 8rpx;
+}
+
+.coupon-state-row {
+ display: flex;
+ margin-bottom: 8rpx;
+}
+
.coupon-title {
+ min-width: 0;
+ flex: 1;
font-size: 30rpx;
font-weight: 600;
color: #222;
- margin-bottom: 8rpx;
+}
+
+.coupon-status {
+ flex-shrink: 0;
+ padding: 4rpx 12rpx;
+ border-radius: 999rpx;
+ font-size: 22rpx;
+ line-height: 1.4;
+}
+
+.coupon-status--issued {
+ color: #ff8a00;
+ background: rgba(255, 138, 0, 0.12);
+}
+
+.coupon-status--claimed {
+ color: #0f766e;
+ background: rgba(15, 118, 110, 0.12);
+}
+
+.coupon-status--expired {
+ color: #909399;
+ background: #f0f2f5;
}
.coupon-info-grid {
@@ -416,6 +504,10 @@ onShow(async () => {
color: #ff8a00;
}
+.coupon-action.disabled {
+ color: #a8abb2;
+}
+
.empty {
padding: 120rpx 0;
text-align: center;
diff --git a/pages/login/redirect-page.vue b/pages/login/redirect-page.vue
index cc70517..c20f9cd 100644
--- a/pages/login/redirect-page.vue
+++ b/pages/login/redirect-page.vue
@@ -143,24 +143,47 @@ async function bindTeam({ corpUserId, externalUserId }) {
}
}
+function normalizePlainOptions(options = {}) {
+ return Object.keys(options || {}).reduce((acc, key) => {
+ acc[key] = typeof options[key] === 'string' ? safeDecode(options[key]) : options[key];
+ return acc;
+ }, {})
+}
+
onLoad((options) => {
- if (options.q) {
- opts.value = JSON.stringify(options)
- changeTeam(parseInviteOptions(options));
- } else if (options.type === 'experienceCoupon' && options.issueId && options.corpId) {
+ const normalizedOptions = normalizePlainOptions(options);
+ if (normalizedOptions.q) {
+ opts.value = JSON.stringify(normalizedOptions)
+ changeTeam(parseInviteOptions(normalizedOptions));
+ } else if (normalizedOptions.type === 'experienceCoupon' && normalizedOptions.issueId && normalizedOptions.corpId) {
const params = [
- `corpId=${encodeURIComponent(options.corpId || "")}`,
- `issueId=${encodeURIComponent(options.issueId || "")}`,
- options.memberId ? `memberId=${encodeURIComponent(options.memberId)}` : "",
- options.couponId ? `couponId=${encodeURIComponent(options.couponId)}` : "",
- options.unionid ? `unionid=${encodeURIComponent(options.unionid)}` : "",
- options.externalUserId ? `externalUserId=${encodeURIComponent(options.externalUserId)}` : "",
+ `corpId=${encodeURIComponent(normalizedOptions.corpId || "")}`,
+ `issueId=${encodeURIComponent(normalizedOptions.issueId || "")}`,
+ normalizedOptions.memberId ? `memberId=${encodeURIComponent(normalizedOptions.memberId)}` : "",
+ normalizedOptions.couponId ? `couponId=${encodeURIComponent(normalizedOptions.couponId)}` : "",
+ normalizedOptions.unionid ? `unionid=${encodeURIComponent(normalizedOptions.unionid)}` : "",
+ normalizedOptions.externalUserId ? `externalUserId=${encodeURIComponent(normalizedOptions.externalUserId)}` : "",
].filter(Boolean).join("&");
uni.redirectTo({
url: `/pages/experience-coupon/claim?${params}`,
});
- } else if (options.type === 'archive' || (options.teamId && options.corpId)) {
- changeTeam(options);
+ } else if (normalizedOptions.type === 'appointmentRegistration' && normalizedOptions.customerId && normalizedOptions.corpId) {
+ const params = [
+ `corpId=${encodeURIComponent(normalizedOptions.corpId || "")}`,
+ normalizedOptions.teamId ? `teamId=${encodeURIComponent(normalizedOptions.teamId)}` : "",
+ `customerId=${encodeURIComponent(normalizedOptions.customerId || "")}`,
+ normalizedOptions.name ? `name=${encodeURIComponent(normalizedOptions.name)}` : "",
+ normalizedOptions.corpName ? `corpName=${encodeURIComponent(normalizedOptions.corpName)}` : "",
+ normalizedOptions.appointmentId ? `appointmentId=${encodeURIComponent(normalizedOptions.appointmentId)}` : "",
+ normalizedOptions.month ? `month=${encodeURIComponent(normalizedOptions.month)}` : "",
+ normalizedOptions.externalUserId ? `externalUserId=${encodeURIComponent(normalizedOptions.externalUserId)}` : "",
+ normalizedOptions.corpUserId ? `corpUserId=${encodeURIComponent(normalizedOptions.corpUserId)}` : "",
+ ].filter(Boolean).join("&");
+ uni.redirectTo({
+ url: `/pages/record/appointment-record?${params}`,
+ });
+ } else if (normalizedOptions.type === 'archive' || (normalizedOptions.teamId && normalizedOptions.corpId)) {
+ changeTeam(normalizedOptions);
}
});
diff --git a/pages/record/appointment-record.vue b/pages/record/appointment-record.vue
index 227b82a..09ec3a6 100644
--- a/pages/record/appointment-record.vue
+++ b/pages/record/appointment-record.vue
@@ -61,11 +61,19 @@
diff --git a/pages/record/treatment-record.vue b/pages/record/treatment-record.vue
index 27253a6..385aff5 100644
--- a/pages/record/treatment-record.vue
+++ b/pages/record/treatment-record.vue
@@ -47,18 +47,6 @@
{{ item.treatmentDoctorName || item.doctorName ||
staffText(item.treatmentDoctorUserId) }}
-
- 【配台】
- {{ assistantText(item) }}
-
-
- 【治疗部位】
- {{ item.treatmentArea }}
-
-
- 【治疗备注】
- {{ item.treatmentRemark }}
-