Files

234 lines
7.2 KiB
Plaintext

<template>
<view class="credit-records-container">
<!-- 顶部导航栏 -->
<custom-nav-bar :title="t('Mobile.CreditRecords.pageTitle')">
<template v-slot:left>
<view class="header-icon-box" @tap="jumpNavigateBack">
<text class="iconfont icon-a-Chevronleft"></text>
</view>
</template>
</custom-nav-bar>
<!-- 分类筛选横向滑动 Tab 栏 -->
<scroll-view
direction="horizontal"
class="filter-tabs-bar"
:show-scrollbar="false"
>
<view
v-for="tab in filterTabs"
:key="tab.value"
class="filter-tab-item"
:class="{ active: currentType === tab.value }"
@tap="handleSelectType(tab.value)"
>
<text class="tab-label" :class="{ active: currentType === tab.value }">
{{ t(tab.label) }}
</text>
</view>
</scroll-view>
<!-- 列表展示区域,使用 scroll-view 支持下拉刷新和上拉触底加载 -->
<scroll-view
scroll-y
class="list-scroll-view"
:refresher-enabled="true"
:refresher-triggered="refreshing"
@refresherrefresh="handleRefresh"
@scrolltolower="handleLoadMore"
>
<view v-if="list.length > 0" class="list-content">
<view v-for="item in list" :key="item.id" class="record-card">
<!-- 左侧流水描述、时间以及增加/扣减标签 -->
<view class="card-left">
<text class="record-name">
{{
item.remark != null && item.remark !== ""
? item.remark
: item.creditTypeName
}}
</text>
<text class="record-time">
{{ formatDate(item.created, "YYYY-MM-DD HH:mm:ss") }}
</text>
<view class="badge-container">
<view
class="record-badge"
:class="
item.operationType === 1 ? 'badge-increase' : 'badge-decrease'
"
>
<text
class="badge-text"
:class="
item.operationType === 1 ? 'text-increase' : 'text-decrease'
"
>
{{
item.operationType === 1
? t("Mobile.CreditRecords.badgeIncrease")
: t("Mobile.CreditRecords.badgeDecrease")
}}
</text>
</view>
</view>
</view>
<!-- 右侧变化额度与变动后积分余额 -->
<view class="card-right">
<text
class="record-amount"
:class="{
'amount-increase': item.operationType === 1,
'amount-decrease': item.operationType === 2,
}"
>
{{ item.operationType === 1 ? "+" : "-"
}}{{ formatNumber(item.amount, 2) }}
</text>
<text class="record-balance">
{{
t("Mobile.CreditRecords.remaining") +
" " +
formatNumber(item.afterAmount, 2)
}}
</text>
</view>
</view>
<!-- 触底加载更多 Loading 提示 -->
<view v-if="loading && !refreshing" class="loading-state">
<text class="loading-text">{{ t("Mobile.Page.loadingMore") }}</text>
</view>
<!-- 已加载完毕提示 -->
<view v-if="!hasMore && list.length > 0" class="no-more-state">
<text class="no-more-text">{{ t("Mobile.Common.noMoreData") }}</text>
</view>
</view>
<!-- 空数据状态 -->
<view v-else-if="!loading" class="empty-wrapper">
<empty-state text="Mobile.CreditRecords.empty" />
</view>
<!-- 首次加载的 loading -->
<view v-else class="loading-state">
<text class="loading-text">{{ t("Mobile.Page.loadingMore") }}</text>
</view>
</scroll-view>
</view>
</template>
<script setup lang="uts">
import type { CreditRecordInfo } from "@/subpackages/types/interfaces/subscription";
import { SUCCESS_CODE } from "@/constants/codes.constants.uts";
import { apiGetCreditFlows } from "@/subpackages/servers/subscription";
import { jumpNavigateBack } from "@/utils/common";
import { formatDate, formatNumber } from "@/utils/system";
import { useI18n } from "@/utils/i18n";
import EmptyState from "@/components/empty-state/empty-state.uvue";
const { t } = useI18n();
// 筛选 Tab 配置词条
interface TabOption {
label: string;
value: string;
}
const filterTabs: TabOption[] = [
{ label: "Mobile.Common.all", value: "" },
{ label: "Mobile.CreditRecords.typeSubscription", value: "SUBSCRIPTION" },
{ label: "Mobile.CreditRecords.typePurchase", value: "PURCHASE" },
{ label: "Mobile.CreditRecords.typeActivity", value: "ACTIVITY" },
{ label: "Mobile.CreditRecords.typeManual", value: "MANUAL" },
{ label: "Mobile.CreditRecords.typeModelCall", value: "MODEL_CALL" },
{ label: "Mobile.CreditRecords.typeAgentCall", value: "AGENT_CALL" },
{ label: "Mobile.CreditRecords.typeToolCall", value: "TOOL_CALL" },
{ label: "Mobile.CreditRecords.typeManualDeduct", value: "MANUAL_DEDUCT" },
];
// 状态属性定义
const list = ref<CreditRecordInfo[]>([]);
const loading = ref<boolean>(false);
const refreshing = ref<boolean>(false);
const currentType = ref<string>("");
const lastId = ref<number | null>(null);
const hasMore = ref<boolean>(true);
// 获取积分明细数据
const fetchPageData = async (isFirst: boolean) => {
if (loading.value) return;
loading.value = true;
try {
const params: Record<string, any> = {
pageSize: 30,
};
if (currentType.value !== "") {
params["creditType"] = currentType.value;
}
if (!isFirst && lastId.value != null) {
params["lastId"] = lastId.value;
}
const res = await apiGetCreditFlows(params);
if (res.code === SUCCESS_CODE && res.data != null) {
const newData = res.data!;
if (isFirst) {
list.value = newData;
} else {
list.value = [...list.value, ...newData];
}
if (newData.length > 0) {
lastId.value = newData[newData.length - 1].id;
}
hasMore.value = newData.length === 30;
}
} catch (e) {
console.error("加载积分明细异常:", e);
} finally {
loading.value = false;
refreshing.value = false;
}
};
// 下拉刷新事件
const handleRefresh = () => {
refreshing.value = true;
lastId.value = null;
hasMore.value = true;
fetchPageData(true);
};
// 上拉触底加载
const handleLoadMore = () => {
if (!hasMore.value || loading.value) return;
fetchPageData(false);
};
// 切换筛选类型
const handleSelectType = (value: string) => {
if (currentType.value === value) return;
currentType.value = value;
lastId.value = null;
hasMore.value = true;
list.value = [];
fetchPageData(true);
};
// 页面生命周期载入
onLoad(() => {
fetchPageData(true);
});
</script>
<style lang="scss" scoped>
@import "./styles/index.scss";
</style>