feat: transaction framework
This commit is contained in:
@@ -1,17 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-navigation-drawer expand-on-hover rail>
|
<v-navigation-drawer class="pt-4" rail>
|
||||||
<v-list>
|
<v-list>
|
||||||
<v-list-item prepend-avatar="https://randomuser.me/api/portraits/women/85.jpg"
|
<v-list-item prepend-avatar="https://randomuser.me/api/portraits/women/85.jpg" subtitle="sandra_a88@gmailcom"
|
||||||
subtitle="sandra_a88@gmailcom" title="Sandra Adams"></v-list-item>
|
title="Sandra Adams"></v-list-item>
|
||||||
</v-list>
|
</v-list>
|
||||||
|
|
||||||
<v-divider></v-divider>
|
<v-divider></v-divider>
|
||||||
|
|
||||||
<v-list density="compact" nav>
|
|
||||||
<v-list-item prepend-icon="mdi-folder" title="My Files" value="myfiles"></v-list-item>
|
|
||||||
<v-list-item prepend-icon="mdi-account-multiple" title="Shared with me" value="shared"></v-list-item>
|
|
||||||
<v-list-item prepend-icon="mdi-star" title="Starred" value="starred"></v-list-item>
|
|
||||||
</v-list>
|
|
||||||
</v-navigation-drawer>
|
|
||||||
|
|
||||||
|
<v-list density="compact" nav>
|
||||||
|
<v-list-item prepend-icon="mdi-folder" title="My Files" value="myfiles"></v-list-item>
|
||||||
|
<v-list-item prepend-icon="mdi-account-multiple" title="Shared with me" value="shared"></v-list-item>
|
||||||
|
<v-list-item prepend-icon="mdi-star" title="Starred" value="starred"></v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</v-navigation-drawer>
|
||||||
</template>
|
</template>
|
||||||
@@ -1,7 +1,57 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-list :items="items"></v-list>
|
<v-list rounded="lg" lines="two">
|
||||||
|
<TransactionListItem v-for="v in transactions" :transaction-item="v"/>
|
||||||
|
</v-list>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
let transactions = [
|
||||||
|
{
|
||||||
|
"id": "20842",
|
||||||
|
"description": "aaa",
|
||||||
|
"category": {
|
||||||
|
"id": "10087241",
|
||||||
|
"name": "Category1",
|
||||||
|
},
|
||||||
|
"amount": {
|
||||||
|
"value": "135.33",
|
||||||
|
"currency": "CNY",
|
||||||
|
},
|
||||||
|
"time": "1738564519",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"id": "1742004",
|
||||||
|
"name": "t1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1742932",
|
||||||
|
"name": "t3",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "20845",
|
||||||
|
"description": "bbb",
|
||||||
|
"category": {
|
||||||
|
"id": "10088823",
|
||||||
|
"name": "Category2",
|
||||||
|
},
|
||||||
|
"amount": {
|
||||||
|
"value": "77.99",
|
||||||
|
"currency": "CNY",
|
||||||
|
},
|
||||||
|
"time": "1738564519",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"id": "1742004",
|
||||||
|
"name": "t1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1742932",
|
||||||
|
"name": "t3",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
]
|
||||||
let items = [
|
let items = [
|
||||||
{ type: 'subheader', title: 'Group #1' },
|
{ type: 'subheader', title: 'Group #1' },
|
||||||
{
|
{
|
||||||
|
|||||||
37
components/TransactionListItem.vue
Normal file
37
components/TransactionListItem.vue
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<template>
|
||||||
|
<v-list-item :value="transactionItem.id">
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<v-avatar color="grey-lighten-1">
|
||||||
|
<v-icon color="white">mdi-folder</v-icon>
|
||||||
|
</v-avatar>
|
||||||
|
</template>
|
||||||
|
<v-list-item-title>
|
||||||
|
{{transactionItem.category?.name}}
|
||||||
|
</v-list-item-title>
|
||||||
|
<v-list-item-subtitle>
|
||||||
|
{{formatDateTime}} · {{transactionItem.description}}
|
||||||
|
</v-list-item-subtitle>
|
||||||
|
<template v-slot:append>
|
||||||
|
<v-list-item-action class="flex-column align-end">
|
||||||
|
<span v-html="formatAmountString"></span>
|
||||||
|
</v-list-item-action>
|
||||||
|
</template>
|
||||||
|
</v-list-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {Transaction} from "@/models/transaction.ts"
|
||||||
|
import {computed } from 'vue'
|
||||||
|
import * as dayjs from 'dayjs'
|
||||||
|
import {CurrencyTypeToSymbol} from "@/utils/currency"
|
||||||
|
const props = defineProps<{
|
||||||
|
transactionItem: Transaction;
|
||||||
|
}>();
|
||||||
|
const formatDateTime = computed(() => {
|
||||||
|
let parsed = dayjs.unix(Number(props.transactionItem.time))
|
||||||
|
return parsed.format("HH:mm")
|
||||||
|
})
|
||||||
|
const formatAmountString = computed(() => {
|
||||||
|
return CurrencyTypeToSymbol(props.transactionItem.amount.currency)+props.transactionItem.amount.val||"0.00"
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- <v-layout class=""> -->
|
<v-layout style="min-height: 100%;">
|
||||||
<!-- <p>desktop layout</p> -->
|
<!-- <p>desktop layout</p> -->
|
||||||
<div>
|
<!-- <div> -->
|
||||||
<DesktopSideBar />
|
<DesktopSideBar />
|
||||||
<v-app-bar flat>
|
<v-app-bar flat>
|
||||||
<slot name="app-bar-container"></slot>
|
<slot name="app-bar-container"></slot>
|
||||||
</v-app-bar>
|
</v-app-bar>
|
||||||
<v-main class="bg-grey-lighten-4">
|
<v-main class="bg-grey-lighten-4 d-flex align-start justify-center">
|
||||||
<v-container>
|
<v-container fluid>
|
||||||
<slot />
|
<slot />
|
||||||
</v-container>
|
</v-container>
|
||||||
</v-main>
|
</v-main>
|
||||||
</div>
|
<!-- </div> -->
|
||||||
<!-- </v-layout> -->
|
</v-layout>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
28
models/transaction.ts
Normal file
28
models/transaction.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
export interface Category {
|
||||||
|
id: string,
|
||||||
|
name: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Tag {
|
||||||
|
id: string,
|
||||||
|
name: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Amount{
|
||||||
|
currency: string,
|
||||||
|
val: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Transaction{
|
||||||
|
id: string,
|
||||||
|
description: string,
|
||||||
|
time: string,
|
||||||
|
category?: Category,
|
||||||
|
tags?: Tag[],
|
||||||
|
amount?:
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TransactionGroup {
|
||||||
|
group_title: string,
|
||||||
|
transactions?: Transaction[],
|
||||||
|
}
|
||||||
7
package-lock.json
generated
7
package-lock.json
generated
@@ -8,6 +8,7 @@
|
|||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mdi/font": "^7.4.47",
|
"@mdi/font": "^7.4.47",
|
||||||
|
"dayjs": "^1.11.13",
|
||||||
"nuxt": "^3.15.4",
|
"nuxt": "^3.15.4",
|
||||||
"vue": "latest",
|
"vue": "latest",
|
||||||
"vue-router": "latest"
|
"vue-router": "latest"
|
||||||
@@ -4081,6 +4082,12 @@
|
|||||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/dayjs": {
|
||||||
|
"version": "1.11.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
|
||||||
|
"integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/db0": {
|
"node_modules/db0": {
|
||||||
"version": "0.2.3",
|
"version": "0.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/db0/-/db0-0.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/db0/-/db0-0.2.3.tgz",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mdi/font": "^7.4.47",
|
"@mdi/font": "^7.4.47",
|
||||||
|
"dayjs": "^1.11.13",
|
||||||
"nuxt": "^3.15.4",
|
"nuxt": "^3.15.4",
|
||||||
"vue": "latest",
|
"vue": "latest",
|
||||||
"vue-router": "latest"
|
"vue-router": "latest"
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div style="min-height: 100%;">
|
||||||
Dashboard Page
|
<NuxtLayout name="desktop">
|
||||||
|
<p>
|
||||||
|
Dashboard Page
|
||||||
|
</p>
|
||||||
|
</NuxtLayout>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: 'desktop'
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
@@ -1,17 +1,96 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div style="min-height: 100%;">
|
||||||
<NuxtLayout name="desktop">
|
<NuxtLayout name="desktop">
|
||||||
<template #app-bar-container>
|
<template #app-bar-container>
|
||||||
<v-app-bar-title>Transactions</v-app-bar-title>
|
<v-app-bar-title>Transactions</v-app-bar-title>
|
||||||
</template>
|
</template>
|
||||||
<v-card>
|
<v-row align="start">
|
||||||
<transaction-list></transaction-list>
|
<v-col md="8" lg="8" xl="8" xxl="8">
|
||||||
</v-card>
|
<v-sheet rounded="lg">
|
||||||
|
<v-list rounded="lg" lines="two">
|
||||||
|
<template v-for="grp in transactionList.transactionGroups">
|
||||||
|
<v-list-subheader>{{ grp.group_title }}</v-list-subheader>
|
||||||
|
<TransactionListItem v-for="v in grp.transactions" :transaction-item="v" />
|
||||||
|
</template>
|
||||||
|
</v-list>
|
||||||
|
</v-sheet>
|
||||||
|
</v-col>
|
||||||
|
<v-col>
|
||||||
|
<v-sheet rounded="lg">
|
||||||
|
<v-container>
|
||||||
|
<v-form>
|
||||||
|
<v-text-field v-model="name" label="Name" variant="outlined"></v-text-field>
|
||||||
|
</v-form>
|
||||||
|
</v-container>
|
||||||
|
</v-sheet>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
</NuxtLayout>
|
</NuxtLayout>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
definePageMeta({
|
import { ref } from 'vue';
|
||||||
|
import { Transaction, TransactionGroup } from '@/models/transaction';
|
||||||
|
|
||||||
|
interface TransactionPageData {
|
||||||
|
transactionGroups: TransactionGroup[],
|
||||||
|
}
|
||||||
|
const transactionList = ref(
|
||||||
|
{
|
||||||
|
transactionGroups: [],
|
||||||
|
// filters
|
||||||
|
}
|
||||||
|
)
|
||||||
|
let transactionGrp: TransactionGroup = { transactions: [], group_title: "aabbccdd" };
|
||||||
|
transactionGrp.transactions.push({
|
||||||
|
"id": "20842",
|
||||||
|
"description": "aaa",
|
||||||
|
"category": {
|
||||||
|
"id": "10087241",
|
||||||
|
"name": "Category1",
|
||||||
|
},
|
||||||
|
"amount": {
|
||||||
|
"val": "135.33",
|
||||||
|
"currency": "CNY",
|
||||||
|
},
|
||||||
|
"time": "1738564519",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"id": "1742004",
|
||||||
|
"name": "t1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1742932",
|
||||||
|
"name": "t3",
|
||||||
|
},
|
||||||
|
],
|
||||||
})
|
})
|
||||||
|
transactionList.value.transactionGroups.push(transactionGrp)
|
||||||
|
|
||||||
|
let transactionGrp2: TransactionGroup = { transactions: [], group_title: "eeffgghh" };
|
||||||
|
transactionGrp2.transactions.push({
|
||||||
|
"id": "898539",
|
||||||
|
"description": "bbb",
|
||||||
|
"category": {
|
||||||
|
"id": "10087241",
|
||||||
|
"name": "Category2",
|
||||||
|
},
|
||||||
|
"amount": {
|
||||||
|
"val": "67.00",
|
||||||
|
"currency": "CNY",
|
||||||
|
},
|
||||||
|
"time": "1738561205",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"id": "1742004",
|
||||||
|
"name": "t1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1742932",
|
||||||
|
"name": "t3",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
transactionList.value.transactionGroups.push(transactionGrp2)
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
17
utils/currency.ts
Normal file
17
utils/currency.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
// 定义支持的货币类型
|
||||||
|
type Currency = "USD" | "EUR" | "GBP" | "JPY" | "CNY";
|
||||||
|
|
||||||
|
// 定义货币类型到 HTML 字符的映射
|
||||||
|
const currencySymbols: Record<Currency, string> = {
|
||||||
|
USD: "$", // 美元符号
|
||||||
|
EUR: "€", // 欧元符号
|
||||||
|
GBP: "£", // 英镑符号
|
||||||
|
JPY: "¥", // 日元符号
|
||||||
|
CNY: "¥", // 人民币符号(与日元相同)
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export function CurrencyTypeToSymbol(currencyType: string){
|
||||||
|
return currencySymbols[currencyType] || "";
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user