438 lines
11 KiB
Vue
438 lines
11 KiB
Vue
<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}`;
|
||
}
|
||
|
||
async function loadCorpName() {
|
||
if (!corpId.value) {
|
||
corpName.value = "";
|
||
return;
|
||
}
|
||
const res = await api("getCorpInfo", { corpId: corpId.value }, false);
|
||
const corp = Array.isArray(res?.data) ? res.data[0] : null;
|
||
corpName.value = corp?.corp_name || corp?.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 || "";
|
||
}
|
||
|
||
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 || "";
|
||
await loadCorpName();
|
||
|
||
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 loadCorpName();
|
||
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> |