feat: 添加收益模块,包含总收益概览、收益趋势和收益来源,更新相关组件和路由
This commit is contained in:
5
auto-imports.d.ts
vendored
5
auto-imports.d.ts
vendored
@@ -41,7 +41,7 @@ declare global {
|
||||
const effectScope: typeof import('vue').effectScope
|
||||
const emailPattern: typeof import('./src/utils/pattern').emailPattern
|
||||
const extendRef: typeof import('@vueuse/core').extendRef
|
||||
const formatAmount: typeof import('./src/utils/helper').formatAmount
|
||||
const formatAmountWithSplit: typeof import('./src/utils/helper').formatAmountWithSplit
|
||||
const formatAmountWithUnit: typeof import('./src/utils/helper').formatAmountWithUnit
|
||||
const formatBalance: typeof import('./src/utils/helper').formatBalance
|
||||
const getActivePinia: typeof import('pinia').getActivePinia
|
||||
@@ -405,7 +405,8 @@ declare module 'vue' {
|
||||
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
|
||||
readonly emailPattern: UnwrapRef<typeof import('./src/utils/pattern')['emailPattern']>
|
||||
readonly extendRef: UnwrapRef<typeof import('@vueuse/core')['extendRef']>
|
||||
readonly formatAmount: UnwrapRef<typeof import('./src/utils/helper')['formatAmount']>
|
||||
readonly formatAmountWithSplit: UnwrapRef<typeof import('./src/utils/helper')['formatAmountWithSplit']>
|
||||
readonly formatAmountWithUnit: UnwrapRef<typeof import('./src/utils/helper')['formatAmountWithUnit']>
|
||||
readonly formatBalance: UnwrapRef<typeof import('./src/utils/helper')['formatBalance']>
|
||||
readonly getActivePinia: UnwrapRef<typeof import('pinia')['getActivePinia']>
|
||||
readonly getCacheRemainingTime: UnwrapRef<typeof import('./src/composables/useStorageCache')['getCacheRemainingTime']>
|
||||
|
||||
6
components.d.ts
vendored
6
components.d.ts
vendored
@@ -39,6 +39,9 @@ declare module 'vue' {
|
||||
IonLabel: typeof import('@ionic/vue')['IonLabel']
|
||||
IonList: typeof import('@ionic/vue')['IonList']
|
||||
IonListHeader: typeof import('@ionic/vue')['IonListHeader']
|
||||
IonMenu: typeof import('@ionic/vue')['IonMenu']
|
||||
IonMenuButton: typeof import('@ionic/vue')['IonMenuButton']
|
||||
IonMenuToggle: typeof import('@ionic/vue')['IonMenuToggle']
|
||||
IonModal: typeof import('@ionic/vue')['IonModal']
|
||||
IonNote: typeof import('@ionic/vue')['IonNote']
|
||||
IonPage: typeof import('@ionic/vue')['IonPage']
|
||||
@@ -97,6 +100,9 @@ declare global {
|
||||
const IonLabel: typeof import('@ionic/vue')['IonLabel']
|
||||
const IonList: typeof import('@ionic/vue')['IonList']
|
||||
const IonListHeader: typeof import('@ionic/vue')['IonListHeader']
|
||||
const IonMenu: typeof import('@ionic/vue')['IonMenu']
|
||||
const IonMenuButton: typeof import('@ionic/vue')['IonMenuButton']
|
||||
const IonMenuToggle: typeof import('@ionic/vue')['IonMenuToggle']
|
||||
const IonModal: typeof import('@ionic/vue')['IonModal']
|
||||
const IonNote: typeof import('@ionic/vue')['IonNote']
|
||||
const IonPage: typeof import('@ionic/vue')['IonPage']
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"@elysiajs/eden": "^1.4.5",
|
||||
"@ionic/vue": "^8.7.11",
|
||||
"@ionic/vue-router": "^8.7.11",
|
||||
"@riwa/api-types": "http://192.168.1.27:9527/api/riwa-api-types-0.0.64.tgz",
|
||||
"@riwa/api-types": "http://192.168.1.27:9527/api/riwa-api-types-0.0.67.tgz",
|
||||
"@tailwindcss/vite": "^4.1.18",
|
||||
"@vee-validate/yup": "^4.15.1",
|
||||
"@vueuse/core": "^14.1.0",
|
||||
|
||||
12
pnpm-lock.yaml
generated
12
pnpm-lock.yaml
generated
@@ -57,8 +57,8 @@ importers:
|
||||
specifier: ^8.7.11
|
||||
version: 8.7.11(@stencil/core@4.39.0)(vue-router@4.6.3(vue@3.5.25(typescript@5.9.3)))(vue@3.5.25(typescript@5.9.3))
|
||||
'@riwa/api-types':
|
||||
specifier: http://192.168.1.27:9527/api/riwa-api-types-0.0.64.tgz
|
||||
version: http://192.168.1.27:9527/api/riwa-api-types-0.0.64.tgz(@elysiajs/eden@1.4.5(elysia@1.4.18(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3)))
|
||||
specifier: http://192.168.1.27:9527/api/riwa-api-types-0.0.67.tgz
|
||||
version: http://192.168.1.27:9527/api/riwa-api-types-0.0.67.tgz(@elysiajs/eden@1.4.5(elysia@1.4.18(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3)))
|
||||
'@tailwindcss/vite':
|
||||
specifier: ^4.1.18
|
||||
version: 4.1.18(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
||||
@@ -1401,9 +1401,9 @@ packages:
|
||||
resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==}
|
||||
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
|
||||
|
||||
'@riwa/api-types@http://192.168.1.27:9527/api/riwa-api-types-0.0.64.tgz':
|
||||
resolution: {tarball: http://192.168.1.27:9527/api/riwa-api-types-0.0.64.tgz}
|
||||
version: 0.0.64
|
||||
'@riwa/api-types@http://192.168.1.27:9527/api/riwa-api-types-0.0.67.tgz':
|
||||
resolution: {tarball: http://192.168.1.27:9527/api/riwa-api-types-0.0.67.tgz}
|
||||
version: 0.0.67
|
||||
peerDependencies:
|
||||
'@elysiajs/eden': ^1.4.5
|
||||
|
||||
@@ -6644,7 +6644,7 @@ snapshots:
|
||||
|
||||
'@pkgr/core@0.2.9': {}
|
||||
|
||||
'@riwa/api-types@http://192.168.1.27:9527/api/riwa-api-types-0.0.64.tgz(@elysiajs/eden@1.4.5(elysia@1.4.18(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3)))':
|
||||
'@riwa/api-types@http://192.168.1.27:9527/api/riwa-api-types-0.0.67.tgz(@elysiajs/eden@1.4.5(elysia@1.4.18(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3)))':
|
||||
dependencies:
|
||||
'@elysiajs/eden': 1.4.5(elysia@1.4.18(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3))
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import type { Awaitable } from "@vueuse/core";
|
||||
import type { ChartOptions, DeepPartial, IChartApi, ISeriesApi, LayoutOptions, OhlcData, SeriesType } from "lightweight-charts";
|
||||
import type { ChartOptions, DeepPartial, IChartApi, ISeriesApi, LayoutOptions, OhlcData, SeriesDataItemTypeMap, SeriesType } from "lightweight-charts";
|
||||
import type { ThemeMode } from "./useTheme";
|
||||
import { AreaSeries, BarSeries, BaselineSeries, CandlestickSeries, ColorType, createChart, HistogramSeries, LineSeries } from "lightweight-charts";
|
||||
import { mergeWith } from "lodash-es";
|
||||
|
||||
export type Series = "Area" | "Bar" | "Baseline" | "Candlestick" | "Histogram" | "Line";
|
||||
|
||||
export type TData = OhlcData;
|
||||
export type TData = SeriesDataItemTypeMap[SeriesType];
|
||||
|
||||
export type WeightChartOptions = DeepPartial<ChartOptions>;
|
||||
|
||||
|
||||
@@ -241,6 +241,44 @@
|
||||
"revenueDetails": "Revenue Details"
|
||||
}
|
||||
},
|
||||
"income": {
|
||||
"title": "Total Revenue",
|
||||
"overview": {
|
||||
"totalRevenue": "Total Revenue",
|
||||
"yesterdayRevenue": "Yesterday's Revenue",
|
||||
"monthRevenue": "This Month",
|
||||
"pendingRevenue": "Pending Revenue"
|
||||
},
|
||||
"trend": {
|
||||
"title": "Revenue Trend",
|
||||
"last7Days": "Last 7 Days",
|
||||
"last30Days": "Last 30 Days",
|
||||
"last90Days": "Last 90 Days"
|
||||
},
|
||||
"sources": {
|
||||
"title": "Revenue Sources",
|
||||
"dividend": "Dividend",
|
||||
"appreciation": "Appreciation",
|
||||
"trade": "Trading"
|
||||
},
|
||||
"records": {
|
||||
"title": "Revenue Records",
|
||||
"all": "All",
|
||||
"recent": "Recent Records",
|
||||
"viewAll": "View All",
|
||||
"assetName": "Asset Name",
|
||||
"type": "Type",
|
||||
"amount": "Amount",
|
||||
"date": "Date",
|
||||
"status": "Status",
|
||||
"noData": "No revenue records"
|
||||
},
|
||||
"status": {
|
||||
"completed": "Completed",
|
||||
"pending": "Pending",
|
||||
"processing": "Processing"
|
||||
}
|
||||
},
|
||||
"myIssues": {
|
||||
"title": "My Issuance Applications",
|
||||
"search": "Search",
|
||||
@@ -317,7 +355,10 @@
|
||||
"common": {
|
||||
"failedSendCode": "Failed to send verification code",
|
||||
"uploadFile": "Upload File",
|
||||
"files": "files"
|
||||
"files": "files",
|
||||
"today": "Today",
|
||||
"yesterday": "Yesterday",
|
||||
"items": "items"
|
||||
},
|
||||
"auth": {
|
||||
"login": {
|
||||
|
||||
@@ -247,6 +247,44 @@
|
||||
"revenueDetails": "收益明细"
|
||||
}
|
||||
},
|
||||
"income": {
|
||||
"title": "总收益",
|
||||
"overview": {
|
||||
"totalRevenue": "累计总收益",
|
||||
"yesterdayRevenue": "昨日收益",
|
||||
"monthRevenue": "本月收益",
|
||||
"pendingRevenue": "待确认收益"
|
||||
},
|
||||
"trend": {
|
||||
"title": "收益趋势",
|
||||
"last7Days": "最近7天",
|
||||
"last30Days": "最近30天",
|
||||
"last90Days": "最近90天"
|
||||
},
|
||||
"sources": {
|
||||
"title": "收益来源",
|
||||
"dividend": "分红收益",
|
||||
"appreciation": "资产增值",
|
||||
"trade": "交易收益"
|
||||
},
|
||||
"records": {
|
||||
"title": "收益明细",
|
||||
"all": "全部",
|
||||
"recent": "最近记录",
|
||||
"viewAll": "查看全部",
|
||||
"assetName": "资产名称",
|
||||
"type": "类型",
|
||||
"amount": "金额",
|
||||
"date": "日期",
|
||||
"status": "状态",
|
||||
"noData": "暂无收益记录"
|
||||
},
|
||||
"status": {
|
||||
"completed": "已完成",
|
||||
"pending": "待确认",
|
||||
"processing": "处理中"
|
||||
}
|
||||
},
|
||||
"myIssues": {
|
||||
"title": "我的发行申请",
|
||||
"search": "搜索",
|
||||
@@ -323,7 +361,10 @@
|
||||
"common": {
|
||||
"failedSendCode": "发送验证码失败",
|
||||
"uploadFile": "上传文件",
|
||||
"files": "个文件"
|
||||
"files": "个文件",
|
||||
"today": "今天",
|
||||
"yesterday": "昨天",
|
||||
"items": "项"
|
||||
},
|
||||
"auth": {
|
||||
"login": {
|
||||
|
||||
129
src/mocks/data/income.ts
Normal file
129
src/mocks/data/income.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import { useMocks } from "../index";
|
||||
|
||||
const mocks = useMocks();
|
||||
|
||||
// 总收益 Mock 数据
|
||||
mocks.register("income/total", () => {
|
||||
return {
|
||||
// 总收益概览
|
||||
overview: {
|
||||
totalRevenue: 125680.50, // 累计总收益
|
||||
yesterdayRevenue: 1256.80, // 昨日收益
|
||||
monthRevenue: 38450.20, // 本月收益
|
||||
pendingRevenue: 5420.30, // 待确认收益
|
||||
currency: "USD",
|
||||
},
|
||||
// 收益趋势数据(最近7天)
|
||||
trend: [
|
||||
{ date: "2025-12-21", revenue: 1100.50 },
|
||||
{ date: "2025-12-22", revenue: 1300.80 },
|
||||
{ date: "2025-12-23", revenue: 1450.20 },
|
||||
{ date: "2025-12-24", revenue: 1200.60 },
|
||||
{ date: "2025-12-25", revenue: 1350.90 },
|
||||
{ date: "2025-12-26", revenue: 1380.40 },
|
||||
{ date: "2025-12-27", revenue: 1256.80 },
|
||||
],
|
||||
// 收益来源分布
|
||||
sources: [
|
||||
{
|
||||
type: "dividend", // 分红收益
|
||||
name: "分红收益",
|
||||
amount: 85420.30,
|
||||
percentage: 67.96,
|
||||
count: 125,
|
||||
},
|
||||
{
|
||||
type: "appreciation", // 资产增值
|
||||
name: "资产增值",
|
||||
amount: 32150.20,
|
||||
percentage: 25.59,
|
||||
count: 45,
|
||||
},
|
||||
{
|
||||
type: "trade", // 交易收益
|
||||
name: "交易收益",
|
||||
amount: 8110.00,
|
||||
percentage: 6.45,
|
||||
count: 89,
|
||||
},
|
||||
],
|
||||
// 最近收益明细
|
||||
recentRecords: [
|
||||
{
|
||||
id: "1",
|
||||
type: "dividend",
|
||||
typeName: "分红收益",
|
||||
assetName: "纽约曼哈顿中心公寓",
|
||||
assetCode: "NYC-001",
|
||||
amount: 520.50,
|
||||
currency: "USD",
|
||||
date: "2025-12-27 10:30:00",
|
||||
status: "completed", // completed, pending, processing
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
type: "appreciation",
|
||||
typeName: "资产增值",
|
||||
assetName: "旧金山商业地产",
|
||||
assetCode: "SF-002",
|
||||
amount: 320.80,
|
||||
currency: "USD",
|
||||
date: "2025-12-27 09:15:00",
|
||||
status: "completed",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
type: "trade",
|
||||
typeName: "交易收益",
|
||||
assetName: "洛杉矶住宅楼",
|
||||
assetCode: "LA-003",
|
||||
amount: 215.50,
|
||||
currency: "USD",
|
||||
date: "2025-12-26 16:45:00",
|
||||
status: "completed",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
type: "dividend",
|
||||
typeName: "分红收益",
|
||||
assetName: "迈阿密海景别墅",
|
||||
assetCode: "MIA-004",
|
||||
amount: 680.20,
|
||||
currency: "USD",
|
||||
date: "2025-12-26 14:20:00",
|
||||
status: "pending",
|
||||
},
|
||||
{
|
||||
id: "5",
|
||||
type: "appreciation",
|
||||
typeName: "资产增值",
|
||||
assetName: "芝加哥写字楼",
|
||||
assetCode: "CHI-005",
|
||||
amount: 450.60,
|
||||
currency: "USD",
|
||||
date: "2025-12-25 11:30:00",
|
||||
status: "completed",
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
|
||||
// 收益明细列表(支持筛选)
|
||||
mocks.register("income/records", () => {
|
||||
return {
|
||||
list: Array.from({ length: 20 }, (_, index) => ({
|
||||
id: String(index + 1),
|
||||
type: ["dividend", "appreciation", "trade"][index % 3],
|
||||
typeName: ["分红收益", "资产增值", "交易收益"][index % 3],
|
||||
assetName: `资产名称 ${index + 1}`,
|
||||
assetCode: `ASSET-${String(index + 1).padStart(3, "0")}`,
|
||||
amount: (Math.random() * 1000 + 100).toFixed(2),
|
||||
currency: "USD",
|
||||
date: new Date(Date.now() - index * 86400000).toISOString(),
|
||||
status: ["completed", "pending", "processing"][index % 3],
|
||||
})),
|
||||
total: 100,
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
};
|
||||
});
|
||||
@@ -1 +1,2 @@
|
||||
import "./notify";
|
||||
import "./income";
|
||||
|
||||
@@ -59,6 +59,11 @@ const routes: Array<RouteRecordRaw> = [
|
||||
component: () => import("@/views/withdraw/index.vue"),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/income/total",
|
||||
component: () => import("@/views/income/total/index.vue"),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/wallet/bill",
|
||||
component: () => import("@/views/wallet/bill.vue"),
|
||||
@@ -135,12 +140,12 @@ const routes: Array<RouteRecordRaw> = [
|
||||
component: () => import("@/views/trade-settings/my-subscribe/index.vue"),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
// {
|
||||
// path: ":id",
|
||||
// props: true,
|
||||
// component: () => import("@/views/trade-settings/my-subscribe/detail.vue"),
|
||||
// meta: { requiresAuth: true },
|
||||
// },
|
||||
{
|
||||
path: ":id",
|
||||
props: true,
|
||||
component: () => import("@/views/trade-settings/my-subscribe/detail.vue"),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -23,9 +23,9 @@ html.ion-palette-light {
|
||||
--ion-color-tertiary-tint: #757575;
|
||||
|
||||
--ion-color-success: #2c810e;
|
||||
--ion-color-success-rgb: 44,129,14;
|
||||
--ion-color-success-rgb: 44, 129, 14;
|
||||
--ion-color-success-contrast: #ffffff;
|
||||
--ion-color-success-contrast-rgb: 255,255,255;
|
||||
--ion-color-success-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-success-shade: #27720c;
|
||||
--ion-color-success-tint: #418e26;
|
||||
|
||||
@@ -143,9 +143,9 @@ html.ion-palette-dark {
|
||||
--ion-color-tertiary-tint: #dadada;
|
||||
|
||||
--ion-color-success: #2c810e;
|
||||
--ion-color-success-rgb: 44,129,14;
|
||||
--ion-color-success-rgb: 44, 129, 14;
|
||||
--ion-color-success-contrast: #ffffff;
|
||||
--ion-color-success-contrast-rgb: 255,255,255;
|
||||
--ion-color-success-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-success-shade: #27720c;
|
||||
--ion-color-success-tint: #418e26;
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ export function timeToLocal(originalTime: number) {
|
||||
return Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds()) / 1000;
|
||||
}
|
||||
|
||||
export function formatAmount(amount: MaybeRefOrGetter<number | string | null | undefined>): string {
|
||||
export function formatAmountWithUnit(amount: MaybeRefOrGetter<number | string | null | undefined>): string {
|
||||
const value = toValue(amount);
|
||||
if (Number.isNaN(Number(value))) {
|
||||
return "0";
|
||||
@@ -24,21 +24,37 @@ export function formatAmount(amount: MaybeRefOrGetter<number | string | null | u
|
||||
const num = Number(value);
|
||||
|
||||
// 不超过1万,原样显示
|
||||
if (num < 10000) {
|
||||
if (num < 1e4) {
|
||||
return String(num);
|
||||
}
|
||||
|
||||
// 1亿以上,显示为xx亿
|
||||
if (num >= 100000000) {
|
||||
const yi = (num / 100000000).toFixed(1);
|
||||
if (num >= 1e8) {
|
||||
const yi = (num / 1e8).toFixed(1);
|
||||
return yi.endsWith(".0") ? `${Number.parseInt(yi)}亿` : `${yi}亿`;
|
||||
}
|
||||
|
||||
// 1万到1亿,显示为xx万
|
||||
if (num >= 10000) {
|
||||
const wan = (num / 10000).toFixed(1);
|
||||
if (num >= 1e4) {
|
||||
const wan = (num / 1e4).toFixed(1);
|
||||
return wan.endsWith(".0") ? `${Number.parseInt(wan)}万` : `${wan}万`;
|
||||
}
|
||||
|
||||
return String(num);
|
||||
}
|
||||
|
||||
export function formatAmountWithSplit(amount: MaybeRefOrGetter<number | string | null | undefined>): string {
|
||||
const value = toValue(amount);
|
||||
if (Number.isNaN(Number(value))) {
|
||||
return "0";
|
||||
}
|
||||
|
||||
const num = Number(value);
|
||||
|
||||
return Intl.NumberFormat("en-US", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
}).format(num);
|
||||
}
|
||||
|
||||
41
src/views/income/total/components/overview.vue
Normal file
41
src/views/income/total/components/overview.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<script lang='ts' setup>
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-text-900 rounded-xl p-7">
|
||||
<div>
|
||||
<div class="text-sm mb-2">
|
||||
累计总收益
|
||||
</div>
|
||||
<div class="text-4xl font-bold">
|
||||
{{ formatAmountWithSplit(190421321) }}
|
||||
</div>
|
||||
<div class="flex items-center gap-2 text-xs mt-1">
|
||||
<div>昨日收益</div>
|
||||
<div>+{{ formatAmountWithSplit(864314) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-px bg-text-800 my-5" />
|
||||
<div class="flex justify-around">
|
||||
<div class="flex-col-center">
|
||||
<div class="text-base font-bold">
|
||||
{{ formatAmountWithSplit(42523) }}
|
||||
</div>
|
||||
<div class="text-xs">
|
||||
本月收益
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-auto w-px bg-text-800" />
|
||||
<div class="flex-col-center">
|
||||
<div class="text-base font-bold">
|
||||
{{ formatAmountWithSplit(12345) }}
|
||||
</div>
|
||||
<div class="text-xs">
|
||||
待确认收益
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang='css' scoped></style>
|
||||
59
src/views/income/total/components/trend.vue
Normal file
59
src/views/income/total/components/trend.vue
Normal file
@@ -0,0 +1,59 @@
|
||||
<script lang='ts' setup>
|
||||
const tradingViewInst = useTemplateRef<HTMLDivElement>("tradingViewInst");
|
||||
|
||||
useTradingView(tradingViewInst, {
|
||||
type: "Area",
|
||||
data: [
|
||||
{
|
||||
time: "2023-01-01",
|
||||
value: 1000,
|
||||
},
|
||||
{
|
||||
time: "2023-02-01",
|
||||
value: 1200,
|
||||
},
|
||||
{
|
||||
time: "2023-03-01",
|
||||
value: 900,
|
||||
},
|
||||
{
|
||||
time: "2023-04-01",
|
||||
value: 1400,
|
||||
},
|
||||
{
|
||||
time: "2023-05-01",
|
||||
value: 1300,
|
||||
},
|
||||
{
|
||||
time: "2023-06-01",
|
||||
value: 1500,
|
||||
},
|
||||
{
|
||||
time: "2023-07-01",
|
||||
value: 1700,
|
||||
},
|
||||
{
|
||||
time: "2023-08-01",
|
||||
value: 1600,
|
||||
},
|
||||
],
|
||||
weightChartOptions: {
|
||||
height: 150,
|
||||
rightPriceScale: {
|
||||
visible: false,
|
||||
},
|
||||
leftPriceScale: {
|
||||
visible: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h4>收益趋势</h4>
|
||||
<div ref="tradingViewInst" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang='css' scoped></style>
|
||||
53
src/views/income/total/index.vue
Normal file
53
src/views/income/total/index.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<script lang='ts' setup>
|
||||
import type { RefresherCustomEvent } from "@ionic/vue";
|
||||
import { mockClient } from "@/api";
|
||||
import Overview from "./components/overview.vue";
|
||||
import Trend from "./components/trend.vue";
|
||||
|
||||
const { t } = useI18n();
|
||||
const { vibrate } = useHaptics();
|
||||
|
||||
// 收益数据
|
||||
const loading = ref(true);
|
||||
const { data } = await mockClient("income/total");
|
||||
|
||||
async function loadIncomeData() {
|
||||
loading.value = true;
|
||||
useTimeoutFn(() => {
|
||||
loading.value = false;
|
||||
}, 800);
|
||||
}
|
||||
|
||||
async function handleRefresh(event: RefresherCustomEvent) {
|
||||
vibrate();
|
||||
useTimeoutFn(() => {
|
||||
event.target.complete();
|
||||
}, 800);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadIncomeData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ion-page>
|
||||
<ion-header class="ion-no-border">
|
||||
<ion-toolbar class="ui-toolbar">
|
||||
<ion-back-button slot="start" />
|
||||
<ion-title>{{ t("income.title") }}</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>
|
||||
<Overview />
|
||||
<Trend />
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</template>
|
||||
@@ -33,7 +33,7 @@ const { t } = useI18n();
|
||||
<div class="label">
|
||||
{{ t('market.tradeRwa.fields.valuation') }}
|
||||
</div>
|
||||
<div>${{ formatAmount(data?.product.estimatedValue) }}</div>
|
||||
<div>${{ formatAmountWithUnit(data?.product.estimatedValue) }}</div>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
<ion-row>
|
||||
@@ -41,7 +41,7 @@ const { t } = useI18n();
|
||||
<div class="label">
|
||||
{{ t('market.tradeRwa.fields.unitPrice') }}
|
||||
</div>
|
||||
<div>${{ formatAmount(data?.unitPrice) }}</div>
|
||||
<div>${{ formatAmountWithUnit(data?.unitPrice) }}</div>
|
||||
</ion-col>
|
||||
<ion-col>
|
||||
<div class="label">
|
||||
|
||||
@@ -126,7 +126,7 @@ onUpdated(() => {
|
||||
</p>
|
||||
<ion-button
|
||||
expand="block"
|
||||
color="success"
|
||||
fill="outline"
|
||||
@click="handleAddCard"
|
||||
>
|
||||
<ion-icon slot="start" :icon="addOutline" />
|
||||
|
||||
@@ -33,7 +33,7 @@ const { t } = useI18n();
|
||||
<div class="label">
|
||||
{{ t('market.tradeRwa.fields.valuation') }}
|
||||
</div>
|
||||
<div>${{ formatAmount(data?.estimatedValue) }}</div>
|
||||
<div>${{ formatAmountWithUnit(data?.estimatedValue) }}</div>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
|
||||
@@ -28,16 +28,13 @@ async function handleSubscribe(val: number) {
|
||||
await toast.present();
|
||||
model.value?.$el.dismiss();
|
||||
}
|
||||
function gotoEdit() {
|
||||
router.push({ name: "trade-rwa-edit" });
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ion-page>
|
||||
<ion-header>
|
||||
<ion-toolbar class="ui-toolbar">
|
||||
<ion-back-button slot="start" text="" />
|
||||
<ion-back-button slot="start" />
|
||||
<ion-title>
|
||||
{{ data?.edition.product.code }}
|
||||
</ion-title>
|
||||
@@ -53,7 +50,7 @@ function gotoEdit() {
|
||||
{{ data?.edition.product.name }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-500 font-semibold">
|
||||
{{ data?.edition.product.categoryId }}
|
||||
{{ data?.edition.product.code }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,13 +3,7 @@
|
||||
|
||||
<template>
|
||||
<ion-page>
|
||||
<ion-header>
|
||||
<ion-toolbar class="ui-toolbar">
|
||||
<ion-back-button slot="start" />
|
||||
<ion-title>我的申购</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<router-view />
|
||||
<ion-router-outlet />
|
||||
</ion-page>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
import { calendarOutline, listOutline, timeOutline, walletOutline } from "ionicons/icons";
|
||||
|
||||
const { t } = useI18n();
|
||||
const router = useRouter();
|
||||
const { vibrate } = useHaptics();
|
||||
|
||||
function navigateToTotal() {
|
||||
vibrate();
|
||||
router.push("/income/total");
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -10,26 +17,29 @@ const { t } = useI18n();
|
||||
{{ t("asset.revenue.myRevenue") }}
|
||||
</ion-label>
|
||||
<div class="grid grid-cols-4 mt-5">
|
||||
<div class="col-span-1 flex-col-center gap-2">
|
||||
<ion-icon :icon="walletOutline" />
|
||||
<div
|
||||
class="col-span-1 flex flex-col items-center gap-2 cursor-pointer transition-opacity active:opacity-70"
|
||||
@click="navigateToTotal"
|
||||
>
|
||||
<ion-icon :icon="walletOutline" class="text-2xl text-primary" />
|
||||
<div class="text-xs">
|
||||
{{ t("asset.revenue.totalRevenue") }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-span-1 flex-col-center gap-2">
|
||||
<ion-icon :icon="calendarOutline" />
|
||||
<div class="col-span-1 flex flex-col items-center gap-2 cursor-pointer transition-opacity active:opacity-70">
|
||||
<ion-icon :icon="calendarOutline" class="text-2xl text-primary" />
|
||||
<div class="text-xs">
|
||||
{{ t("asset.revenue.monthlyRevenue") }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-span-1 flex-col-center gap-2">
|
||||
<ion-icon :icon="timeOutline" />
|
||||
<div class="col-span-1 flex flex-col items-center gap-2 cursor-pointer transition-opacity active:opacity-70">
|
||||
<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-col-center gap-2">
|
||||
<ion-icon :icon="listOutline" />
|
||||
<div class="col-span-1 flex flex-col items-center gap-2 cursor-pointer transition-opacity active:opacity-70">
|
||||
<ion-icon :icon="listOutline" class="text-2xl text-primary" />
|
||||
<div class="text-xs">
|
||||
{{ t("asset.revenue.revenueDetails") }}
|
||||
</div>
|
||||
@@ -37,10 +47,3 @@ const { t } = useI18n();
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang='css' scoped>
|
||||
ion-icon {
|
||||
font-size: 1.5rem;
|
||||
color: var(--ion-color-primary);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -39,7 +39,7 @@ async function handleScan() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<IonPage>
|
||||
<ion-page>
|
||||
<ion-header class="ion-no-border">
|
||||
<ion-toolbar class="ui-toolbar">
|
||||
<div slot="end">
|
||||
@@ -55,7 +55,7 @@ async function handleScan() {
|
||||
</div>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<IonContent :fullscreen="true" class="ion-padding">
|
||||
<ion-content :fullscreen="true" class="ion-padding">
|
||||
<ion-refresher slot="fixed" @ion-refresh="handleRefresh($event)">
|
||||
<ion-refresher-content />
|
||||
</ion-refresher>
|
||||
@@ -68,8 +68,8 @@ async function handleScan() {
|
||||
<MyRevenue />
|
||||
<TradeSettings />
|
||||
</div>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
Reference in New Issue
Block a user