体验券活动优化
This commit is contained in:
parent
9b5ab9fa9c
commit
f353daccaa
601
pages.json
601
pages.json
@ -1,280 +1,321 @@
|
||||
{
|
||||
"pages": [
|
||||
{
|
||||
"path": "pages/home/home",
|
||||
"style": {
|
||||
"navigationBarTitleText": "首页",
|
||||
"navigationStyle": "custom",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/message/message",
|
||||
"style": {
|
||||
"navigationBarTitleText": "消息",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/message/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "聊天",
|
||||
"enablePullDownRefresh": false,
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/mine/mine",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/mine/contact",
|
||||
"style": {
|
||||
"navigationBarTitleText": "联系客服",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/login",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康柚",
|
||||
"navigationStyle": "custom",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/redirect-page",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康柚",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/agreement",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康柚",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/common/privacy",
|
||||
"style": {
|
||||
"navigationBarTitleText": "隐私政策",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/common/agreement",
|
||||
"style": {
|
||||
"navigationBarTitleText": "用户协议",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/web-view/web-view",
|
||||
"style": {
|
||||
"navigationBarTitleText": "",
|
||||
"disableScroll": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"subPackages": [
|
||||
{
|
||||
"root": "pages/article",
|
||||
"name": "article",
|
||||
"pages": [
|
||||
{
|
||||
"path": "article-list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的宣教",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "article-cate-list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康宣教",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "article-detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "宣教文章",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "send-article",
|
||||
"style": {
|
||||
"navigationBarTitleText": "选择宣教文章",
|
||||
"disableScroll": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/survey",
|
||||
"name": "survey",
|
||||
"pages": [
|
||||
{
|
||||
"path": "survey-list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的问卷",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "fill",
|
||||
"style": {
|
||||
"navigationBarTitleText": "问卷",
|
||||
"disableScroll": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/rate",
|
||||
"name": "rate",
|
||||
"pages": [
|
||||
{
|
||||
"path": "rate-list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "服务评价",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "rate-detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "服务评价",
|
||||
"disableScroll": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/archive",
|
||||
"name": "archive",
|
||||
"pages": [
|
||||
{
|
||||
"path": "archive-manage",
|
||||
"style": {
|
||||
"navigationBarTitleText": "档案管理",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "edit-archive",
|
||||
"style": {
|
||||
"navigationBarTitleText": "新增档案",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "archive-result",
|
||||
"style": {
|
||||
"navigationBarTitleText": "团队服务",
|
||||
"disableScroll": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/health",
|
||||
"name": "health",
|
||||
"pages": [
|
||||
{
|
||||
"path": "list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康信息",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "record",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康信息",
|
||||
"disableScroll": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/library",
|
||||
"name": "library",
|
||||
"pages": [
|
||||
{
|
||||
"path": "diagnosis-list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "选择诊断",
|
||||
"disableScroll": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/team",
|
||||
"name": "team",
|
||||
"pages": [
|
||||
{
|
||||
"path": "team-detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "团队介绍",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "homepage",
|
||||
"style": {
|
||||
"navigationBarTitleText": "个人主页",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "friend",
|
||||
"style": {
|
||||
"navigationBarTitleText": "添加好友",
|
||||
"disableScroll": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
"navigationBarTextStyle": "white",
|
||||
"navigationBarTitleText": "uni-app",
|
||||
"navigationBarBackgroundColor": "#065bd6",
|
||||
"backgroundColor": "#065bd6"
|
||||
},
|
||||
"tabBar": {
|
||||
"color": "#666666",
|
||||
"selectedColor": "#007aff",
|
||||
"backgroundColor": "#ffffff",
|
||||
"borderStyle": "white",
|
||||
"list": [
|
||||
{
|
||||
"pagePath": "pages/home/home",
|
||||
"iconPath": "static/tabbar/home.png",
|
||||
"selectedIconPath": "static/tabbar/home_selected.png",
|
||||
"text": "服务"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/message/message",
|
||||
"iconPath": "static/tabbar/consult.png",
|
||||
"selectedIconPath": "static/tabbar/consult_selected.png",
|
||||
"text": "咨询"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/mine/mine",
|
||||
"iconPath": "static/tabbar/mine.png",
|
||||
"selectedIconPath": "static/tabbar/mine_selected.png",
|
||||
"text": "我的"
|
||||
}
|
||||
]
|
||||
},
|
||||
"uniIdRouter": {}
|
||||
}
|
||||
{
|
||||
"pages": [
|
||||
{
|
||||
"path": "pages/home/home",
|
||||
"style": {
|
||||
"navigationBarTitleText": "首页",
|
||||
"navigationStyle": "custom",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/message/message",
|
||||
"style": {
|
||||
"navigationBarTitleText": "消息",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/message/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "聊天",
|
||||
"enablePullDownRefresh": false,
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/mine/mine",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/mine/contact",
|
||||
"style": {
|
||||
"navigationBarTitleText": "联系客服",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/login",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康柚",
|
||||
"navigationStyle": "custom",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/redirect-page",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康柚",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/agreement",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康柚",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/common/privacy",
|
||||
"style": {
|
||||
"navigationBarTitleText": "隐私政策",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/common/agreement",
|
||||
"style": {
|
||||
"navigationBarTitleText": "用户协议",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/web-view/web-view",
|
||||
"style": {
|
||||
"navigationBarTitleText": "",
|
||||
"disableScroll": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"subPackages": [
|
||||
{
|
||||
"root": "pages/article",
|
||||
"name": "article",
|
||||
"pages": [
|
||||
{
|
||||
"path": "article-list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的宣教",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "article-cate-list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康宣教",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "article-detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "宣教文章",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "send-article",
|
||||
"style": {
|
||||
"navigationBarTitleText": "选择宣教文章",
|
||||
"disableScroll": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/survey",
|
||||
"name": "survey",
|
||||
"pages": [
|
||||
{
|
||||
"path": "survey-list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的问卷",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "fill",
|
||||
"style": {
|
||||
"navigationBarTitleText": "问卷",
|
||||
"disableScroll": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/rate",
|
||||
"name": "rate",
|
||||
"pages": [
|
||||
{
|
||||
"path": "rate-list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "服务评价",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "rate-detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "服务评价",
|
||||
"disableScroll": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/experience-coupon",
|
||||
"name": "experience-coupon",
|
||||
"pages": [
|
||||
{
|
||||
"path": "claim",
|
||||
"style": {
|
||||
"navigationBarTitleText": "领取体验券",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "my-rights",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的权益",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "select-rights-archive",
|
||||
"style": {
|
||||
"navigationBarTitleText": "选择档案",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "my-coupons",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的体验券",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "wallet",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的卡包",
|
||||
"disableScroll": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/archive",
|
||||
"name": "archive",
|
||||
"pages": [
|
||||
{
|
||||
"path": "archive-manage",
|
||||
"style": {
|
||||
"navigationBarTitleText": "档案管理",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "edit-archive",
|
||||
"style": {
|
||||
"navigationBarTitleText": "新增档案",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "archive-result",
|
||||
"style": {
|
||||
"navigationBarTitleText": "团队服务",
|
||||
"disableScroll": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/health",
|
||||
"name": "health",
|
||||
"pages": [
|
||||
{
|
||||
"path": "list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康信息",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "record",
|
||||
"style": {
|
||||
"navigationBarTitleText": "健康信息",
|
||||
"disableScroll": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/library",
|
||||
"name": "library",
|
||||
"pages": [
|
||||
{
|
||||
"path": "diagnosis-list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "选择诊断",
|
||||
"disableScroll": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/team",
|
||||
"name": "team",
|
||||
"pages": [
|
||||
{
|
||||
"path": "team-detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "团队介绍",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "homepage",
|
||||
"style": {
|
||||
"navigationBarTitleText": "个人主页",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "friend",
|
||||
"style": {
|
||||
"navigationBarTitleText": "添加好友",
|
||||
"disableScroll": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
"navigationBarTextStyle": "white",
|
||||
"navigationBarTitleText": "uni-app",
|
||||
"navigationBarBackgroundColor": "#065bd6",
|
||||
"backgroundColor": "#065bd6"
|
||||
},
|
||||
"tabBar": {
|
||||
"color": "#666666",
|
||||
"selectedColor": "#007aff",
|
||||
"backgroundColor": "#ffffff",
|
||||
"borderStyle": "white",
|
||||
"list": [
|
||||
{
|
||||
"pagePath": "pages/home/home",
|
||||
"iconPath": "static/tabbar/home.png",
|
||||
"selectedIconPath": "static/tabbar/home_selected.png",
|
||||
"text": "服务"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/message/message",
|
||||
"iconPath": "static/tabbar/consult.png",
|
||||
"selectedIconPath": "static/tabbar/consult_selected.png",
|
||||
"text": "咨询"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/mine/mine",
|
||||
"iconPath": "static/tabbar/mine.png",
|
||||
"selectedIconPath": "static/tabbar/mine_selected.png",
|
||||
"text": "我的"
|
||||
}
|
||||
]
|
||||
},
|
||||
"uniIdRouter": {}
|
||||
}
|
||||
|
||||
358
pages/experience-coupon/claim.vue
Normal file
358
pages/experience-coupon/claim.vue
Normal file
@ -0,0 +1,358 @@
|
||||
<template>
|
||||
<full-page>
|
||||
<view class="claim-page">
|
||||
<view v-if="tip" class="tip">{{ tip }}</view>
|
||||
<view v-else class="tip">加载中...</view>
|
||||
</view>
|
||||
|
||||
<!-- 海报领取弹窗 -->
|
||||
<view v-if="posterVisible" class="poster-mask">
|
||||
<view class="poster-dialog">
|
||||
<image
|
||||
class="poster-image"
|
||||
:src="issue?.posterUrl || ''"
|
||||
mode="widthFix"
|
||||
/>
|
||||
<view class="poster-info">
|
||||
<view class="poster-title">{{ issue?.activityName || "体验券" }}</view>
|
||||
<view class="poster-desc">{{ projectText }}</view>
|
||||
<view class="poster-desc">{{ validText }}</view>
|
||||
</view>
|
||||
<view class="poster-btn" :class="{ disabled: claiming }" @click="acceptCoupon">
|
||||
{{ claiming ? "领取中..." : "接受" }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</full-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { onLoad, onShow } from "@dcloudio/uni-app";
|
||||
import useAccount from "@/store/account";
|
||||
import api from "@/utils/api";
|
||||
import { confirm, hideLoading, loading, toast } from "@/utils/widget";
|
||||
import { normalizeCorpId } from "@/utils/api-base-config";
|
||||
import FullPage from "@/components/full-page.vue";
|
||||
|
||||
const { account } = storeToRefs(useAccount());
|
||||
const { login } = useAccount();
|
||||
|
||||
const corpId = ref("");
|
||||
const issueId = ref("");
|
||||
const memberId = ref("");
|
||||
const issue = ref(null);
|
||||
const tip = ref("");
|
||||
const claiming = ref(false);
|
||||
const autoPrompted = ref(false);
|
||||
const posterVisible = ref(false);
|
||||
|
||||
const projectText = computed(() => {
|
||||
const projects = Array.isArray(issue.value?.projectSnapshot) ? issue.value.projectSnapshot : [];
|
||||
if (!projects.length) return "未配置项目";
|
||||
return projects.map((p) => `${p.projectName || "项目"}×${p.usageCount || 1}`).join("、");
|
||||
});
|
||||
|
||||
const validText = computed(() => {
|
||||
const rule = issue.value?.projectValidRuleSnapshot || {};
|
||||
if (issue.value?.projectExpireTime) {
|
||||
return `有效期至 ${formatDate(issue.value.projectExpireTime)}`;
|
||||
}
|
||||
if (rule.type === "fixedDate" && rule.fixedDate) {
|
||||
return `有效期至 ${formatDate(rule.fixedDate)}`;
|
||||
}
|
||||
if (rule.type === "daysAfterClaim") {
|
||||
return `领取后 ${rule.days || 0} 天有效`;
|
||||
}
|
||||
return "有效期以活动规则为准";
|
||||
});
|
||||
|
||||
function formatDate(ts) {
|
||||
const d = new Date(Number(ts));
|
||||
if (Number.isNaN(d.getTime())) return "-";
|
||||
const y = d.getFullYear();
|
||||
const m = String(d.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(d.getDate()).padStart(2, "0");
|
||||
return `${y}-${m}-${day}`;
|
||||
}
|
||||
|
||||
function buildClaimContent() {
|
||||
const name = issue.value?.activityName || "体验券";
|
||||
return `您收到一张【${name}】体验券,包含${projectText.value},是否存入您的权益卡包?`;
|
||||
}
|
||||
|
||||
async function ensureLogin() {
|
||||
if (!account.value) await login();
|
||||
if (!account.value) {
|
||||
tip.value = "请先登录后再领取体验券";
|
||||
uni.navigateTo({ url: "/pages/login/login" });
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async function loadIssueDetail() {
|
||||
const res = await api(
|
||||
"getExperienceCouponIssueDetail",
|
||||
{ corpId: corpId.value, issueId: issueId.value },
|
||||
false
|
||||
);
|
||||
if (!res?.success || !res.data) {
|
||||
tip.value = res?.message || "体验券不存在或已失效";
|
||||
return false;
|
||||
}
|
||||
issue.value = res.data;
|
||||
if (!memberId.value && res.data.customerId) {
|
||||
memberId.value = res.data.customerId;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async function hasBoundArchive() {
|
||||
const openid = account.value?.openid || "";
|
||||
if (!openid || !corpId.value) return false;
|
||||
|
||||
if (memberId.value) {
|
||||
try {
|
||||
const detail = await api(
|
||||
"getCustomerByCustomerId",
|
||||
{ corpId: corpId.value, customerId: memberId.value },
|
||||
false
|
||||
);
|
||||
const customer = detail?.data || null;
|
||||
if (customer && String(customer.miniAppId || "") === String(openid)) {
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("getCustomerByCustomerId failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const countRes = await api(
|
||||
"getWxAppCustomerCount",
|
||||
{ miniAppId: openid, corpId: corpId.value },
|
||||
false
|
||||
);
|
||||
return Number(countRes?.data || 0) > 0;
|
||||
} catch (e) {
|
||||
console.warn("getWxAppCustomerCount failed", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function goBindArchive() {
|
||||
uni.navigateTo({
|
||||
url: `/pages/archive/archive-manage?corpId=${encodeURIComponent(corpId.value || "")}`,
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
async function acceptCoupon() {
|
||||
if (claiming.value || !issue.value) return;
|
||||
|
||||
try {
|
||||
await confirm(buildClaimContent(), {
|
||||
title: "确认接受",
|
||||
confirmText: "确认接受",
|
||||
cancelText: "暂不领取",
|
||||
});
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
claiming.value = true;
|
||||
loading("领取中...");
|
||||
try {
|
||||
const res = await api("claimExperienceCoupon", {
|
||||
corpId: corpId.value,
|
||||
issueId: issueId.value,
|
||||
claimUserId: account.value?.openid || "",
|
||||
});
|
||||
hideLoading();
|
||||
if (!res?.success) {
|
||||
tip.value = res?.message || "领取失败";
|
||||
toast(tip.value);
|
||||
return;
|
||||
}
|
||||
toast("领取成功");
|
||||
posterVisible.value = false;
|
||||
uni.redirectTo({
|
||||
url: `/pages/experience-coupon/my-rights?corpId=${encodeURIComponent(corpId.value)}&customerId=${encodeURIComponent(memberId.value || "")}&name=${encodeURIComponent(issue.value.customerName || "")}`,
|
||||
});
|
||||
} catch (e) {
|
||||
hideLoading();
|
||||
tip.value = e?.message || "领取失败";
|
||||
toast(tip.value);
|
||||
} finally {
|
||||
claiming.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function promptClaim() {
|
||||
if (autoPrompted.value) return;
|
||||
autoPrompted.value = true;
|
||||
|
||||
if (!issue.value) return;
|
||||
if (issue.value.status === "claimed") {
|
||||
tip.value = "该体验券已领取";
|
||||
uni.redirectTo({
|
||||
url: `/pages/experience-coupon/my-rights?corpId=${encodeURIComponent(corpId.value)}&customerId=${encodeURIComponent(memberId.value || "")}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (issue.value.status === "void") {
|
||||
tip.value = "该体验券已作废";
|
||||
return;
|
||||
}
|
||||
if (issue.value.status && issue.value.status !== "issued") {
|
||||
tip.value = "当前状态不可领取";
|
||||
return;
|
||||
}
|
||||
if (isExpiredIssue()) {
|
||||
tip.value = "体验券已过期,无法领取";
|
||||
return;
|
||||
}
|
||||
|
||||
const bound = await hasBoundArchive();
|
||||
if (!bound) {
|
||||
tip.value = "请先绑定档案后再领取体验券";
|
||||
try {
|
||||
await confirm("请先绑定档案后再领取体验券", {
|
||||
title: "提示",
|
||||
confirmText: "去绑定",
|
||||
cancelText: "取消",
|
||||
});
|
||||
goBindArchive();
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!issue.value.posterUrl) {
|
||||
tip.value = "该体验券暂无海报,无法领取";
|
||||
return;
|
||||
}
|
||||
|
||||
tip.value = "";
|
||||
posterVisible.value = true;
|
||||
}
|
||||
|
||||
async function bootstrap() {
|
||||
loading("加载中...");
|
||||
try {
|
||||
const okLogin = await ensureLogin();
|
||||
if (!okLogin) return;
|
||||
const okDetail = await loadIssueDetail();
|
||||
if (!okDetail) return;
|
||||
await promptClaim();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
onLoad((options = {}) => {
|
||||
corpId.value = normalizeCorpId(options.corpId || "");
|
||||
issueId.value = options.issueId || options.id || "";
|
||||
memberId.value = options.memberId || options.customerId || "";
|
||||
if (!corpId.value || !issueId.value) {
|
||||
tip.value = "领取参数不完整";
|
||||
toast(tip.value);
|
||||
return;
|
||||
}
|
||||
bootstrap();
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
if (issue.value && issue.value.status === "issued" && tip.value.includes("绑定")) {
|
||||
autoPrompted.value = false;
|
||||
promptClaim();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.claim-page {
|
||||
min-height: 100%;
|
||||
padding: 30rpx;
|
||||
box-sizing: border-box;
|
||||
background: #f6fafa;
|
||||
}
|
||||
|
||||
.tip {
|
||||
margin-top: 120rpx;
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.poster-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 1000;
|
||||
background: rgba(0, 0, 0, 0.65);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.poster-dialog {
|
||||
width: 100%;
|
||||
max-width: 620rpx;
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.poster-image {
|
||||
width: 100%;
|
||||
display: block;
|
||||
background: #f5f7fa;
|
||||
}
|
||||
|
||||
.poster-info {
|
||||
padding: 24rpx 28rpx 8rpx;
|
||||
}
|
||||
|
||||
.poster-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #222;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.poster-desc {
|
||||
font-size: 24rpx;
|
||||
color: #888;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.poster-btn {
|
||||
margin: 24rpx 28rpx 32rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
background: #065bd6;
|
||||
color: #fff;
|
||||
font-size: 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.poster-btn.disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
</style>
|
||||
387
pages/experience-coupon/my-coupons.vue
Normal file
387
pages/experience-coupon/my-coupons.vue
Normal file
@ -0,0 +1,387 @@
|
||||
<template>
|
||||
<full-page pageClass="coupons-page" :customScroll="true">
|
||||
<view class="page-body">
|
||||
<view v-if="loading" class="empty">加载中...</view>
|
||||
<view v-else-if="!list.length" class="empty">暂无待领取体验券</view>
|
||||
<view v-else class="coupon-list">
|
||||
<view
|
||||
v-for="item in list"
|
||||
:key="item._id"
|
||||
class="coupon-card"
|
||||
@click="openPoster(item)"
|
||||
>
|
||||
<image
|
||||
v-if="item.posterUrl"
|
||||
class="coupon-poster"
|
||||
:src="item.posterUrl"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view v-else class="coupon-poster coupon-poster--empty">体验券</view>
|
||||
<view class="coupon-body">
|
||||
<view class="coupon-title">{{ item.activityName || "体验券" }}</view>
|
||||
<view class="coupon-meta">{{ item.projectText }}</view>
|
||||
<view class="coupon-meta">{{ item.validText }}</view>
|
||||
<view class="coupon-action">点击查看海报领取</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 海报领取弹窗 -->
|
||||
<view v-if="posterVisible" class="poster-mask" @click="closePoster">
|
||||
<view class="poster-dialog" @click.stop>
|
||||
<image
|
||||
class="poster-image"
|
||||
:src="current?.posterUrl || ''"
|
||||
mode="widthFix"
|
||||
/>
|
||||
<view class="poster-info">
|
||||
<view class="poster-title">{{ current?.activityName || "体验券" }}</view>
|
||||
<view class="poster-desc">{{ current?.projectText }}</view>
|
||||
</view>
|
||||
<view class="poster-btn" :class="{ disabled: claiming }" @click="acceptCoupon">
|
||||
{{ claiming ? "领取中..." : "接受" }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</full-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { onLoad, onShow } from "@dcloudio/uni-app";
|
||||
import { storeToRefs } from "pinia";
|
||||
import useAccount from "@/store/account";
|
||||
import api from "@/utils/api";
|
||||
import { get } from "@/utils/cache";
|
||||
import { toast } from "@/utils/widget";
|
||||
import { normalizeCorpId } from "@/utils/api-base-config";
|
||||
import FullPage from "@/components/full-page.vue";
|
||||
|
||||
const HOME_CURRENT_TEAM_CACHE_KEY = "home-current-team-info";
|
||||
const { account } = storeToRefs(useAccount());
|
||||
const { getTeams } = useAccount();
|
||||
|
||||
const corpId = ref("");
|
||||
const teamId = ref("");
|
||||
const customerId = ref("");
|
||||
const list = ref([]);
|
||||
const loading = ref(false);
|
||||
const posterVisible = ref(false);
|
||||
const current = ref(null);
|
||||
const claiming = ref(false);
|
||||
|
||||
function formatDate(ts) {
|
||||
if (!ts) return "";
|
||||
const d = new Date(Number(ts));
|
||||
if (Number.isNaN(d.getTime())) return "";
|
||||
const y = d.getFullYear();
|
||||
const m = String(d.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(d.getDate()).padStart(2, "0");
|
||||
return `${y}-${m}-${day}`;
|
||||
}
|
||||
|
||||
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) {
|
||||
const rule = item.projectValidRuleSnapshot || {};
|
||||
if (item.projectExpireTime) return `有效期至 ${formatDate(item.projectExpireTime)}`;
|
||||
if (rule.type === "fixedDate" && rule.fixedDate) {
|
||||
return `有效期至 ${formatDate(rule.fixedDate)}`;
|
||||
}
|
||||
if (rule.type === "daysAfterClaim") {
|
||||
return `领取后 ${rule.days || 0} 天有效`;
|
||||
}
|
||||
return "领取后按活动规则生效";
|
||||
}
|
||||
|
||||
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);
|
||||
if (!expireAt) return false;
|
||||
return Date.now() > expireAt;
|
||||
}
|
||||
|
||||
async function resolveContext(options = {}) {
|
||||
corpId.value = normalizeCorpId(options.corpId || corpId.value || "");
|
||||
teamId.value = options.teamId || teamId.value || "";
|
||||
customerId.value = options.customerId || options.memberId || customerId.value || "";
|
||||
|
||||
const cached = get(HOME_CURRENT_TEAM_CACHE_KEY) || {};
|
||||
let teams = [];
|
||||
try {
|
||||
teams = (await getTeams()) || [];
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
}
|
||||
const matched =
|
||||
teams.find((t) => t.teamId === (teamId.value || cached.teamId)) ||
|
||||
teams.find((t) => normalizeCorpId(t.corpId) === corpId.value) ||
|
||||
teams[0] ||
|
||||
null;
|
||||
|
||||
if (!corpId.value) corpId.value = normalizeCorpId(matched?.corpId || cached.corpId || "");
|
||||
if (!teamId.value) teamId.value = matched?.teamId || cached.teamId || "";
|
||||
if (!corpId.value) return false;
|
||||
|
||||
if (customerId.value) return true;
|
||||
|
||||
const miniAppId = account.value?.openid || uni.getStorageSync("openid") || "";
|
||||
if (!miniAppId) return false;
|
||||
const res = await api("getMiniAppCustomers", { miniAppId, corpId: corpId.value }, false);
|
||||
const customers = res?.success && Array.isArray(res.data) ? res.data : [];
|
||||
const preferred = customers.find((c) => c.relationship === "本人") || customers[0];
|
||||
customerId.value = preferred?._id || "";
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadCoupons() {
|
||||
if (!corpId.value || !customerId.value) {
|
||||
list.value = [];
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await api(
|
||||
"getCustomerExperienceCoupons",
|
||||
{
|
||||
corpId: corpId.value,
|
||||
customerId: customerId.value,
|
||||
status: "issued",
|
||||
page: 1,
|
||||
pageSize: 100,
|
||||
},
|
||||
false
|
||||
);
|
||||
if (!res?.success) {
|
||||
toast(res?.message || "加载体验券失败");
|
||||
list.value = [];
|
||||
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),
|
||||
});
|
||||
}
|
||||
list.value = valid;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function openPoster(item) {
|
||||
if (!item?.posterUrl) {
|
||||
toast("该体验券暂无海报");
|
||||
return;
|
||||
}
|
||||
current.value = item;
|
||||
posterVisible.value = true;
|
||||
}
|
||||
|
||||
function closePoster() {
|
||||
if (claiming.value) return;
|
||||
posterVisible.value = false;
|
||||
current.value = null;
|
||||
}
|
||||
|
||||
async function acceptCoupon() {
|
||||
if (!current.value || claiming.value) return;
|
||||
claiming.value = true;
|
||||
try {
|
||||
const res = await api("claimExperienceCoupon", {
|
||||
corpId: corpId.value,
|
||||
issueId: current.value._id,
|
||||
claimUserId: account.value?.openid || "",
|
||||
});
|
||||
if (!res?.success) {
|
||||
toast(res?.message || "领取失败");
|
||||
return;
|
||||
}
|
||||
toast("领取成功");
|
||||
posterVisible.value = false;
|
||||
uni.redirectTo({
|
||||
url: `/pages/experience-coupon/my-rights?corpId=${encodeURIComponent(corpId.value)}&teamId=${encodeURIComponent(teamId.value)}&customerId=${encodeURIComponent(customerId.value)}&name=${encodeURIComponent(current.value.customerName || "")}`,
|
||||
});
|
||||
} catch (e) {
|
||||
toast(e?.message || "领取失败");
|
||||
} finally {
|
||||
claiming.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onLoad(async (options = {}) => {
|
||||
uni.setNavigationBarTitle({ title: "我的体验券" });
|
||||
const ok = await resolveContext(options);
|
||||
if (!ok) {
|
||||
toast("请先绑定档案");
|
||||
return;
|
||||
}
|
||||
await loadCoupons();
|
||||
});
|
||||
|
||||
onShow(async () => {
|
||||
if (corpId.value && customerId.value) {
|
||||
await loadCoupons();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-body {
|
||||
min-height: 100%;
|
||||
padding: 24rpx 30rpx 40rpx;
|
||||
box-sizing: border-box;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.coupon-card {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 8rpx 10rpx 0 rgba(60, 169, 145, 0.06);
|
||||
}
|
||||
|
||||
.coupon-poster {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 12rpx;
|
||||
background: #f5f7fa;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.coupon-poster--empty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #c0c4cc;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.coupon-body {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.coupon-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #222;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.coupon-meta {
|
||||
font-size: 24rpx;
|
||||
color: #888;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.coupon-action {
|
||||
margin-top: 12rpx;
|
||||
font-size: 24rpx;
|
||||
color: #ff8a00;
|
||||
}
|
||||
|
||||
.empty {
|
||||
padding: 120rpx 0;
|
||||
text-align: center;
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.poster-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 1000;
|
||||
background: rgba(0, 0, 0, 0.65);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.poster-dialog {
|
||||
width: 100%;
|
||||
max-width: 620rpx;
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.poster-image {
|
||||
width: 100%;
|
||||
display: block;
|
||||
background: #f5f7fa;
|
||||
}
|
||||
|
||||
.poster-info {
|
||||
padding: 24rpx 28rpx 8rpx;
|
||||
}
|
||||
|
||||
.poster-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #222;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.poster-desc {
|
||||
font-size: 24rpx;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.poster-btn {
|
||||
margin: 24rpx 28rpx 32rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
background: #065bd6;
|
||||
color: #fff;
|
||||
font-size: 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.poster-btn.disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
</style>
|
||||
412
pages/experience-coupon/my-rights.vue
Normal file
412
pages/experience-coupon/my-rights.vue
Normal file
@ -0,0 +1,412 @@
|
||||
<template>
|
||||
<full-page pageClass="rights-page" :customScroll="true">
|
||||
<view class="page-body">
|
||||
<view class="user-card" @click="toSelectPage">
|
||||
<view class="avatar">
|
||||
<uni-icons type="person-filled" size="36" color="#c0c4cc" />
|
||||
</view>
|
||||
<view class="user-main">
|
||||
<view class="user-name">{{ customerName || "未选择档案" }}</view>
|
||||
<view class="user-corp">所属机构:{{ corpName || "-" }}</view>
|
||||
</view>
|
||||
<uni-icons type="right" size="16" color="#c0c4cc" />
|
||||
</view>
|
||||
|
||||
<view class="summary">
|
||||
以下为“<text class="summary-name">{{ customerName || "-" }}</text>”的所有剩余权益
|
||||
</view>
|
||||
|
||||
<view v-if="loading" class="empty">加载中...</view>
|
||||
<view v-else-if="!groups.length" class="empty">暂无权益</view>
|
||||
<view v-else class="group-list">
|
||||
<view v-for="group in groups" :key="group.key" class="group-card">
|
||||
<view class="group-head">
|
||||
<view class="group-badge">{{ group.badge }}</view>
|
||||
<view class="group-title">{{ group.title }}</view>
|
||||
</view>
|
||||
<view class="table-head">
|
||||
<text class="col col-name">项目名称</text>
|
||||
<text class="col col-num">总次数</text>
|
||||
<text class="col col-num">剩余次数</text>
|
||||
<text class="col col-date">有效期至</text>
|
||||
</view>
|
||||
<view v-for="row in group.rows" :key="row._id" class="table-row">
|
||||
<text class="col col-name">{{ row.projectName }}</text>
|
||||
<text class="col col-num">{{ row.usageCount }}</text>
|
||||
<text class="col col-num col-rest">{{ row.restUsageCount }}</text>
|
||||
<text class="col col-date">{{ row.validText }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</full-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from "vue";
|
||||
import { onLoad, onShow } from "@dcloudio/uni-app";
|
||||
import { storeToRefs } from "pinia";
|
||||
import useAccount from "@/store/account";
|
||||
import api from "@/utils/api";
|
||||
import { get, remove } from "@/utils/cache";
|
||||
import { toast } from "@/utils/widget";
|
||||
import { normalizeCorpId } from "@/utils/api-base-config";
|
||||
import FullPage from "@/components/full-page.vue";
|
||||
|
||||
const HOME_CURRENT_TEAM_CACHE_KEY = "home-current-team-info";
|
||||
const RIGHTS_SELECTION_CACHE_KEY = "experience-coupon-rights-selection";
|
||||
const { account } = storeToRefs(useAccount());
|
||||
const { getTeams } = useAccount();
|
||||
|
||||
const corpId = ref("");
|
||||
const teamId = ref("");
|
||||
const customerId = ref("");
|
||||
const customerName = ref("");
|
||||
const corpName = ref("");
|
||||
const teams = ref([]);
|
||||
const records = ref([]);
|
||||
const loading = ref(false);
|
||||
|
||||
function formatDate(ts) {
|
||||
if (!ts) return "无限期";
|
||||
const d = new Date(Number(ts));
|
||||
if (Number.isNaN(d.getTime())) return "无限期";
|
||||
const y = d.getFullYear();
|
||||
const m = String(d.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(d.getDate()).padStart(2, "0");
|
||||
return `${y}-${m}-${day}`;
|
||||
}
|
||||
|
||||
function pickCorpName(team = {}) {
|
||||
return team.licenseHospitalName || team.leaderCorp || team.corpName || "-";
|
||||
}
|
||||
|
||||
const groups = computed(() => {
|
||||
const list = Array.isArray(records.value) ? records.value : [];
|
||||
const valid = list.filter((item) => {
|
||||
const status = String(item.treatmentStatus || "");
|
||||
return !["void", "canceled", "cancel"].includes(status);
|
||||
});
|
||||
|
||||
const packageMap = {};
|
||||
const singleGroups = [];
|
||||
|
||||
valid.forEach((item) => {
|
||||
const row = {
|
||||
_id: item._id,
|
||||
projectName: item.projectName || "-",
|
||||
usageCount: Number(item.usageCount || 0),
|
||||
restUsageCount: Number(item.restUsageCount || 0),
|
||||
validText: formatDate(item.validTime),
|
||||
};
|
||||
|
||||
if (item.packageId) {
|
||||
const key = String(item.packageId);
|
||||
if (!packageMap[key]) {
|
||||
packageMap[key] = {
|
||||
key: `pkg_${key}`,
|
||||
badge: "套",
|
||||
title: `套餐(${item.packageName || "未命名套餐"})`,
|
||||
rows: [],
|
||||
};
|
||||
}
|
||||
packageMap[key].rows.push(row);
|
||||
return;
|
||||
}
|
||||
|
||||
singleGroups.push({
|
||||
key: `single_${item._id}`,
|
||||
badge: "项",
|
||||
title: "单项目",
|
||||
rows: [row],
|
||||
});
|
||||
});
|
||||
|
||||
return [...singleGroups, ...Object.values(packageMap)];
|
||||
});
|
||||
|
||||
function applySelection(selection = {}) {
|
||||
corpId.value = normalizeCorpId(selection.corpId || corpId.value || "");
|
||||
teamId.value = selection.teamId || teamId.value || "";
|
||||
customerId.value = selection.customerId || selection.memberId || customerId.value || "";
|
||||
customerName.value = selection.name || customerName.value || "";
|
||||
if (selection.corpName) {
|
||||
corpName.value = selection.corpName;
|
||||
return;
|
||||
}
|
||||
const matched = teams.value.find(
|
||||
(team) =>
|
||||
normalizeCorpId(team.corpId) === corpId.value &&
|
||||
(!teamId.value || team.teamId === teamId.value)
|
||||
);
|
||||
corpName.value = pickCorpName(matched || {});
|
||||
}
|
||||
|
||||
async function resolveContext(options = {}) {
|
||||
const cached = get(HOME_CURRENT_TEAM_CACHE_KEY) || {};
|
||||
try {
|
||||
teams.value = (await getTeams()) || [];
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
teams.value = [];
|
||||
}
|
||||
|
||||
const incoming = {
|
||||
corpId: options.corpId || corpId.value || cached.corpId || "",
|
||||
teamId: options.teamId || teamId.value || cached.teamId || "",
|
||||
customerId: options.customerId || options.memberId || customerId.value || "",
|
||||
name: options.name ? decodeURIComponent(options.name) : customerName.value,
|
||||
};
|
||||
applySelection(incoming);
|
||||
|
||||
const matched =
|
||||
teams.value.find(
|
||||
(team) =>
|
||||
team.teamId === teamId.value &&
|
||||
normalizeCorpId(team.corpId) === corpId.value
|
||||
) ||
|
||||
teams.value.find((team) => team.teamId === teamId.value) ||
|
||||
teams.value.find((team) => normalizeCorpId(team.corpId) === corpId.value) ||
|
||||
teams.value[0] ||
|
||||
null;
|
||||
|
||||
if (!corpId.value) corpId.value = normalizeCorpId(matched?.corpId || "");
|
||||
if (!teamId.value) teamId.value = matched?.teamId || "";
|
||||
if (!corpName.value) corpName.value = pickCorpName(matched || {});
|
||||
|
||||
if (customerId.value) return true;
|
||||
|
||||
const miniAppId = account.value?.openid || uni.getStorageSync("openid") || "";
|
||||
if (!miniAppId || !corpId.value) return false;
|
||||
|
||||
const res = await api("getMiniAppCustomers", { miniAppId, corpId: corpId.value }, false);
|
||||
const customers = res?.success && Array.isArray(res.data) ? res.data : [];
|
||||
const preferred = customers.find((item) => item.relationship === "本人") || customers[0];
|
||||
customerId.value = preferred?._id || "";
|
||||
customerName.value = preferred?.name || customerName.value || "";
|
||||
return !!customerId.value;
|
||||
}
|
||||
|
||||
async function loadRights() {
|
||||
if (!corpId.value || !customerId.value) {
|
||||
records.value = [];
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await api(
|
||||
"getTreatmentRecord",
|
||||
{
|
||||
corpId: corpId.value,
|
||||
customerId: customerId.value,
|
||||
page: 1,
|
||||
pageSize: 200,
|
||||
},
|
||||
false
|
||||
);
|
||||
if (!res?.success) {
|
||||
toast(res?.message || "加载权益失败");
|
||||
records.value = [];
|
||||
return;
|
||||
}
|
||||
records.value = res.list || res.data?.list || [];
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function syncSelectedProfile() {
|
||||
const selected = get(RIGHTS_SELECTION_CACHE_KEY);
|
||||
if (!selected) return false;
|
||||
remove(RIGHTS_SELECTION_CACHE_KEY);
|
||||
applySelection(selected);
|
||||
await loadRights();
|
||||
return true;
|
||||
}
|
||||
|
||||
function toSelectPage() {
|
||||
const params = [
|
||||
`corpId=${encodeURIComponent(corpId.value || "")}`,
|
||||
`teamId=${encodeURIComponent(teamId.value || "")}`,
|
||||
`customerId=${encodeURIComponent(customerId.value || "")}`,
|
||||
`name=${encodeURIComponent(customerName.value || "")}`,
|
||||
].join("&");
|
||||
uni.navigateTo({
|
||||
url: `/pages/experience-coupon/select-rights-archive?${params}`,
|
||||
});
|
||||
}
|
||||
|
||||
onLoad(async (options = {}) => {
|
||||
uni.setNavigationBarTitle({ title: "我的权益" });
|
||||
const ok = await resolveContext(options);
|
||||
if (!ok) {
|
||||
toast("请先绑定档案");
|
||||
return;
|
||||
}
|
||||
await loadRights();
|
||||
});
|
||||
|
||||
onShow(async () => {
|
||||
const changed = await syncSelectedProfile();
|
||||
if (changed) return;
|
||||
if (corpId.value && customerId.value) {
|
||||
await loadRights();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-body {
|
||||
min-height: 100%;
|
||||
padding: 0 0 40rpx;
|
||||
box-sizing: border-box;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.user-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
border-radius: 0;
|
||||
padding: 24rpx 30rpx;
|
||||
border-bottom: 1px solid #eceff4;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 88rpx;
|
||||
height: 88rpx;
|
||||
border-radius: 50%;
|
||||
background: #f0f2f5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 20rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.user-main {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #222;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.user-corp {
|
||||
font-size: 24rpx;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.summary {
|
||||
padding: 18rpx 30rpx;
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
background: #f0f2f5;
|
||||
border-top: 1px solid #eceff4;
|
||||
border-bottom: 1px solid #eceff4;
|
||||
}
|
||||
|
||||
.summary-name {
|
||||
color: #065bd6;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.group-list {
|
||||
padding-top: 12rpx;
|
||||
}
|
||||
|
||||
.group-card {
|
||||
margin-bottom: 16rpx;
|
||||
background: #fff;
|
||||
border-top: 1px solid #eceff4;
|
||||
border-bottom: 1px solid #eceff4;
|
||||
}
|
||||
|
||||
.group-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 18rpx 24rpx;
|
||||
border-bottom: 1px solid #eceff4;
|
||||
}
|
||||
|
||||
.group-badge {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
border-radius: 50%;
|
||||
background: #2f7df6;
|
||||
color: #fff;
|
||||
font-size: 22rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 12rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.group-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.table-head,
|
||||
.table-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding: 18rpx 24rpx;
|
||||
}
|
||||
|
||||
.table-head {
|
||||
color: #666;
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
background: #f5f9fc;
|
||||
border-bottom: 1px solid #eceff4;
|
||||
}
|
||||
|
||||
.table-row {
|
||||
color: #333;
|
||||
font-size: 26rpx;
|
||||
border-bottom: 1px solid #f1f3f6;
|
||||
}
|
||||
|
||||
.table-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.col {
|
||||
text-align: center;
|
||||
word-break: break-all;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.col-name {
|
||||
flex: 1.35;
|
||||
text-align: left;
|
||||
padding-right: 16rpx;
|
||||
}
|
||||
|
||||
.col-num {
|
||||
flex: 0.7;
|
||||
}
|
||||
|
||||
.col-date {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.col-rest {
|
||||
color: #ff9800;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.empty {
|
||||
padding: 120rpx 0;
|
||||
text-align: center;
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
</style>
|
||||
285
pages/experience-coupon/select-rights-archive.vue
Normal file
285
pages/experience-coupon/select-rights-archive.vue
Normal file
@ -0,0 +1,285 @@
|
||||
<template>
|
||||
<full-page pageClass="rights-selector-page" :customScroll="true">
|
||||
<view class="page-body">
|
||||
<view v-if="loading" class="empty">加载中...</view>
|
||||
<view v-else-if="!options.length" class="empty">暂无可用档案</view>
|
||||
<view v-else class="archive-list">
|
||||
<view
|
||||
v-for="item in options"
|
||||
:key="item.key"
|
||||
class="archive-card"
|
||||
:class="{ active: item.isCurrent }"
|
||||
@click="selectArchive(item)"
|
||||
>
|
||||
<view class="archive-head">
|
||||
<view class="archive-name-row">
|
||||
<view class="archive-name">{{ item.name || "未命名" }}</view>
|
||||
<view v-if="item.relationship" class="archive-tag">{{ item.relationship }}</view>
|
||||
</view>
|
||||
<view v-if="item.isCurrent" class="archive-current">当前</view>
|
||||
</view>
|
||||
<view class="archive-meta">{{ item.metaText }}</view>
|
||||
<view class="archive-id">证件号:{{ item.idCardText }}</view>
|
||||
<view class="archive-corp">{{ item.corpName }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</full-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { storeToRefs } from "pinia";
|
||||
import useAccount from "@/store/account";
|
||||
import api from "@/utils/api";
|
||||
import { set } from "@/utils/cache";
|
||||
import { toast } from "@/utils/widget";
|
||||
import { normalizeCorpId } from "@/utils/api-base-config";
|
||||
import FullPage from "@/components/full-page.vue";
|
||||
|
||||
const RIGHTS_SELECTION_CACHE_KEY = "experience-coupon-rights-selection";
|
||||
const { account } = storeToRefs(useAccount());
|
||||
const { getTeams } = useAccount();
|
||||
|
||||
const loading = ref(false);
|
||||
const options = ref([]);
|
||||
const currentCorpId = ref("");
|
||||
const currentTeamId = ref("");
|
||||
const currentCustomerId = ref("");
|
||||
const mode = ref("back");
|
||||
const target = ref("");
|
||||
|
||||
function pickCorpName(team = {}) {
|
||||
return team.licenseHospitalName || team.leaderCorp || team.corpName || "-";
|
||||
}
|
||||
|
||||
function maskMobile(mobile = "") {
|
||||
const value = String(mobile || "").trim();
|
||||
if (!value) return "";
|
||||
if (/^\d{11}$/.test(value)) {
|
||||
return value.replace(/(\d{3})\d{4}(\d{4})/, "$1****$2");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function maskIdCard(idCard = "") {
|
||||
const value = String(idCard || "").trim();
|
||||
if (!value) return "--";
|
||||
if (value.length <= 4) return value;
|
||||
return `${"*".repeat(Math.max(value.length - 4, 2))}${value.slice(-4)}`;
|
||||
}
|
||||
|
||||
function buildMetaText(customer = {}) {
|
||||
const parts = [];
|
||||
if (customer.age) parts.push(`${customer.age}岁`);
|
||||
if (customer.mobile) parts.push(maskMobile(customer.mobile));
|
||||
return parts.length ? parts.join(",") : "暂无档案信息";
|
||||
}
|
||||
|
||||
function dedupeByCorp(list = []) {
|
||||
const map = new Map();
|
||||
list.forEach((team) => {
|
||||
const corpId = normalizeCorpId(team.corpId);
|
||||
if (!corpId || map.has(corpId)) return;
|
||||
map.set(corpId, { ...team, corpId });
|
||||
});
|
||||
return Array.from(map.values());
|
||||
}
|
||||
|
||||
function buildTargetUrl(item) {
|
||||
const corp = encodeURIComponent(item.corpId || "");
|
||||
const team = encodeURIComponent(item.teamId || "");
|
||||
const customer = encodeURIComponent(item.customerId || "");
|
||||
const name = encodeURIComponent(item.name || "");
|
||||
|
||||
if (target.value === "coupons") {
|
||||
return `/pages/experience-coupon/my-coupons?corpId=${corp}&teamId=${team}&customerId=${customer}`;
|
||||
}
|
||||
if (target.value === "health") {
|
||||
return `/pages/health/list?teamId=${team}&corpId=${corp}&id=${customer}&name=${name}`;
|
||||
}
|
||||
return `/pages/experience-coupon/my-rights?corpId=${corp}&teamId=${team}&customerId=${customer}&name=${name}`;
|
||||
}
|
||||
|
||||
async function loadArchives() {
|
||||
const miniAppId = account.value?.openid || uni.getStorageSync("openid") || "";
|
||||
if (!miniAppId) {
|
||||
options.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
const teams = (await getTeams()) || [];
|
||||
const corpTeams = dedupeByCorp(teams);
|
||||
const responses = await Promise.all(
|
||||
corpTeams.map(async (team) => {
|
||||
try {
|
||||
const res = await api("getMiniAppCustomers", { miniAppId, corpId: team.corpId }, false);
|
||||
const customers = res?.success && Array.isArray(res.data) ? res.data : [];
|
||||
return customers.map((customer) => ({ team, customer }));
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
return [];
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
const list = responses
|
||||
.flat()
|
||||
.map(({ team, customer }) => ({
|
||||
key: `${team.corpId}_${customer._id}`,
|
||||
corpId: team.corpId,
|
||||
teamId: team.teamId || "",
|
||||
customerId: customer._id || "",
|
||||
name: customer.name || "",
|
||||
relationship: customer.relationship || "",
|
||||
metaText: buildMetaText(customer),
|
||||
idCardText: maskIdCard(customer.idCard),
|
||||
corpName: pickCorpName(team),
|
||||
}))
|
||||
.filter((item) => item.corpId && item.customerId)
|
||||
.sort((a, b) => {
|
||||
const aScore =
|
||||
(a.corpId === currentCorpId.value ? 4 : 0) +
|
||||
(a.teamId === currentTeamId.value ? 2 : 0) +
|
||||
(a.customerId === currentCustomerId.value ? 8 : 0) +
|
||||
(a.relationship === "本人" ? 1 : 0);
|
||||
const bScore =
|
||||
(b.corpId === currentCorpId.value ? 4 : 0) +
|
||||
(b.teamId === currentTeamId.value ? 2 : 0) +
|
||||
(b.customerId === currentCustomerId.value ? 8 : 0) +
|
||||
(b.relationship === "本人" ? 1 : 0);
|
||||
return bScore - aScore;
|
||||
})
|
||||
.map((item) => ({
|
||||
...item,
|
||||
isCurrent:
|
||||
item.corpId === currentCorpId.value &&
|
||||
item.customerId === currentCustomerId.value,
|
||||
}));
|
||||
|
||||
options.value = list;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function selectArchive(item) {
|
||||
if (!item) return;
|
||||
|
||||
if (mode.value === "target") {
|
||||
uni.navigateTo({
|
||||
url: buildTargetUrl(item),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
set(RIGHTS_SELECTION_CACHE_KEY, {
|
||||
corpId: item.corpId,
|
||||
teamId: item.teamId,
|
||||
customerId: item.customerId,
|
||||
name: item.name,
|
||||
corpName: item.corpName,
|
||||
});
|
||||
uni.navigateBack();
|
||||
}
|
||||
|
||||
onLoad(async (optionsData = {}) => {
|
||||
uni.setNavigationBarTitle({ title: "选择档案" });
|
||||
currentCorpId.value = normalizeCorpId(optionsData.corpId || "");
|
||||
currentTeamId.value = optionsData.teamId || "";
|
||||
currentCustomerId.value = optionsData.customerId || optionsData.memberId || "";
|
||||
mode.value = optionsData.mode === "target" ? "target" : "back";
|
||||
target.value = optionsData.target || "rights";
|
||||
await loadArchives();
|
||||
if (!options.value.length) {
|
||||
toast("暂无可用档案");
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-body {
|
||||
min-height: 100%;
|
||||
padding: 24rpx;
|
||||
box-sizing: border-box;
|
||||
background: #f5f6fa;
|
||||
}
|
||||
|
||||
.archive-card {
|
||||
background: #fff;
|
||||
border-radius: 18rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 20rpx;
|
||||
border: 2rpx solid transparent;
|
||||
box-shadow: 0 8rpx 24rpx rgba(15, 23, 42, 0.06);
|
||||
}
|
||||
|
||||
.archive-card.active {
|
||||
border-color: #065bd6;
|
||||
}
|
||||
|
||||
.archive-head {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 16rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.archive-name-row {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.archive-name {
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
color: #1f2329;
|
||||
}
|
||||
|
||||
.archive-tag,
|
||||
.archive-current {
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 999rpx;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
|
||||
.archive-tag {
|
||||
color: #0f766e;
|
||||
background: rgba(15, 118, 110, 0.12);
|
||||
}
|
||||
|
||||
.archive-current {
|
||||
color: #065bd6;
|
||||
background: rgba(6, 91, 214, 0.1);
|
||||
}
|
||||
|
||||
.archive-meta,
|
||||
.archive-id {
|
||||
font-size: 28rpx;
|
||||
color: #4b5563;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.archive-corp {
|
||||
margin-top: 16rpx;
|
||||
padding-top: 16rpx;
|
||||
border-top: 1px solid #eef2f6;
|
||||
font-size: 28rpx;
|
||||
color: #065bd6;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.empty {
|
||||
padding: 120rpx 0;
|
||||
text-align: center;
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
</style>
|
||||
36
pages/experience-coupon/wallet.vue
Normal file
36
pages/experience-coupon/wallet.vue
Normal file
@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<view class="redirect-page">跳转中...</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
|
||||
onLoad((options = {}) => {
|
||||
const type = options.type === "coupon" ? "coupon" : "rights";
|
||||
const params = [
|
||||
options.corpId ? `corpId=${encodeURIComponent(options.corpId)}` : "",
|
||||
options.teamId ? `teamId=${encodeURIComponent(options.teamId)}` : "",
|
||||
options.customerId ? `customerId=${encodeURIComponent(options.customerId)}` : "",
|
||||
options.memberId ? `memberId=${encodeURIComponent(options.memberId)}` : "",
|
||||
options.name ? `name=${encodeURIComponent(options.name)}` : "",
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("&");
|
||||
|
||||
const target =
|
||||
type === "coupon"
|
||||
? `/pages/experience-coupon/my-coupons?${params}`
|
||||
: `/pages/experience-coupon/my-rights?${params}`;
|
||||
|
||||
uni.redirectTo({ url: target });
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.redirect-page {
|
||||
padding: 120rpx 0;
|
||||
text-align: center;
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
</style>
|
||||
@ -147,6 +147,18 @@ onLoad((options) => {
|
||||
if (options.q) {
|
||||
opts.value = JSON.stringify(options)
|
||||
changeTeam(parseInviteOptions(options));
|
||||
} else if (options.type === 'experienceCoupon' && options.issueId && options.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)}` : "",
|
||||
].filter(Boolean).join("&");
|
||||
uni.redirectTo({
|
||||
url: `/pages/experience-coupon/claim?${params}`,
|
||||
});
|
||||
} else if (options.type === 'archive' || (options.teamId && options.corpId)) {
|
||||
changeTeam(options);
|
||||
}
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
<template>
|
||||
<view class="page-container">
|
||||
<!-- Blue background area for header -->
|
||||
<view class="header-bg"></view>
|
||||
|
||||
<!-- User Card -->
|
||||
<view class="user-card">
|
||||
<view class="avatar-container">
|
||||
<uni-icons type="person-filled" size="60" color="#cccccc" class="default-avatar"></uni-icons>
|
||||
@ -14,7 +12,27 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Menu List -->
|
||||
<view class="shortcut-bar">
|
||||
<view class="shortcut-item" @click="openRightsWallet">
|
||||
<view class="shortcut-icon shortcut-icon--rights">
|
||||
<uni-icons type="gift" size="28" color="#065bd6"></uni-icons>
|
||||
</view>
|
||||
<text class="shortcut-title">我的权益</text>
|
||||
</view>
|
||||
<view class="shortcut-item" @click="openCouponWallet">
|
||||
<view class="shortcut-icon shortcut-icon--coupon">
|
||||
<uni-icons type="wallet" size="28" color="#ff8a00"></uni-icons>
|
||||
</view>
|
||||
<text class="shortcut-title">我的体验券</text>
|
||||
</view>
|
||||
<view class="shortcut-item" @click="openHealthRecord">
|
||||
<view class="shortcut-icon shortcut-icon--record">
|
||||
<uni-icons type="compose" size="28" color="#19be6b"></uni-icons>
|
||||
</view>
|
||||
<text class="shortcut-title">治疗记录</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-container">
|
||||
<view class="px-15 py-12 flex items-center border-b" @click="toPage('/pages/mine/contact')">
|
||||
<view class="flex-shrink-0 item-icon">
|
||||
@ -53,81 +71,203 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- <view class="menu-container">
|
||||
<uni-list>
|
||||
<uni-list-item title="联系客服" link to="/pages/mine/contact" clickable>
|
||||
<template v-slot:header>
|
||||
<view class="item-icon">
|
||||
<uni-icons type="headphones" size="22" color="#000"></uni-icons>
|
||||
</view>
|
||||
</template>
|
||||
</uni-list-item>
|
||||
<uni-list-item title="隐私保护政策" link to="/pages/common/privacy" clickable>
|
||||
<template v-slot:header>
|
||||
<view class="item-icon">
|
||||
<uni-icons type="locked" size="22" color="#000"></uni-icons>
|
||||
</view>
|
||||
</template>
|
||||
</uni-list-item>
|
||||
<uni-list-item title="用户注册协议" link to="/pages/common/agreement" clickable>
|
||||
<template v-slot:header>
|
||||
<view class="item-icon">
|
||||
<uni-icons type="paperclip" size="22" color="#000"></uni-icons>
|
||||
</view>
|
||||
</template>
|
||||
</uni-list-item>
|
||||
<uni-list-item title="退出登录" link @click="handleLogout">
|
||||
<template v-slot:header>
|
||||
<view class="item-icon">
|
||||
<uni-icons type="undo" size="22" color="#000"></uni-icons>
|
||||
</view>
|
||||
</template>
|
||||
</uni-list-item>
|
||||
</uni-list>
|
||||
</view> -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { onShow } from '@dcloudio/uni-app';
|
||||
import { computed, ref } from "vue";
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
import { storeToRefs } from "pinia";
|
||||
import accountStore from "@/store/account";
|
||||
import api from "@/utils/api";
|
||||
import { get } from "@/utils/cache";
|
||||
import { normalizeCorpId } from "@/utils/api-base-config";
|
||||
import { toast } from "@/utils/widget";
|
||||
|
||||
const HOME_CURRENT_TEAM_CACHE_KEY = "home-current-team-info";
|
||||
|
||||
const store = accountStore();
|
||||
const { account } = storeToRefs(store);
|
||||
const { getTeams, login } = store;
|
||||
|
||||
const context = ref({
|
||||
corpId: "",
|
||||
teamId: "",
|
||||
customerId: "",
|
||||
customerName: "",
|
||||
customers: [],
|
||||
});
|
||||
const resolving = ref(false);
|
||||
|
||||
const maskedPhone = computed(() => {
|
||||
const account = uni.getStorageSync('account');
|
||||
const mobile = account?.mobile || store.account?.mobile || '';
|
||||
if (!mobile) return '';
|
||||
return mobile.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
|
||||
const localAccount = uni.getStorageSync("account");
|
||||
const mobile = localAccount?.mobile || account.value?.mobile || "";
|
||||
if (!mobile) return "";
|
||||
return mobile.replace(/(\d{3})\d{4}(\d{4})/, "$1****$2");
|
||||
});
|
||||
|
||||
const handleLogout = () => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要退出登录吗?',
|
||||
title: "提示",
|
||||
content: "确定要退出登录吗?",
|
||||
success: function (res) {
|
||||
if (res.confirm) {
|
||||
// Clear login info
|
||||
uni.removeStorageSync('account');
|
||||
uni.removeStorageSync('openid');
|
||||
uni.removeStorageSync("account");
|
||||
uni.removeStorageSync("openid");
|
||||
store.account = null;
|
||||
|
||||
// Redirect to login or home
|
||||
uni.reLaunch({
|
||||
url: '/pages/login/login'
|
||||
url: "/pages/login/login",
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
function toPage(url) {
|
||||
function pickPreferredCustomer(list = []) {
|
||||
if (!Array.isArray(list) || !list.length) return null;
|
||||
return list.find((item) => item.relationship === "本人") || list[0] || null;
|
||||
}
|
||||
|
||||
async function ensureLogin() {
|
||||
if (account.value?.openid) return true;
|
||||
const local = uni.getStorageSync("account");
|
||||
if (local?.openid) {
|
||||
store.account = local;
|
||||
return true;
|
||||
}
|
||||
const res = await login();
|
||||
return !!(res && res.openid);
|
||||
}
|
||||
|
||||
async function resolveTeam() {
|
||||
const cached = get(HOME_CURRENT_TEAM_CACHE_KEY) || {};
|
||||
let teams = [];
|
||||
try {
|
||||
teams = (await getTeams()) || [];
|
||||
} catch (e) {
|
||||
console.warn("getTeams failed", e);
|
||||
}
|
||||
const cachedCorpId = normalizeCorpId(cached.corpId || "");
|
||||
const matched =
|
||||
teams.find(
|
||||
(item) =>
|
||||
item.teamId === cached.teamId &&
|
||||
(!cachedCorpId || normalizeCorpId(item.corpId || "") === cachedCorpId)
|
||||
) ||
|
||||
teams.find((item) => item.teamId === cached.teamId) ||
|
||||
teams[0] ||
|
||||
null;
|
||||
|
||||
return {
|
||||
corpId: normalizeCorpId(matched?.corpId || cached.corpId || ""),
|
||||
teamId: matched?.teamId || cached.teamId || "",
|
||||
};
|
||||
}
|
||||
|
||||
async function resolveContext({ force = false } = {}) {
|
||||
if (resolving.value) return context.value;
|
||||
if (!force && context.value.corpId && context.value.customerId) {
|
||||
return context.value;
|
||||
}
|
||||
|
||||
resolving.value = true;
|
||||
try {
|
||||
const ok = await ensureLogin();
|
||||
if (!ok) {
|
||||
toast("请先登录");
|
||||
return null;
|
||||
}
|
||||
|
||||
const team = await resolveTeam();
|
||||
if (!team.corpId) {
|
||||
toast("请先加入服务团队");
|
||||
return null;
|
||||
}
|
||||
|
||||
const miniAppId = account.value?.openid || uni.getStorageSync("openid") || "";
|
||||
const res = await api(
|
||||
"getMiniAppCustomers",
|
||||
{ miniAppId, corpId: team.corpId },
|
||||
false
|
||||
);
|
||||
const customers = res?.success && Array.isArray(res.data) ? res.data : [];
|
||||
const preferred = pickPreferredCustomer(customers);
|
||||
|
||||
context.value = {
|
||||
corpId: team.corpId,
|
||||
teamId: team.teamId,
|
||||
customerId: preferred?._id || "",
|
||||
customerName: preferred
|
||||
? `${preferred.name || ""}${preferred.relationship ? ` ${preferred.relationship}` : ""}`.trim()
|
||||
: "",
|
||||
customers,
|
||||
};
|
||||
return context.value;
|
||||
} finally {
|
||||
resolving.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function goBindArchive(ctx) {
|
||||
const corpId = ctx?.corpId || "";
|
||||
const teamId = ctx?.teamId || "";
|
||||
uni.navigateTo({
|
||||
url: url
|
||||
url: `/pages/archive/archive-manage?corpId=${encodeURIComponent(corpId)}&teamId=${encodeURIComponent(teamId)}`,
|
||||
});
|
||||
}
|
||||
|
||||
function openArchiveSelector(target, ctx) {
|
||||
const params = [
|
||||
`mode=target`,
|
||||
`target=${encodeURIComponent(target)}`,
|
||||
`corpId=${encodeURIComponent(ctx?.corpId || "")}`,
|
||||
`teamId=${encodeURIComponent(ctx?.teamId || "")}`,
|
||||
`customerId=${encodeURIComponent(ctx?.customerId || "")}`,
|
||||
`name=${encodeURIComponent(ctx?.customerName || "")}`,
|
||||
].join("&");
|
||||
uni.navigateTo({
|
||||
url: `/pages/experience-coupon/select-rights-archive?${params}`,
|
||||
});
|
||||
}
|
||||
|
||||
async function openWithArchive(target) {
|
||||
const ctx = await resolveContext({ force: true });
|
||||
if (!ctx) return;
|
||||
|
||||
if (!ctx.customerId) {
|
||||
uni.showModal({
|
||||
title: "提示",
|
||||
content: "请先绑定档案后再查看",
|
||||
confirmText: "去绑定",
|
||||
success: (res) => {
|
||||
if (res.confirm) goBindArchive(ctx);
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
openArchiveSelector(target, ctx);
|
||||
}
|
||||
|
||||
async function openRightsWallet() {
|
||||
await openWithArchive("rights");
|
||||
}
|
||||
|
||||
async function openCouponWallet() {
|
||||
await openWithArchive("coupons");
|
||||
}
|
||||
|
||||
async function openHealthRecord() {
|
||||
await openWithArchive("health");
|
||||
}
|
||||
|
||||
function toPage(url) {
|
||||
uni.navigateTo({ url });
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
resolveContext({ force: true });
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@ -184,14 +324,63 @@ page {
|
||||
}
|
||||
}
|
||||
|
||||
.shortcut-bar {
|
||||
margin: 0 15px 15px;
|
||||
padding: 12px 0 10px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.shortcut-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.shortcut-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin-bottom: 6px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.shortcut-icon--rights {
|
||||
background: #eef5ff;
|
||||
}
|
||||
|
||||
.shortcut-icon--coupon {
|
||||
background: #fff6e8;
|
||||
}
|
||||
|
||||
.shortcut-icon--record {
|
||||
background: #ecfff5;
|
||||
}
|
||||
|
||||
.shortcut-title {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.menu-container {
|
||||
background-color: #fff;
|
||||
margin-top: 10px;
|
||||
margin: 0 15px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.item-icon {
|
||||
margin-right: 10px;
|
||||
width: 24px;
|
||||
margin-right: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@ -12,6 +12,26 @@ export default [
|
||||
path: 'pages/login/redirect-page',
|
||||
meta: { title: '柚健康' },
|
||||
},
|
||||
{
|
||||
path: 'pages/experience-coupon/claim',
|
||||
meta: { title: '领取体验券', login: true }
|
||||
},
|
||||
{
|
||||
path: 'pages/experience-coupon/my-rights',
|
||||
meta: { title: '我的权益', login: true }
|
||||
},
|
||||
{
|
||||
path: 'pages/experience-coupon/select-rights-archive',
|
||||
meta: { title: '选择档案', login: true }
|
||||
},
|
||||
{
|
||||
path: 'pages/experience-coupon/my-coupons',
|
||||
meta: { title: '我的体验券', login: true }
|
||||
},
|
||||
{
|
||||
path: 'pages/experience-coupon/wallet',
|
||||
meta: { title: '我的卡包', login: true }
|
||||
},
|
||||
{
|
||||
path: 'pages/archive/archive-manage',
|
||||
meta: { title: '档案管理', login: true }
|
||||
|
||||
@ -50,9 +50,15 @@ const urlsConfig = {
|
||||
addMedicalRecord: 'addMedicalRecord',
|
||||
authCustomerToTeam: 'authCustomerToTeam', // 授权客户到团队
|
||||
bindMiniAppArchive: "bindMiniAppArchive",
|
||||
claimExperienceCoupon: "claimExperienceCoupon",
|
||||
voidExperienceCouponIssue: "voidExperienceCouponIssue",
|
||||
getCustomerByCustomerId: 'getCustomerByCustomerId',
|
||||
getCustomerExperienceCoupons: "getCustomerExperienceCoupons",
|
||||
getExperienceCouponIssueDetail: "getExperienceCouponIssueDetail",
|
||||
getExperienceCouponIssueList: "getExperienceCouponIssueList",
|
||||
getMiniAppCustomers: 'getMiniAppCustomers',
|
||||
getTeamCustomers: 'getTeamCustomers',
|
||||
getTreatmentRecord: "getTreatmentRecord",
|
||||
getUnbindMiniAppCustomers: 'getUnbindMiniAppCustomers',
|
||||
getCustomerMedicalRecord: 'getCustomerMedicalRecord',
|
||||
getMedicalRecordById: 'getMedicalRecordById',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user