feat: 添加通知功能,集成模拟数据并更新通知视图

This commit is contained in:
2025-12-27 01:30:37 +07:00
parent 6b7a2c7ef1
commit ee07f06d9e
8 changed files with 197 additions and 18 deletions

View File

@@ -3,6 +3,7 @@ import type { WatchSource } from "vue";
import { treaty } from "@elysiajs/eden";
import { toastController } from "@ionic/vue";
import { i18n } from "@/locales";
import { useMocks } from "@/mocks";
const client = treaty<App>(window.location.origin, {
fetch: {
@@ -121,4 +122,17 @@ export function safeClient<T, E>(
return promise as SafeClientReturn<T, E> & Promise<SafeClientReturn<T, E>>;
}
export function mockClient<T = any, E = any>(key: string): SafeClientReturn<T, E> & Promise<SafeClientReturn<T, E>> {
const requestPromise = async () => {
const mocks = useMocks();
const mockFn = mocks.get(key);
if (!mockFn) {
throw new Error(`Mock with key "${key}" not found.`);
}
const data = await mockFn();
return { data: data as T, error: null as E };
};
return safeClient<T, E>(requestPromise, { immediate: true });
}
export { client };

View File

@@ -5,7 +5,7 @@ import uiComponents from "@/ui";
import App from "./App.vue";
import { authClient } from "./auth";
import { i18n } from "./locales";
import { setupMocks } from "./mocks";
import { router } from "./router";
/* Core CSS required for Ionic components to work properly */
@@ -41,26 +41,32 @@ import "./theme/ionic.css";
useTheme();
authClient.getSession().then((session) => {
const pinia = createPinia();
const userStore = useUserStore(pinia);
userStore.setToken(session.data?.session.token || "");
function initApp() {
authClient.getSession().then((session) => {
const pinia = createPinia();
const userStore = useUserStore(pinia);
userStore.setToken(session.data?.session.token || "");
const app = createApp(App)
.use(IonicVue, {
backButtonText: "返回",
mode: "ios",
statusTap: true,
swipeBackEnabled: true,
const app = createApp(App)
.use(IonicVue, {
backButtonText: "返回",
mode: "ios",
statusTap: true,
swipeBackEnabled: true,
// rippleEffect: true,
// animated: false,
})
.use(uiComponents)
.use(pinia)
.use(router)
.use(i18n);
})
.use(uiComponents)
.use(pinia)
.use(router)
.use(i18n);
router.isReady().then(() => {
app.mount("#app");
router.isReady().then(() => {
app.mount("#app");
});
});
}
setupMocks().then(() => {
initApp();
});

1
src/mocks/data/index.ts Normal file
View File

@@ -0,0 +1 @@
import "./notify";

80
src/mocks/data/notify.ts Normal file
View File

@@ -0,0 +1,80 @@
import { useMocks } from "..";
const mocks = useMocks();
mocks.register("notify.list", () => {
const now = new Date();
return [
{
id: 1,
title: "系统通知",
content: "您的账户安全等级已提升至 LV2现在可以享受更高的交易限额。",
date: new Date(now.getTime() - 1000 * 60 * 30).toISOString(), // 30分钟前
read: false,
},
{
id: 2,
title: "交易通知",
content: "您的买入订单已成功成交,交易对: BTC/USDT数量: 0.005 BTC",
date: new Date(now.getTime() - 1000 * 60 * 60 * 2).toISOString(), // 2小时前
read: false,
},
{
id: 3,
title: "活动通知",
content: "新春特惠活动火热进行中!邀请好友注册交易即可获得最高 100 USDT 奖励活动截止时间2025-02-28",
date: new Date(now.getTime() - 1000 * 60 * 60 * 5).toISOString(), // 5小时前
read: true,
},
{
id: 4,
title: "安全提醒",
content: "检测到您的账户在新设备登录,登录地点:北京,如非本人操作,请立即修改密码并联系客服。",
date: new Date(now.getTime() - 1000 * 60 * 60 * 24).toISOString(), // 1天前
read: false,
},
{
id: 5,
title: "交易通知",
content: "您的卖出订单已成功成交,交易对: ETH/USDT数量: 0.1 ETH成交价格: 2850 USDT",
date: new Date(now.getTime() - 1000 * 60 * 60 * 24 * 2).toISOString(), // 2天前
read: true,
},
{
id: 6,
title: "系统通知",
content: "平台将于北京时间 2025-01-05 02:00 至 04:00 进行系统升级维护,期间部分功能可能暂时无法使用。",
date: new Date(now.getTime() - 1000 * 60 * 60 * 24 * 3).toISOString(), // 3天前
read: true,
},
{
id: 7,
title: "活动通知",
content: "恭喜您获得新手礼包!包含 10 USDT 体验金和 7 天 VIP 会员权益,请及时领取。",
date: new Date(now.getTime() - 1000 * 60 * 60 * 24 * 5).toISOString(), // 5天前
read: true,
},
{
id: 8,
title: "安全提醒",
content: "您的提现申请已通过审核,预计将在 24 小时内到账,请注意查收。",
date: new Date(now.getTime() - 1000 * 60 * 60 * 24 * 7).toISOString(), // 7天前
read: true,
},
{
id: 9,
title: "交易通知",
content: "市场波动较大,您持仓的 BTC 当前盈利已达 15%,是否考虑止盈?",
date: new Date(now.getTime() - 1000 * 60 * 60 * 24 * 10).toISOString(), // 10天前
read: true,
},
{
id: 10,
title: "系统通知",
content: "为了更好地保护您的资产安全建议您开启双重验证2FA功能。",
date: new Date(now.getTime() - 1000 * 60 * 60 * 24 * 15).toISOString(), // 15天前
read: true,
},
];
});

41
src/mocks/index.ts Normal file
View File

@@ -0,0 +1,41 @@
import type { Awaitable } from "@vueuse/core";
export class Mock {
private mocks: Map<string, () => Awaitable<any>>;
constructor() {
this.mocks = new Map();
}
get keys(): string[] {
return Array.from(this.mocks.keys());
}
register(key: string, data: () => Awaitable<any>) {
this.mocks.set(key, data);
}
unregister(key: string) {
this.mocks.delete(key);
}
get(key: string): (() => Awaitable<any>) | undefined {
return this.mocks.get(key);
}
}
export function useMocks() {
const singletonKey = "__mocks_singleton__";
if (!(globalThis as any)[singletonKey]) {
(globalThis as any)[singletonKey] = new Mock();
}
return (globalThis as any)[singletonKey] as Mock;
}
export function setupMocks() {
return new Promise<void>((resolve) => {
if (import.meta.env.DEV) {
import("./data");
}
resolve();
});
}

View File

@@ -1,6 +1,10 @@
<script setup lang="ts">
import IcBaselineNotificationsNone from "~icons/ic/baseline-notifications-none";
import IconParkOutlineClearFormat from "~icons/icon-park-outline/clear-format";
import MaterialSymbolsAndroidContacts from "~icons/material-symbols/android-contacts";
import { mockClient } from "@/api";
const { data } = mockClient("notify.list");
</script>
<template>
@@ -18,6 +22,27 @@ import MaterialSymbolsAndroidContacts from "~icons/material-symbols/android-cont
</IonHeader>
<IonContent :fullscreen="true">
<ion-searchbar placeholder="Search" />
<ion-list lines="none">
<ion-item v-for="item in data" :key="item.id" class="py-3">
<div slot="start" class="bg-text-900 p-2.5 rounded-full">
<IcBaselineNotificationsNone class="text-2xl text-(--ion-color-success-tint)" />
</div>
<div class="pl-3 w-full">
<div class="flex items-center justify-between mb-1">
<div class="text-md font-semibold">
{{ item.title }}
</div>
<ion-note slot="end" class="text-xs">
{{ useDateFormat(item.date, 'MM/DD HH:mm') }}
</ion-note>
</div>
<div class="text-xs overflow-hidden text-ellipsis">
{{ item.content }}
</div>
</div>
</ion-item>
</ion-list>
</IonContent>
</IonPage>
</template>