2026-08-03 10:03:56 +08:00

441 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<full-page pageClass="rights-page" :customScroll="true">
<scroll-view
class="rights-scroll"
scroll-y="true"
refresher-enabled="true"
:refresher-triggered="refreshing"
@refresherrefresh="refreshRights"
>
<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>
</scroll-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);
const refreshing = 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(options = {}) {
const { silent = false } = options;
if (!corpId.value || !customerId.value) {
records.value = [];
return;
}
if (!silent) 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 refreshRights() {
if (refreshing.value) return;
refreshing.value = true;
try {
await loadRights({ silent: true });
} finally {
refreshing.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>
.rights-page :deep(.page-scroll) {
overflow: hidden;
}
.rights-scroll {
height: 100%;
background: #f5f5f5;
}
.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>