add point account list

This commit is contained in:
Timi
2026-08-22 16:42:32 +08:00
parent e237b71f13
commit 1a333508cd
12 changed files with 523 additions and 342 deletions
+256
View File
@@ -0,0 +1,256 @@
<template>
<div v-can="permissionCodes.point.accountRead" class="point-account-detail">
<card-container v-if="customer">
<customer-profile-card :customer="customer" />
</card-container>
<section v-if="account" class="account-card">
<div class="header">
<div>积分卡</div>
<div class="time dark-white" v-text="formatTimestamp(account.createdAt!)" />
</div>
<div class="balance">
<t-icon name="money" size="2rem" />
<div class="value" v-text="balanceText" />
</div>
<div class="id" v-text="account.id?.toUpperCase()" />
</section>
<t-cell-group v-if="account" title="积分统计" theme="card">
<t-cell title="累计获得" :note="formatPoints(account.totalEarned)" />
<t-cell title="累计消耗" :note="formatPoints(account.totalConsumed)" />
<t-cell title="累计撤销" :note="formatPoints(account.totalRevoked)" />
<t-cell title="人工调整" :note="formatSignedPoints(account.totalAdjusted)" />
</t-cell-group>
<section v-if="customer" class="actions">
<t-button
v-can="permissionCodes.point.ledgerRead"
class="bg-white"
theme="primary"
variant="outline"
@click="toLedger"
>
查看积分流水
</t-button>
<t-button
v-can="permissionCodes.point.adjust"
theme="primary"
@click="toAdjust"
>
人工调整积分
</t-button>
</section>
<t-empty v-if="!account" :description="isLoading ? '正在加载积分账户' : '积分账户不存在或已被删除'" />
</div>
</template>
<script setup lang="ts">
import { Message } from "@/utils/message";
import { useUserStore } from "timi-web";
import CustomerAPI from "@/api/CustomerAPI";
import PointAPI from "@/api/PointAPI";
import CustomerProfileCard from "@/components/CustomerProfileCard.vue";
import { useCurrentStoreStore } from "@/store/currentStoreStore";
import type { Customer } from "@/types/Customer";
import type { PointAccount as PointAccountData } from "@/types/Point";
import { gaoRoleCodes, permissionCodes } from "@/utils/permission";
defineOptions({
name: "PointAccountDetail"
});
const route = useRoute();
const router = useRouter();
const userStore = useUserStore();
const currentStoreStore = useCurrentStoreStore();
// ---------- 门店 ----------
const isGlobalManager = computed(() => userStore.hasRoles(gaoRoleCodes.globalManager));
async function prepareStoreScope(): Promise<void> {
if (isGlobalManager.value) {
await currentStoreStore.loadStoreList();
return;
}
await currentStoreStore.loadLoginStore();
}
// ---------- 账户 ----------
const accountId = String(route.params.id || "");
const customer = ref<Customer>();
const account = ref<PointAccountData>();
const isLoading = ref(false);
const balanceText = computed(() => formatPoints(account.value?.balance));
async function loadAccount(): Promise<void> {
if (!accountId) {
customer.value = undefined;
account.value = undefined;
return;
}
isLoading.value = true;
try {
account.value = await PointAPI.detail(
accountId,
String(route.query.storeId || currentStoreStore.currentStoreId || "")
);
if (!account.value) {
customer.value = undefined;
return;
}
customer.value = account.value.customer || (account.value.customerId ? await CustomerAPI.detail(account.value.customerId) : undefined);
} catch {
customer.value = undefined;
account.value = undefined;
Message.error("积分账户加载失败");
} finally {
isLoading.value = false;
}
}
// ---------- 导航 ----------
async function toLedger(): Promise<void> {
if (!account.value?.customerId) {
return;
}
await router.push({
name: "PointLedgerList",
query: {
customerId: account.value.customerId,
accountId: account.value.id || "",
transitionDepth: String(getCurrentRouteDepth() + 1)
}
});
}
function getCurrentRouteDepth(): number {
const transitionDepth = Array.isArray(route.query.transitionDepth)
? route.query.transitionDepth[0]
: route.query.transitionDepth;
const depth = Number(transitionDepth);
if (transitionDepth && Number.isInteger(depth) && 0 <= depth && depth <= 20) {
return depth;
}
return Number(route.meta.depth ?? 0);
}
async function toAdjust(): Promise<void> {
if (!account.value?.customerId) {
return;
}
await router.push({
name: "PointAdjust",
query: { customerId: account.value.customerId }
});
}
// ---------- 展示 ----------
function formatPoints(value?: number): string {
return `${(value ?? 0).toLocaleString("zh-CN")} 分`;
}
function formatTimestamp(timestamp?: number): string {
if (!timestamp) {
return "未知时间";
}
const date = new Date(timestamp);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}${month}${day}`;
}
function formatSignedPoints(value?: number): string {
const pointValue = value ?? 0;
return pointValue < 0 ? `扣除 ${Math.abs(pointValue).toLocaleString("zh-CN")} 分` : `增加 ${pointValue.toLocaleString("zh-CN")} 分`;
}
onMounted(async () => {
try {
await prepareStoreScope();
await loadAccount();
} catch {
Message.error("积分账户页面加载失败");
}
});
</script>
<style scoped lang="less">
.point-account-detail {
gap: 1rem;
display: flex;
padding: calc(var(--app-nav-height) + 1rem) 0 calc(6rem + env(safe-area-inset-bottom, 0));
flex-direction: column;
.account-card {
color: #FFF;
margin: 0 1rem;
padding: 1rem;
background: rgba(255, 255, 255, .92);
box-shadow: var(--td-shadow-1);
border-radius: 1.4rem;
background: linear-gradient(135deg, #0f766e, #0f4c5c);
.header {
display: flex;
justify-content: space-between;
}
.balance {
gap: 1rem;
margin: 2rem 0 1rem 0;
display: flex;
align-items: center;
justify-content: end;
.value {
font-size: 2rem;
text-align: right;
font-weight: 700;
letter-spacing: .03em;
}
}
.id {
color: rgba(255, 255, 255, .72);
font-size: .75rem;
}
.time {
text-align: right;
}
}
.actions {
left: 1rem;
right: 1rem;
gap: .75rem;
z-index: 20;
bottom: calc(.75rem + env(safe-area-inset-bottom, 0));
display: flex;
position: fixed;
.t-button {
flex: 1;
}
}
.t-empty {
margin: 1rem;
}
}
:global(html.theme-dark) {
.point-account-detail {
.account-card {
box-shadow: 0 .75rem 2rem rgba(2, 6, 23, .32);
}
}
}
</style>