feat: 添加产品记录页面,支持我的业务功能和数据加载
This commit is contained in:
@@ -39,6 +39,11 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/product/records",
|
||||||
|
component: () => import("@/views/product/records.vue"),
|
||||||
|
meta: { requiresAuth: true },
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/news/:id",
|
path: "/news/:id",
|
||||||
props: true,
|
props: true,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { Treaty } from "@elysiajs/eden";
|
|||||||
import type { InfiniteScrollCustomEvent } from "@ionic/vue";
|
import type { InfiniteScrollCustomEvent } from "@ionic/vue";
|
||||||
import type { TreatyQuery } from "@/api/types";
|
import type { TreatyQuery } from "@/api/types";
|
||||||
import { modalController } from "@ionic/vue";
|
import { modalController } from "@ionic/vue";
|
||||||
import { calendarOutline, cardOutline, timeOutline, trendingUpOutline } from "ionicons/icons";
|
import { bagHandleOutline, calendarOutline, cardOutline, timeOutline, trendingUpOutline } from "ionicons/icons";
|
||||||
import { client, safeClient } from "@/api";
|
import { client, safeClient } from "@/api";
|
||||||
import Subscribe from "./components/subscribe.vue";
|
import Subscribe from "./components/subscribe.vue";
|
||||||
|
|
||||||
@@ -82,13 +82,17 @@ onMounted(() => {
|
|||||||
<!-- 基金产品列表 -->
|
<!-- 基金产品列表 -->
|
||||||
<section class="mb-5 -mt-5 ion-padding-horizontal">
|
<section class="mb-5 -mt-5 ion-padding-horizontal">
|
||||||
<div class="flex justify-between items-center mb-4">
|
<div class="flex justify-between items-center mb-4">
|
||||||
<div class="flex justify-between items-center mb-4">
|
<div class="flex justify-between items-center mb-4 w-full">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<img src="@/assets/images/icon.png" class="size-7">
|
<img src="@/assets/images/icon.png" class="size-7">
|
||||||
<div class="text-xl font-bold text-[#1a1a1a]">
|
<div class="text-xl font-bold text-[#1a1a1a]">
|
||||||
基金产品
|
基金产品
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<ion-button fill="clear" size="small" @click="$router.push('/product/records')">
|
||||||
|
<ion-icon slot="start" :icon="bagHandleOutline" />
|
||||||
|
我的业务
|
||||||
|
</ion-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -137,6 +141,12 @@ onMounted(() => {
|
|||||||
{{ Number(product.cycleDays) }}
|
{{ Number(product.cycleDays) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<span class="text-xs text-[#999] mb-1">改革先锋扶持金</span>
|
||||||
|
<span class="text-lg font-bold text-[#52c41a] flex items-center gap-0.5">
|
||||||
|
¥{{ Number(product.reformPioneerAllowance) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 申购按钮 -->
|
<!-- 申购按钮 -->
|
||||||
|
|||||||
128
src/views/product/records.vue
Normal file
128
src/views/product/records.vue
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
<script lang='ts' setup>
|
||||||
|
import type { Treaty } from "@elysiajs/eden";
|
||||||
|
import type { InfiniteScrollCustomEvent } from "@ionic/vue";
|
||||||
|
import type { TreatyQuery } from "@/api/types";
|
||||||
|
import { client, safeClient } from "@/api";
|
||||||
|
|
||||||
|
type Subscribe = Treaty.Data<typeof client.api.subscription.orders.get>["data"][number];
|
||||||
|
type SubscribeQuery = TreatyQuery<typeof client.api.subscription.orders.get>;
|
||||||
|
|
||||||
|
const [query] = useResetRef<SubscribeQuery>({
|
||||||
|
offset: 0,
|
||||||
|
limit: 10,
|
||||||
|
});
|
||||||
|
const data = ref<Subscribe[]>([]);
|
||||||
|
const isFinished = ref(false);
|
||||||
|
|
||||||
|
async function fetchData() {
|
||||||
|
const { data: responseData } = await safeClient(client.api.subscription.orders.get({ query: { ...query.value } }));
|
||||||
|
data.value.push(...(responseData.value?.data || []));
|
||||||
|
isFinished.value = responseData.value?.pagination.hasNextPage === false;
|
||||||
|
}
|
||||||
|
async function handleInfinite(event: InfiniteScrollCustomEvent) {
|
||||||
|
if (isFinished.value) {
|
||||||
|
event.target.complete();
|
||||||
|
event.target.disabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
query.value.offset! += query.value.limit!;
|
||||||
|
await fetchData();
|
||||||
|
setTimeout(() => {
|
||||||
|
event.target.complete();
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchData();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ion-page>
|
||||||
|
<ion-header class="ion-no-border">
|
||||||
|
<ion-toolbar class="ion-toolbar">
|
||||||
|
<ion-buttons slot="start">
|
||||||
|
<back-button />
|
||||||
|
</ion-buttons>
|
||||||
|
<ion-title>我的业务</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content :fullscreen="true">
|
||||||
|
<div class="p-4">
|
||||||
|
<empty v-if="data.length === 0 && !isFinished" class="my-20" />
|
||||||
|
|
||||||
|
<div v-else class="flex flex-col gap-3">
|
||||||
|
<div
|
||||||
|
v-for="item in data"
|
||||||
|
:key="item.id"
|
||||||
|
class="bg-white rounded-2xl p-4 shadow-sm"
|
||||||
|
>
|
||||||
|
<!-- 顶部状态栏 -->
|
||||||
|
<div class="flex items-center justify-between mb-3 pb-3 border-b border-gray-100">
|
||||||
|
<div class="text-xs text-gray-500">
|
||||||
|
{{ useDateFormat(item.createdAt, 'YYYY-MM-DD HH:mm') }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="px-3 py-1 rounded-full text-xs font-semibold"
|
||||||
|
:class="{
|
||||||
|
'bg-green-50 text-green-600': item.status === 'unlocked',
|
||||||
|
'bg-blue-50 text-blue-600': item.status === 'locked',
|
||||||
|
'bg-yellow-50 text-yellow-600': item.status === 'pending',
|
||||||
|
'bg-red-50 text-red-600': item.status === 'cancelled',
|
||||||
|
'bg-gray-50 text-gray-600': !['unlocked', 'locked', 'pending', 'cancelled'].includes(item.status),
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ item.status === 'unlocked' ? '已解锁' : item.status === 'locked' ? '锁定中' : item.status === 'pending' ? '待处理' : item.status === 'cancelled' ? '已取消' : item.status }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 金额信息 -->
|
||||||
|
<div class="grid grid-cols-2 gap-4 mb-3">
|
||||||
|
<div>
|
||||||
|
<div class="text-xs text-gray-500 mb-1">
|
||||||
|
申购金额
|
||||||
|
</div>
|
||||||
|
<div class="text-lg font-bold text-[#c41e3a]">
|
||||||
|
¥{{ Number(item.price).toFixed(2) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="text-xs text-gray-500 mb-1">
|
||||||
|
改革先锋津贴
|
||||||
|
</div>
|
||||||
|
<div class="text-lg font-bold text-green-600">
|
||||||
|
¥{{ Number(item.reformPioneerAllowance).toFixed(2) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 周期信息 -->
|
||||||
|
<div class="flex items-center justify-between pt-3 border-t border-gray-100">
|
||||||
|
<div class="text-sm text-gray-600">
|
||||||
|
投资周期
|
||||||
|
</div>
|
||||||
|
<div class="text-sm font-semibold text-gray-900">
|
||||||
|
{{ item.cycleDays }} 天
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ion-infinite-scroll threshold="100px" :disabled="isFinished" @ion-infinite="handleInfinite">
|
||||||
|
<ion-infinite-scroll-content
|
||||||
|
loading-spinner="bubbles"
|
||||||
|
loading-text="加载更多..."
|
||||||
|
/>
|
||||||
|
</ion-infinite-scroll>
|
||||||
|
</ion-content>
|
||||||
|
</ion-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang='css' scoped>
|
||||||
|
.shadow-sm {
|
||||||
|
box-shadow:
|
||||||
|
0 1px 3px 0 rgba(0, 0, 0, 0.1),
|
||||||
|
0 1px 2px -1px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user