feat: 添加待确认收益和收益记录页面,更新路由和导航功能
This commit is contained in:
10
components.d.ts
vendored
10
components.d.ts
vendored
@@ -14,6 +14,10 @@ declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
IIcBaselineDataSaverOff: typeof import('~icons/ic/baseline-data-saver-off')['default']
|
||||
IIcBaselineDownloading: typeof import('~icons/ic/baseline-downloading')['default']
|
||||
IIcBaselineInfo: typeof import('~icons/ic/baseline-info')['default']
|
||||
IIcBaselineLightbulbOutline: typeof import('~icons/ic/baseline-lightbulb-outline')['default']
|
||||
IIcBaselineReceiptLong: typeof import('~icons/ic/baseline-receipt-long')['default']
|
||||
IIcOutlineAccessTime: typeof import('~icons/ic/outline-access-time')['default']
|
||||
IIcRoundArrowForwardIos: typeof import('~icons/ic/round-arrow-forward-ios')['default']
|
||||
IonApp: typeof import('@ionic/vue')['IonApp']
|
||||
IonAvatar: typeof import('@ionic/vue')['IonAvatar']
|
||||
@@ -21,6 +25,7 @@ declare module 'vue' {
|
||||
IonBadge: typeof import('@ionic/vue')['IonBadge']
|
||||
IonButton: typeof import('@ionic/vue')['IonButton']
|
||||
IonButtons: typeof import('@ionic/vue')['IonButtons']
|
||||
IonChip: typeof import('@ionic/vue')['IonChip']
|
||||
IonCol: typeof import('@ionic/vue')['IonCol']
|
||||
IonContent: typeof import('@ionic/vue')['IonContent']
|
||||
IonDatetime: typeof import('@ionic/vue')['IonDatetime']
|
||||
@@ -75,6 +80,10 @@ declare module 'vue' {
|
||||
declare global {
|
||||
const IIcBaselineDataSaverOff: typeof import('~icons/ic/baseline-data-saver-off')['default']
|
||||
const IIcBaselineDownloading: typeof import('~icons/ic/baseline-downloading')['default']
|
||||
const IIcBaselineInfo: typeof import('~icons/ic/baseline-info')['default']
|
||||
const IIcBaselineLightbulbOutline: typeof import('~icons/ic/baseline-lightbulb-outline')['default']
|
||||
const IIcBaselineReceiptLong: typeof import('~icons/ic/baseline-receipt-long')['default']
|
||||
const IIcOutlineAccessTime: typeof import('~icons/ic/outline-access-time')['default']
|
||||
const IIcRoundArrowForwardIos: typeof import('~icons/ic/round-arrow-forward-ios')['default']
|
||||
const IonApp: typeof import('@ionic/vue')['IonApp']
|
||||
const IonAvatar: typeof import('@ionic/vue')['IonAvatar']
|
||||
@@ -82,6 +91,7 @@ declare global {
|
||||
const IonBadge: typeof import('@ionic/vue')['IonBadge']
|
||||
const IonButton: typeof import('@ionic/vue')['IonButton']
|
||||
const IonButtons: typeof import('@ionic/vue')['IonButtons']
|
||||
const IonChip: typeof import('@ionic/vue')['IonChip']
|
||||
const IonCol: typeof import('@ionic/vue')['IonCol']
|
||||
const IonContent: typeof import('@ionic/vue')['IonContent']
|
||||
const IonDatetime: typeof import('@ionic/vue')['IonDatetime']
|
||||
|
||||
@@ -69,6 +69,16 @@ const routes: Array<RouteRecordRaw> = [
|
||||
component: () => import("@/views/revenue/monthly/index.vue"),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/revenue/pending",
|
||||
component: () => import("@/views/revenue/pending/index.vue"),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/revenue/records",
|
||||
component: () => import("@/views/revenue/records/index.vue"),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/wallet/bill",
|
||||
component: () => import("@/views/wallet/bill.vue"),
|
||||
|
||||
201
src/views/revenue/pending/index.vue
Normal file
201
src/views/revenue/pending/index.vue
Normal file
@@ -0,0 +1,201 @@
|
||||
<script lang='ts' setup>
|
||||
import type { RefresherCustomEvent } from "@ionic/vue";
|
||||
|
||||
const { t } = useI18n();
|
||||
const { vibrate } = useHaptics();
|
||||
|
||||
const loading = ref(true);
|
||||
|
||||
// 待确认收益数据
|
||||
const pendingItems = ref([
|
||||
{
|
||||
id: "1",
|
||||
type: "dividend",
|
||||
typeName: "分红收益",
|
||||
assetName: "迈阿密海景别墅",
|
||||
assetCode: "MIA-004",
|
||||
amount: 680.20,
|
||||
expectedDate: "2025-12-28",
|
||||
status: "pending",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
type: "appreciation",
|
||||
typeName: "资产增值",
|
||||
assetName: "波士顿商业中心",
|
||||
assetCode: "BOS-006",
|
||||
amount: 1250.50,
|
||||
expectedDate: "2025-12-29",
|
||||
status: "pending",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
type: "trade",
|
||||
typeName: "交易收益",
|
||||
assetName: "西雅图科技园区",
|
||||
assetCode: "SEA-007",
|
||||
amount: 890.30,
|
||||
expectedDate: "2025-12-30",
|
||||
status: "processing",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
type: "dividend",
|
||||
typeName: "分红收益",
|
||||
assetName: "达拉斯住宅区",
|
||||
assetCode: "DAL-008",
|
||||
amount: 520.80,
|
||||
expectedDate: "2025-12-30",
|
||||
status: "pending",
|
||||
},
|
||||
{
|
||||
id: "5",
|
||||
type: "appreciation",
|
||||
typeName: "资产增值",
|
||||
assetName: "奥斯汀高端公寓",
|
||||
assetCode: "AUS-009",
|
||||
amount: 1580.20,
|
||||
expectedDate: "2026-01-02",
|
||||
status: "processing",
|
||||
},
|
||||
]);
|
||||
|
||||
const totalPending = computed(() =>
|
||||
pendingItems.value.reduce((sum, item) => sum + item.amount, 0),
|
||||
);
|
||||
|
||||
async function loadData() {
|
||||
loading.value = true;
|
||||
useTimeoutFn(() => {
|
||||
loading.value = false;
|
||||
}, 800);
|
||||
}
|
||||
|
||||
async function handleRefresh(event: RefresherCustomEvent) {
|
||||
vibrate();
|
||||
useTimeoutFn(() => {
|
||||
event.target.complete();
|
||||
}, 800);
|
||||
}
|
||||
|
||||
function getStatusColor(status: string) {
|
||||
return status === "pending" ? "warning" : "medium";
|
||||
}
|
||||
|
||||
function getStatusText(status: string) {
|
||||
return status === "pending" ? "待确认" : "处理中";
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ion-page>
|
||||
<ion-header class="ion-no-border">
|
||||
<ion-toolbar class="ui-toolbar">
|
||||
<ui-back-button slot="start" />
|
||||
<ion-title>{{ t("asset.revenue.pendingRevenue") }}</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content :fullscreen="true" class="ion-padding">
|
||||
<ion-refresher slot="fixed" @ion-refresh="handleRefresh($event)">
|
||||
<ion-refresher-content />
|
||||
</ion-refresher>
|
||||
|
||||
<div class="container">
|
||||
<!-- 待确认总金额卡片 -->
|
||||
<div class="bg-text-900 rounded-xl p-6 mb-4">
|
||||
<div class="text-sm text-text-400 mb-2">
|
||||
待确认总金额
|
||||
</div>
|
||||
<div class="flex items-end gap-2">
|
||||
<div class="text-3xl font-bold">
|
||||
{{ formatAmountWithSplit(totalPending) }}
|
||||
</div>
|
||||
<div class="text-sm text-text-400 mb-1">
|
||||
USDT
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 mt-3 text-xs text-text-400">
|
||||
<i-ic-baseline-info class="text-base" />
|
||||
<span>收益将在预计日期后1-3个工作日内到账</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 待确认收益列表 -->
|
||||
<div class="mb-3">
|
||||
<div class="text-base font-medium mb-3">
|
||||
待确认明细
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ion-list class="rounded-xl overflow-hidden">
|
||||
<ion-item v-for="item in pendingItems" :key="item.id" lines="full">
|
||||
<div class="w-full py-4">
|
||||
<div class="flex justify-between items-start mb-3">
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="text-sm font-medium">
|
||||
{{ item.typeName }}
|
||||
</div>
|
||||
<ion-badge
|
||||
:color="getStatusColor(item.status)"
|
||||
class="text-xs px-2 py-0.5"
|
||||
>
|
||||
{{ getStatusText(item.status) }}
|
||||
</ion-badge>
|
||||
</div>
|
||||
<div class="text-base font-semibold">
|
||||
{{ item.assetName }}
|
||||
</div>
|
||||
<div class="text-xs text-text-400">
|
||||
{{ item.assetCode }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div class="text-lg font-bold">
|
||||
+{{ formatAmountWithSplit(item.amount) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 text-xs text-text-400">
|
||||
<i-ic-outline-access-time class="text-sm" />
|
||||
<span>预计到账:{{ item.expectedDate }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
|
||||
<!-- 说明信息 -->
|
||||
<div class="mt-6 p-4 bg-text-950 rounded-xl">
|
||||
<div class="flex items-start gap-2">
|
||||
<!-- <i-ic-baseline-lightbulb-outline class="text-yellow-500 text-lg mt-0.5 flex-shrink-0" /> -->
|
||||
<div class="text-xs text-text-400 leading-relaxed">
|
||||
<div class="font-medium mb-2">
|
||||
待确认收益说明:
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<div>• 分红收益:预计在分红日后2-3个工作日到账</div>
|
||||
<div>• 资产增值:预计在结算日后1-2个工作日到账</div>
|
||||
<div>• 交易收益:预计在交易完成后1个工作日到账</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</template>
|
||||
|
||||
<style lang='css' scoped>
|
||||
.container {
|
||||
--title-font-size: 16px;
|
||||
}
|
||||
|
||||
ion-list {
|
||||
background: transparent;
|
||||
}
|
||||
</style>
|
||||
287
src/views/revenue/records/index.vue
Normal file
287
src/views/revenue/records/index.vue
Normal file
@@ -0,0 +1,287 @@
|
||||
<script lang='ts' setup>
|
||||
import type { RefresherCustomEvent } from "@ionic/vue";
|
||||
import { searchOutline } from "ionicons/icons";
|
||||
|
||||
const { t } = useI18n();
|
||||
const { vibrate } = useHaptics();
|
||||
|
||||
const loading = ref(true);
|
||||
const selectedType = ref("all");
|
||||
const searchQuery = ref("");
|
||||
|
||||
// 收益类型选项
|
||||
const typeOptions = [
|
||||
{ value: "all", label: "全部" },
|
||||
{ value: "dividend", label: "分红收益" },
|
||||
{ value: "appreciation", label: "资产增值" },
|
||||
{ value: "trade", label: "交易收益" },
|
||||
];
|
||||
|
||||
// 收益记录数据
|
||||
const allRecords = ref([
|
||||
{
|
||||
id: "1",
|
||||
type: "dividend",
|
||||
typeName: "分红收益",
|
||||
assetName: "纽约曼哈顿中心公寓",
|
||||
assetCode: "NYC-001",
|
||||
amount: 520.50,
|
||||
date: "2025-12-27 10:30",
|
||||
status: "completed",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
type: "appreciation",
|
||||
typeName: "资产增值",
|
||||
assetName: "旧金山商业地产",
|
||||
assetCode: "SF-002",
|
||||
amount: 320.80,
|
||||
date: "2025-12-27 09:15",
|
||||
status: "completed",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
type: "trade",
|
||||
typeName: "交易收益",
|
||||
assetName: "洛杉矶住宅楼",
|
||||
assetCode: "LA-003",
|
||||
amount: 215.50,
|
||||
date: "2025-12-26 16:45",
|
||||
status: "completed",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
type: "dividend",
|
||||
typeName: "分红收益",
|
||||
assetName: "迈阿密海景别墅",
|
||||
assetCode: "MIA-004",
|
||||
amount: 680.20,
|
||||
date: "2025-12-26 14:20",
|
||||
status: "completed",
|
||||
},
|
||||
{
|
||||
id: "5",
|
||||
type: "appreciation",
|
||||
typeName: "资产增值",
|
||||
assetName: "芝加哥写字楼",
|
||||
assetCode: "CHI-005",
|
||||
amount: 450.60,
|
||||
date: "2025-12-25 11:30",
|
||||
status: "completed",
|
||||
},
|
||||
{
|
||||
id: "6",
|
||||
type: "trade",
|
||||
typeName: "交易收益",
|
||||
assetName: "波士顿商业中心",
|
||||
assetCode: "BOS-006",
|
||||
amount: 890.30,
|
||||
date: "2025-12-24 15:20",
|
||||
status: "completed",
|
||||
},
|
||||
{
|
||||
id: "7",
|
||||
type: "dividend",
|
||||
typeName: "分红收益",
|
||||
assetName: "西雅图科技园区",
|
||||
assetCode: "SEA-007",
|
||||
amount: 720.40,
|
||||
date: "2025-12-23 13:10",
|
||||
status: "completed",
|
||||
},
|
||||
{
|
||||
id: "8",
|
||||
type: "appreciation",
|
||||
typeName: "资产增值",
|
||||
assetName: "达拉斯住宅区",
|
||||
assetCode: "DAL-008",
|
||||
amount: 380.90,
|
||||
date: "2025-12-22 10:50",
|
||||
status: "completed",
|
||||
},
|
||||
{
|
||||
id: "9",
|
||||
type: "trade",
|
||||
typeName: "交易收益",
|
||||
assetName: "奥斯汀高端公寓",
|
||||
assetCode: "AUS-009",
|
||||
amount: 1250.20,
|
||||
date: "2025-12-21 16:30",
|
||||
status: "completed",
|
||||
},
|
||||
{
|
||||
id: "10",
|
||||
type: "dividend",
|
||||
typeName: "分红收益",
|
||||
assetName: "休斯顿商业综合体",
|
||||
assetCode: "HOU-010",
|
||||
amount: 580.70,
|
||||
date: "2025-12-20 14:15",
|
||||
status: "completed",
|
||||
},
|
||||
]);
|
||||
|
||||
// 筛选后的记录
|
||||
const filteredRecords = computed(() => {
|
||||
let records = allRecords.value;
|
||||
|
||||
// 按类型筛选
|
||||
if (selectedType.value !== "all") {
|
||||
records = records.filter(item => item.type === selectedType.value);
|
||||
}
|
||||
|
||||
// 按搜索关键词筛选
|
||||
if (searchQuery.value.trim()) {
|
||||
const query = searchQuery.value.toLowerCase();
|
||||
records = records.filter(item =>
|
||||
item.assetName.toLowerCase().includes(query)
|
||||
|| item.assetCode.toLowerCase().includes(query)
|
||||
|| item.typeName.toLowerCase().includes(query),
|
||||
);
|
||||
}
|
||||
|
||||
return records;
|
||||
});
|
||||
|
||||
async function loadData() {
|
||||
loading.value = true;
|
||||
useTimeoutFn(() => {
|
||||
loading.value = false;
|
||||
}, 800);
|
||||
}
|
||||
|
||||
async function handleRefresh(event: RefresherCustomEvent) {
|
||||
vibrate();
|
||||
useTimeoutFn(() => {
|
||||
event.target.complete();
|
||||
}, 800);
|
||||
}
|
||||
|
||||
function getTypeColor(type: string) {
|
||||
const colors: Record<string, string> = {
|
||||
dividend: "success",
|
||||
appreciation: "primary",
|
||||
trade: "warning",
|
||||
};
|
||||
return colors[type] || "medium";
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ion-page>
|
||||
<ion-header class="ion-no-border">
|
||||
<ion-toolbar class="ui-toolbar">
|
||||
<ui-back-button slot="start" />
|
||||
<ion-title>{{ t("asset.revenue.revenueDetails") }}</ion-title>
|
||||
</ion-toolbar>
|
||||
<ion-searchbar
|
||||
v-model="searchQuery"
|
||||
:placeholder="t('myIssues.search')"
|
||||
/>
|
||||
</ion-header>
|
||||
|
||||
<ion-content :fullscreen="true" class="ion-padding-horizontal">
|
||||
<ion-refresher slot="fixed" @ion-refresh="handleRefresh($event)">
|
||||
<ion-refresher-content />
|
||||
</ion-refresher>
|
||||
|
||||
<!-- 类型筛选 -->
|
||||
<div class="flex gap-2 mb-4 overflow-x-auto pb-2">
|
||||
<ui-tabs size="small">
|
||||
<ui-tab-pane
|
||||
v-for="option in typeOptions"
|
||||
:key="option.value"
|
||||
:name="option.value"
|
||||
:title="option.label"
|
||||
/>
|
||||
</ui-tabs>
|
||||
</div>
|
||||
|
||||
<!-- 收益记录列表 -->
|
||||
<ion-list v-if="filteredRecords.length > 0" class="rounded-xl overflow-hidden">
|
||||
<ion-item
|
||||
v-for="item in filteredRecords"
|
||||
:key="item.id"
|
||||
lines="full"
|
||||
class="record-item"
|
||||
>
|
||||
<div class="w-full py-3">
|
||||
<div class="flex justify-between items-start">
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<ion-badge
|
||||
:color="getTypeColor(item.type)"
|
||||
class="text-xs px-2 py-0.5"
|
||||
>
|
||||
{{ item.typeName }}
|
||||
</ion-badge>
|
||||
<ion-badge color="success" class="text-xs px-2 py-0.5">
|
||||
已完成
|
||||
</ion-badge>
|
||||
</div>
|
||||
<div class="text-base font-semibold mb-1">
|
||||
{{ item.assetName }}
|
||||
</div>
|
||||
<div class="text-xs text-text-400 mb-2">
|
||||
{{ item.assetCode }}
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 text-xs text-text-400">
|
||||
<i-ic-outline-access-time class="text-sm" />
|
||||
<span>{{ item.date }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right ml-4">
|
||||
<div class="text-lg font-bold text-success">
|
||||
+{{ formatAmountWithSplit(item.amount) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<div v-else class="flex flex-col items-center justify-center py-20">
|
||||
<i-ic-baseline-receipt-long class="text-6xl text-text-600 mb-4" />
|
||||
<div class="text-text-400">
|
||||
{{ t("income.records.noData") }}
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</template>
|
||||
|
||||
<style lang='css' scoped>
|
||||
.container {
|
||||
--title-font-size: 16px;
|
||||
}
|
||||
|
||||
ion-list {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.custom-searchbar {
|
||||
--background: var(--ion-color-step-100);
|
||||
--border-radius: 12px;
|
||||
--box-shadow: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ion-chip {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
ion-chip:active {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.record-item {
|
||||
--inner-padding-end: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -11,6 +11,14 @@ function navigateToTotal() {
|
||||
function navigateToMonthly() {
|
||||
router.push("/revenue/monthly");
|
||||
}
|
||||
|
||||
function navigateToPending() {
|
||||
router.push("/revenue/pending");
|
||||
}
|
||||
|
||||
function navigateToRecords() {
|
||||
router.push("/revenue/records");
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -37,13 +45,19 @@ function navigateToMonthly() {
|
||||
{{ t("asset.revenue.monthlyRevenue") }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-span-1 flex flex-col items-center gap-2 cursor-pointer transition-opacity active:opacity-70">
|
||||
<div
|
||||
class="col-span-1 flex flex-col items-center gap-2 cursor-pointer transition-opacity active:opacity-70"
|
||||
@click="navigateToPending"
|
||||
>
|
||||
<ion-icon :icon="timeOutline" class="text-2xl text-primary" />
|
||||
<div class="text-xs">
|
||||
{{ t("asset.revenue.pendingRevenue") }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-span-1 flex flex-col items-center gap-2 cursor-pointer transition-opacity active:opacity-70">
|
||||
<div
|
||||
class="col-span-1 flex flex-col items-center gap-2 cursor-pointer transition-opacity active:opacity-70"
|
||||
@click="navigateToRecords"
|
||||
>
|
||||
<ion-icon :icon="listOutline" class="text-2xl text-primary" />
|
||||
<div class="text-xs">
|
||||
{{ t("asset.revenue.revenueDetails") }}
|
||||
|
||||
Reference in New Issue
Block a user