feat(web): 成员端同构三页与日志详情,管理端轻量筛选
成员视图改管理端壳(总览/调用日志/支付参数),行内详情弹窗含原始数据; 成员日志含错误行;共享时间范围。管理端成员/支付筛选;支付仅留类型筛选。
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* 成员支付参数(交互设计-成员视图 §5):完全公开的登记参数表
|
||||
*/
|
||||
import { computed } from "vue";
|
||||
import PageHeader from "@/components/common/page-header.vue";
|
||||
import TimeRangePicker from "@/components/common/time-range-picker.vue";
|
||||
import QuotaText from "@/components/common/quota-text.vue";
|
||||
import { useMemberRange } from "@/composables/use-member-range.ts";
|
||||
import { useMemberOverview } from "@/queries/member.ts";
|
||||
import { formatDateTime } from "@/utils/time.ts";
|
||||
import { formatPaidAmount } from "@/utils/format.ts";
|
||||
|
||||
const { range, onRangeChange } = useMemberRange();
|
||||
const overviewQuery = useMemberOverview(() => range.value);
|
||||
const payments = computed(() => overviewQuery.data.value?.payments ?? []);
|
||||
|
||||
const extraPresets = computed(() =>
|
||||
payments.value
|
||||
.filter((p) => p.kind === "subscription" && p.period_start != null)
|
||||
.map((p) => ({
|
||||
label: `订阅 #${p.subscription_id ?? p.id} 期间`,
|
||||
from: new Date((p.period_start ?? 0) * 1000).toISOString(),
|
||||
to: new Date((p.period_end ?? Math.floor(Date.now() / 1000)) * 1000).toISOString(),
|
||||
}))
|
||||
.slice(0, 4),
|
||||
);
|
||||
|
||||
function periodText(p: { kind: string; paid_at: number; period_start: number | null; period_end: number | null }): string {
|
||||
if (p.kind !== "subscription") return "—";
|
||||
const end = p.period_end == null ? "进行中" : formatDateTime(p.period_end);
|
||||
return `${formatDateTime(p.period_start)} ~ ${end}`;
|
||||
}
|
||||
|
||||
const columns = [
|
||||
{ title: "类型", key: "kind", width: 80 },
|
||||
{ title: "编号 / 时间", key: "id_or_time", width: 160 },
|
||||
{ title: "期间", key: "period", width: 220 },
|
||||
{ title: "额度", key: "quota", width: 160 },
|
||||
{ title: "金额", key: "amount", width: 100 },
|
||||
{ title: "备注", key: "remark" },
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section>
|
||||
<PageHeader
|
||||
title="支付参数"
|
||||
subtitle="订阅与充值的登记参数 · 对成员完全公开"
|
||||
>
|
||||
<template #actions>
|
||||
<span class="mv-range-label">时间范围</span>
|
||||
<TimeRangePicker
|
||||
:model-value="range"
|
||||
:extra-presets="extraPresets"
|
||||
@change="onRangeChange"
|
||||
/>
|
||||
</template>
|
||||
</PageHeader>
|
||||
|
||||
<a-alert
|
||||
v-if="overviewQuery.isError.value"
|
||||
type="error"
|
||||
show-icon
|
||||
message="支付参数加载失败"
|
||||
:description="overviewQuery.error.value?.message"
|
||||
style="margin-bottom: 16px"
|
||||
>
|
||||
<template #action>
|
||||
<a-button size="small" @click="() => overviewQuery.refetch()">重试</a-button>
|
||||
</template>
|
||||
</a-alert>
|
||||
|
||||
<a-skeleton
|
||||
v-if="overviewQuery.isPending.value"
|
||||
active
|
||||
:paragraph="{ rows: 4 }"
|
||||
/>
|
||||
<a-empty
|
||||
v-else-if="!overviewQuery.isError.value && payments.length === 0"
|
||||
description="暂无支付记录信息"
|
||||
/>
|
||||
<template v-else-if="payments.length > 0">
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="payments"
|
||||
:row-key="(r: { id: number }) => r.id"
|
||||
size="middle"
|
||||
:pagination="false"
|
||||
:scroll="{ x: 860 }"
|
||||
:loading="overviewQuery.isFetching.value"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'kind'">
|
||||
<a-tag :color="record.kind === 'subscription' ? 'blue' : 'gold'">
|
||||
{{ record.kind === "subscription" ? "订阅" : "充值" }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'id_or_time'">
|
||||
<template v-if="record.kind === 'subscription'">
|
||||
订阅 #{{ record.subscription_id ?? record.id }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ formatDateTime(record.paid_at) }}
|
||||
</template>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'period'">
|
||||
{{ periodText(record) }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'quota'">
|
||||
<QuotaText
|
||||
v-if="record.quota_granted != null"
|
||||
:quota="record.quota_granted"
|
||||
:quota-usd="record.quota_usd"
|
||||
/>
|
||||
<span v-else>—</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'amount'">
|
||||
{{ formatPaidAmount(record.paid_amount) }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'remark'">
|
||||
{{ record.remark || "—" }}
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<div class="mv-foot">
|
||||
支付参数对成员完全公开;币种由成员与作者约定
|
||||
</div>
|
||||
</template>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.mv-range-label {
|
||||
font-size: 13px;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
}
|
||||
|
||||
.mv-foot {
|
||||
margin-top: 16px;
|
||||
font-size: 12px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user