feat:初始化工程

This commit is contained in:
2026-06-14 22:53:02 +08:00
commit 795c987d9d
63 changed files with 6276 additions and 0 deletions

5
.eslintrc.js Normal file
View File

@@ -0,0 +1,5 @@
module.exports = {
extends: [
'./node_modules/vue3-eslint'
]
}

24
.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

3
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar"]
}

5
README.md Normal file
View File

@@ -0,0 +1,5 @@
# Vue 3 + TypeScript + Vite
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).

13
index.html Normal file
View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>sweet-hut-pc-ui</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

28
package.json Normal file
View File

@@ -0,0 +1,28 @@
{
"name": "sweet-hut-pc-ui",
"private": true,
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "vue-tsc -b && vite build",
"preview": "vite preview"
},
"dependencies": {
"@primeuix/themes": "^2.0.3",
"@vueuse/core": "^14.3.0",
"axios": "^1.17.0",
"primeicons": "^7.0.0",
"primevue": "^4.5.5",
"vue": "^3.5.34",
"vue3-eslint": "git+https://gitee.com/Cxx0822/vue3-eslint"
},
"devDependencies": {
"@types/node": "^24.12.3",
"@vitejs/plugin-vue": "^6.0.6",
"@vue/tsconfig": "^0.9.1",
"sass": "^1.101.0",
"typescript": "~6.0.2",
"vite": "^8.0.12",
"vue-tsc": "^3.2.8"
}
}

3808
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

1
public/favicon.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.3 KiB

24
public/icons.svg Normal file
View File

@@ -0,0 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg">
<symbol id="bluesky-icon" viewBox="0 0 16 17">
<g clip-path="url(#bluesky-clip)"><path fill="#08060d" d="M7.75 7.735c-.693-1.348-2.58-3.86-4.334-5.097-1.68-1.187-2.32-.981-2.74-.79C.188 2.065.1 2.812.1 3.251s.241 3.602.398 4.13c.52 1.744 2.367 2.333 4.07 2.145-2.495.37-4.71 1.278-1.805 4.512 3.196 3.309 4.38-.71 4.987-2.746.608 2.036 1.307 5.91 4.93 2.746 2.72-2.746.747-4.143-1.747-4.512 1.702.189 3.55-.4 4.07-2.145.156-.528.397-3.691.397-4.13s-.088-1.186-.575-1.406c-.42-.19-1.06-.395-2.741.79-1.755 1.24-3.64 3.752-4.334 5.099"/></g>
<defs><clipPath id="bluesky-clip"><path fill="#fff" d="M.1.85h15.3v15.3H.1z"/></clipPath></defs>
</symbol>
<symbol id="discord-icon" viewBox="0 0 20 19">
<path fill="#08060d" d="M16.224 3.768a14.5 14.5 0 0 0-3.67-1.153c-.158.286-.343.67-.47.976a13.5 13.5 0 0 0-4.067 0c-.128-.306-.317-.69-.476-.976A14.4 14.4 0 0 0 3.868 3.77C1.546 7.28.916 10.703 1.231 14.077a14.7 14.7 0 0 0 4.5 2.306q.545-.748.965-1.587a9.5 9.5 0 0 1-1.518-.74q.191-.14.372-.293c2.927 1.369 6.107 1.369 8.999 0q.183.152.372.294-.723.437-1.52.74.418.838.963 1.588a14.6 14.6 0 0 0 4.504-2.308c.37-3.911-.63-7.302-2.644-10.309m-9.13 8.234c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.894 0 1.614.82 1.599 1.82.001 1-.705 1.82-1.6 1.82m5.91 0c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.893 0 1.614.82 1.599 1.82 0 1-.706 1.82-1.6 1.82"/>
</symbol>
<symbol id="documentation-icon" viewBox="0 0 21 20">
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="m15.5 13.333 1.533 1.322c.645.555.967.833.967 1.178s-.322.623-.967 1.179L15.5 18.333m-3.333-5-1.534 1.322c-.644.555-.966.833-.966 1.178s.322.623.966 1.179l1.534 1.321"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M17.167 10.836v-4.32c0-1.41 0-2.117-.224-2.68-.359-.906-1.118-1.621-2.08-1.96-.599-.21-1.349-.21-2.848-.21-2.623 0-3.935 0-4.983.369-1.684.591-3.013 1.842-3.641 3.428C3 6.449 3 7.684 3 10.154v2.122c0 2.558 0 3.838.706 4.726q.306.383.713.671c.76.536 1.79.64 3.581.66"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M3 10a2.78 2.78 0 0 1 2.778-2.778c.555 0 1.209.097 1.748-.047.48-.129.854-.503.982-.982.145-.54.048-1.194.048-1.749a2.78 2.78 0 0 1 2.777-2.777"/>
</symbol>
<symbol id="github-icon" viewBox="0 0 19 19">
<path fill="#08060d" fill-rule="evenodd" d="M9.356 1.85C5.05 1.85 1.57 5.356 1.57 9.694a7.84 7.84 0 0 0 5.324 7.44c.387.079.528-.168.528-.376 0-.182-.013-.805-.013-1.454-2.165.467-2.616-.935-2.616-.935-.349-.91-.864-1.143-.864-1.143-.71-.48.051-.48.051-.48.787.051 1.2.805 1.2.805.695 1.194 1.817.857 2.268.649.064-.507.27-.857.49-1.052-1.728-.182-3.545-.857-3.545-3.87 0-.857.31-1.558.8-2.104-.078-.195-.349-1 .077-2.078 0 0 .657-.208 2.14.805a7.5 7.5 0 0 1 1.946-.26c.657 0 1.328.092 1.946.26 1.483-1.013 2.14-.805 2.14-.805.426 1.078.155 1.883.078 2.078.502.546.799 1.247.799 2.104 0 3.013-1.818 3.675-3.558 3.87.284.247.528.714.528 1.454 0 1.052-.012 1.896-.012 2.156 0 .208.142.455.528.377a7.84 7.84 0 0 0 5.324-7.441c.013-4.338-3.48-7.844-7.773-7.844" clip-rule="evenodd"/>
</symbol>
<symbol id="social-icon" viewBox="0 0 20 20">
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M12.5 6.667a4.167 4.167 0 1 0-8.334 0 4.167 4.167 0 0 0 8.334 0"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M2.5 16.667a5.833 5.833 0 0 1 8.75-5.053m3.837.474.513 1.035c.07.144.257.282.414.309l.93.155c.596.1.736.536.307.965l-.723.73a.64.64 0 0 0-.152.531l.207.903c.164.715-.213.991-.84.618l-.872-.52a.63.63 0 0 0-.577 0l-.872.52c-.624.373-1.003.094-.84-.618l.207-.903a.64.64 0 0 0-.152-.532l-.723-.729c-.426-.43-.289-.864.306-.964l.93-.156a.64.64 0 0 0 .412-.31l.513-1.034c.28-.562.735-.562 1.012 0"/>
</symbol>
<symbol id="x-icon" viewBox="0 0 19 19">
<path fill="#08060d" fill-rule="evenodd" d="M1.893 1.98c.052.072 1.245 1.769 2.653 3.77l2.892 4.114c.183.261.333.48.333.486s-.068.089-.152.183l-.522.593-.765.867-3.597 4.087c-.375.426-.734.834-.798.905a1 1 0 0 0-.118.148c0 .01.236.017.664.017h.663l.729-.83c.4-.457.796-.906.879-.999a692 692 0 0 0 1.794-2.038c.034-.037.301-.34.594-.675l.551-.624.345-.392a7 7 0 0 1 .34-.374c.006 0 .93 1.306 2.052 2.903l2.084 2.965.045.063h2.275c1.87 0 2.273-.003 2.266-.021-.008-.02-1.098-1.572-3.894-5.547-2.013-2.862-2.28-3.246-2.273-3.266.008-.019.282-.332 2.085-2.38l2-2.274 1.567-1.782c.022-.028-.016-.03-.65-.03h-.674l-.3.342a871 871 0 0 1-1.782 2.025c-.067.075-.405.458-.75.852a100 100 0 0 1-.803.91c-.148.172-.299.344-.99 1.127-.304.343-.32.358-.345.327-.015-.019-.904-1.282-1.976-2.808L6.365 1.85H1.8zm1.782.91 8.078 11.294c.772 1.08 1.413 1.973 1.425 1.984.016.017.241.02 1.05.017l1.03-.004-2.694-3.766L7.796 5.75 5.722 2.852l-1.039-.004-1.039-.004z" clip-rule="evenodd"/>
</symbol>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

120
src/App.vue Normal file
View File

@@ -0,0 +1,120 @@
<template>
<div class="main-container">
<Menubar :model="items" />
<bill-table />
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import BillTable from './components/BillTable.vue'
const items = ref([
{
label: '主页',
icon: 'pi pi-home'
},
{
label: '家庭资产',
icon: 'iconfont icon-zhangben',
items: [
{
label: '账单记录',
icon: 'iconfont icon-zhangdan'
},
{
label: '资产记录',
icon: 'iconfont icon-zichan'
},
{
label: '家庭账本',
icon: 'iconfont icon-zhangben'
},
{
label: '密码管家',
icon: 'iconfont icon-ai-password'
},
{
label: '奖状证书',
icon: 'iconfont icon-zhengshu'
}
]
},
{
label: '生活点滴',
icon: 'pi pi-search',
items: [
{
label: '美食记录',
icon: 'iconfont icon-canyin'
},
{
label: '旅行记录',
icon: 'iconfont icon-jingdian'
},
{
label: '物品记录',
icon: 'iconfont icon-wupin'
},
{
label: '时光手账',
icon: 'iconfont icon-riji'
},
{
label: '日程安排',
icon: 'iconfont icon-paiban'
},
{
label: '纪念日',
icon: 'iconfont icon-richeng'
}
]
},
{
label: '健康生活',
icon: 'pi pi-search',
items: [
{
label: '运动健康',
icon: 'iconfont icon-yundongjiankang'
},
{
label: '月经记录',
icon: 'iconfont icon-yuejing'
}
]
}
])
</script>
<style lang="scss">
@use 'styles/index.module.scss';
.main-container {
height: 100%;
background-color: #F8FAFC;
display: flex;
flex-direction: column;
gap: 10px;
}
.p-menubar {
background-color: #FFF8F0 !important;
border: none !important;
.p-menubar-root-list {
padding: 5px;
width: 100%;
justify-content: center;
font-size: large;
.p-menubar-item-icon {
font-size: large;
color: lightseagreen;
}
}
}
</style>

36
src/apis/anniversary.ts Normal file
View File

@@ -0,0 +1,36 @@
import { cloudService } from './index'
import type { IAnniversary } from '@/types/anniversary'
import type { IStatsChart } from '@/types'
export const addAnniversaryApi = (anniversary: IAnniversary): Promise<boolean> =>
cloudService({
url: '/home-api/anniversary',
method: 'post',
data: anniversary
})
export const updateAnniversaryApi = (id: number, anniversary: IAnniversary): Promise<boolean> =>
cloudService({
url: `/home-api/anniversary/${id}`,
method: 'put',
data: anniversary
})
export const deleteAnniversaryApi = (id: number): Promise<boolean> =>
cloudService({
url: `/home-api/anniversary/${id}`,
method: 'delete'
})
export const queryAnniversaryApi = (category: string): Promise<IAnniversary[]> =>
cloudService({
url: '/home-api/anniversary',
method: 'get',
params: { category }
})
export const queryAnniversaryCategoryApi = (): Promise<IStatsChart[]> =>
cloudService({
url: '/home-api/anniversary/category',
method: 'get'
})

35
src/apis/asset.ts Normal file
View File

@@ -0,0 +1,35 @@
import { cloudService } from './index'
import type { IAssetRecord } from '@/types/asset'
export const addAssetApi = (asset: IAssetRecord): Promise<boolean> =>
cloudService({
url: '/home-api/asset',
method: 'post',
data: asset
})
export const updateAssetApi = (id: number, asset: IAssetRecord): Promise<boolean> =>
cloudService({
url: `/home-api/asset/${id}`,
method: 'put',
data: asset
})
export const deleteAssetApi = (id: number): Promise<boolean> =>
cloudService({
url: `/home-api/asset/${id}`,
method: 'delete'
})
export const queryAssetApi = (category: string): Promise<IAssetRecord[]> =>
cloudService({
url: '/home-api/asset',
method: 'get',
params: { category }
})
export const queryAssetCategoryApi = (): Promise<string[]> =>
cloudService({
url: '/home-api/asset/category',
method: 'get'
})

35
src/apis/award.ts Normal file
View File

@@ -0,0 +1,35 @@
import { cloudService } from './index'
import type { IAward, IAwardQuery } from '@/types/award.ts'
export const addAwardApi = (award: IAward): Promise<boolean> =>
cloudService({
url: '/home-api/award',
method: 'post',
data: award
})
export const updateAwardApi = (id: number, award: IAward): Promise<boolean> =>
cloudService({
url: `/home-api/award/${id}`,
method: 'put',
data: award
})
export const deleteAwardApi = (id: number): Promise<boolean> =>
cloudService({
url: `/home-api/award/${id}`,
method: 'delete'
})
export const queryAwardApi = (id: number): Promise<IAward> =>
cloudService({
url: `/home-api/award/${id}`,
method: 'get'
})
export const queryAwardListApi = (query: IAwardQuery): Promise<IAward[]> =>
cloudService({
url: '/home-api/award',
method: 'get',
params: query
})

110
src/apis/bill.ts Normal file
View File

@@ -0,0 +1,110 @@
import { cloudService } from './index'
import type { IBillBook, IBillCategory, IBillPay, IBillQuery, IBillRecord, IBillSearch } from '@/types/bill'
import type { IStatsChart } from '@/types'
export const addBillRecordApi = (billRecord: IBillRecord): Promise<boolean> =>
cloudService({
url: '/home-api/bill',
method: 'post',
data: billRecord
})
export const updateBillRecordApi = (id: number, billRecord: IBillRecord): Promise<boolean> =>
cloudService({
url: `/home-api/bill/${id}`,
method: 'put',
data: billRecord
})
export const deleteBillRecordApi = (id: number): Promise<boolean> =>
cloudService({
url: `/home-api/bill/${id}`,
method: 'DELETE'
})
export const queryBillRecordApi = (id: number): Promise<IBillRecord> =>
cloudService({
url: `/home-api/bill/${id}`,
method: 'get'
})
export const queryBillSummaryApi = (query: IBillQuery): Promise<IBillRecord[]> =>
cloudService({
url: '/home-api/bill/summary',
method: 'get',
params: query
})
export const searchBillSummaryApi = (search: IBillSearch): Promise<IBillRecord[]> =>
cloudService({
url: '/home-api/bill/search',
method: 'get',
params: search
})
export const queryBillStatsApi = (type: string, query: IBillQuery): Promise<IStatsChart[]> =>
cloudService({
url: `/home-api/bill/stats/${type}`,
method: 'get',
params: query
})
export const queryBillPayApi = (): Promise<IBillPay[]> =>
cloudService({
url: '/home-api/bill/pay',
method: 'get'
})
export const queryBillBookApi = (): Promise<IBillBook[]> =>
cloudService({
url: '/home-api/bill/book',
method: 'get'
})
export const queryBillCategoryApi = (): Promise<IBillCategory[]> =>
cloudService({
url: '/home-api/bill/category',
method: 'get'
})
export const addBillPayApi = (billPay: IBillPay): Promise<boolean> =>
cloudService({
url: '/home-api/bill-manage/pay',
method: 'post',
data: billPay
})
export const updateBillPayApi = (id: number, billPay: IBillPay): Promise<boolean> =>
cloudService({
url: `/home-api/bill-manage/pay/${id}`,
method: 'put',
data: billPay
})
export const addBillBookApi = (billBook: IBillBook): Promise<boolean> =>
cloudService({
url: '/home-api/bill-manage/book',
method: 'post',
data: billBook
})
export const updateBillBookApi = (id: number, billBook: IBillBook): Promise<boolean> =>
cloudService({
url: `/home-api/bill-manage/book/${id}`,
method: 'put',
data: billBook
})
export const addBillCategoryApi = (billCategory: IBillCategory): Promise<boolean> =>
cloudService({
url: '/home-api/bill-manage/category',
method: 'post',
data: billCategory
})
export const updateBillCategoryApi = (id: number, billCategory: IBillCategory): Promise<boolean> =>
cloudService({
url: `/home-api/bill-manage/category/${id}`,
method: 'put',
data: billCategory
})

52
src/apis/blog.ts Normal file
View File

@@ -0,0 +1,52 @@
import { cloudService } from './index'
import type { IBlog, IBlogCategory, IBlogComment, IBlogStats, IQueryBlog } from '@/types/blog'
import qs from 'qs'
import type { IPageRecord } from '@/types'
export const queryBlogApi = (currentPage: number, pageSize: number): Promise<IPageRecord<IBlog>> =>
cloudService({
url: '/blog-api/blog/page',
method: 'get',
params: { currentPage, pageSize }
})
export const queryBlogByConditionApi = (query: IQueryBlog): Promise<IBlog[]> =>
cloudService({
url: '/blog-api/blog/condition',
method: 'get',
params: query,
paramsSerializer: (params) => {
return qs.stringify(params, { arrayFormat: 'indices' })
}
})
export const queryBlogByIdApi = (id: number): Promise<IBlog> =>
cloudService({
url: `/blog-api/blog/${id}/content`,
method: 'get'
})
export const queryBlogCategoryApi = (): Promise<IBlogCategory[]> =>
cloudService({
url: '/blog-api/blog/category',
method: 'get'
})
export const queryBlogStatsApi = (): Promise<IBlogStats> =>
cloudService({
url: '/blog-api/blog/stats',
method: 'get'
})
export const queryLatestBlogApi = (): Promise<IBlog[]> =>
cloudService({
url: '/blog-api/blog/latest',
method: 'get'
})
export const queryBlogCommentApi = (blogId: number): Promise<IBlogComment[]> =>
cloudService({
url: `/blog-api/blog/${blogId}/comment`,
method: 'get'
})

34
src/apis/file.ts Normal file
View File

@@ -0,0 +1,34 @@
import { cloudService } from './index'
import type { IFileInfo } from '@/types/file'
import type { AxiosProgressEvent } from 'axios'
export const uploadFileApi = (form: FormData, path: string, md5: string): Promise<any> =>
cloudService({
url: '/home-api/file',
headers: {
'Content-Type': 'multipart/form-data'
},
method: 'post',
params: { path, md5 },
data: form
})
export const uploadAListFileApi = (form: FormData,
progress: (event: AxiosProgressEvent) => void): Promise<string> =>
cloudService({
url: '/home-api/file/alist',
headers: {
'Content-Type': 'multipart/form-data'
},
onUploadProgress: progress,
timeout: 0,
method: 'post',
data: form
})
export const getFolderInfoApi = (folderName: string): Promise<IFileInfo[]> =>
cloudService({
url: '/home-api/file/catalog',
method: 'get',
params: { folderName }
})

89
src/apis/food.ts Normal file
View File

@@ -0,0 +1,89 @@
import { cloudService } from './index'
import type { IFoodQuery, IFoodRecipe, IFoodRecord, IFoodStats } from '@/types/food'
import type { IPageRecord } from '@/types'
export const addFoodRecipeApi = (foodRecipe: IFoodRecipe): Promise<boolean> =>
cloudService({
url: '/home-api/food/recipe',
method: 'post',
data: foodRecipe
})
export const updateFoodRecipeApi = (id: number, foodRecipe: IFoodRecipe): Promise<boolean> =>
cloudService({
url: `/home-api/food/recipe/${id}`,
method: 'put',
data: foodRecipe
})
export const deleteFoodRecipeApi = (id: number): Promise<boolean> =>
cloudService({
url: `/home-api/food/recipe/${id}`,
method: 'DELETE'
})
export const queryFoodRecipeByIdApi = (id: number): Promise<IFoodRecipe> =>
cloudService({
url: `/home-api/food/recipe/${id}`,
method: 'get'
})
export const queryFoodRecipeApi = (currentPage: number, pageSize: number,
query: IFoodQuery): Promise<IPageRecord<IFoodRecipe>> =>
cloudService({
url: '/home-api/food/recipe/page',
method: 'get',
params: { currentPage, pageSize, ...query }
})
export const searchFoodRecipeApi = (name: string): Promise<IFoodRecipe> =>
cloudService({
url: '/home-api/food/recipe/search',
method: 'get',
params: { name }
})
export const queryFoodNameListApi = (): Promise<string[]> =>
cloudService({
url: '/home-api/food/recipe/name',
method: 'get'
})
export const addFoodRecordApi = (foodRecord: IFoodRecord): Promise<boolean> =>
cloudService({
url: '/home-api/food/record',
method: 'post',
data: foodRecord
})
export const updateFoodRecordApi = (id: number, foodRecord: IFoodRecord): Promise<boolean> =>
cloudService({
url: `/home-api/food/record/${id}`,
method: 'put',
data: foodRecord
})
export const deleteFoodRecordApi = (id: number): Promise<boolean> =>
cloudService({
url: `/home-api/food/record/${id}`,
method: 'DELETE'
})
export const queryFoodRecordApi = (startDate: string, endDate: string): Promise<IFoodRecord[]> =>
cloudService({
url: '/home-api/food/record',
method: 'get',
params: { startDate, endDate }
})
export const queryFoodCategoryApi = (): Promise<string[]> =>
cloudService({
url: '/home-api/food/category',
method: 'get'
})
export const queryFoodStatsApi = (): Promise<IFoodStats> =>
cloudService({
url: '/home-api/food/stats',
method: 'get'
})

16
src/apis/health.ts Normal file
View File

@@ -0,0 +1,16 @@
import { cloudService } from './index'
import type { IHealth, IHealthQuery } from '@/types/health'
export const updateHealthApi = (id: number, health: IHealth): Promise<boolean> =>
cloudService({
url: `/home-api/health/${id}`,
method: 'put',
data: health
})
export const queryHealthApi = (query: IHealthQuery): Promise<IHealth[]> =>
cloudService({
url: '/home-api/health',
method: 'get',
params: query
})

3
src/apis/index.ts Normal file
View File

@@ -0,0 +1,3 @@
import createService from '@/utils/axiosUtil'
export const cloudService = createService()

36
src/apis/journal.ts Normal file
View File

@@ -0,0 +1,36 @@
import { cloudService } from './index'
import type { IStatsChart } from '@/types'
import type { IJournal, IJournalQuery } from '@/types/journal.ts'
export const addJournalApi = (journal: IJournal): Promise<boolean> =>
cloudService({
url: '/home-api/journal',
method: 'post',
data: journal
})
export const updateJournalApi = (id: number, journal: IJournal): Promise<boolean> =>
cloudService({
url: `/home-api/journal/${id}`,
method: 'put',
data: journal
})
export const deleteJournalApi = (id: number): Promise<boolean> =>
cloudService({
url: `/home-api/journal/${id}`,
method: 'delete'
})
export const queryJournalApi = (query: IJournalQuery): Promise<IJournal[]> =>
cloudService({
url: '/home-api/journal',
method: 'get',
params: { ...query }
})
export const queryJournalCategoryApi = (): Promise<IStatsChart[]> =>
cloudService({
url: '/home-api/journal/category',
method: 'get'
})

29
src/apis/kpi.ts Normal file
View File

@@ -0,0 +1,29 @@
import { cloudService } from './index'
import type { IKpi, IKpiQuery } from '@/types/kpi'
export const addKpiApi = (kpi: IKpi): Promise<boolean> =>
cloudService({
url: '/home-api/kpi',
method: 'post',
data: kpi
})
export const updateKpiApi = (id: number, kpi: IKpi): Promise<boolean> =>
cloudService({
url: `/home-api/kpi/${id}`,
method: 'put',
data: kpi
})
export const deleteKpiApi = (id: number): Promise<boolean> =>
cloudService({
url: `/home-api/kpi/${id}`,
method: 'delete'
})
export const queryKpiApi = (query: IKpiQuery): Promise<IKpi[]> =>
cloudService({
url: '/home-api/kpi',
method: 'get',
params: { ...query }
})

35
src/apis/ledger.ts Normal file
View File

@@ -0,0 +1,35 @@
import { cloudService } from './index'
import type { ILedgerRecord } from '@/types/ledger'
export const addLedgerApi = (ledger: ILedgerRecord): Promise<boolean> =>
cloudService({
url: '/home-api/ledger',
method: 'post',
data: ledger
})
export const updateLedgerApi = (id: number, ledger: ILedgerRecord): Promise<boolean> =>
cloudService({
url: `/home-api/ledger/${id}`,
method: 'put',
data: ledger
})
export const deleteLedgerApi = (id: number): Promise<boolean> =>
cloudService({
url: `/home-api/ledger/${id}`,
method: 'delete'
})
export const queryLedgerApi = (book: string): Promise<ILedgerRecord[]> =>
cloudService({
url: '/home-api/ledger',
method: 'get',
params: { book }
})
export const queryLedgerBookApi = (): Promise<string[]> =>
cloudService({
url: '/home-api/ledger/book',
method: 'get'
})

35
src/apis/password.ts Normal file
View File

@@ -0,0 +1,35 @@
import { cloudService } from './index'
import type { IPassword, IPasswordQuery } from '@/types/password'
export const addPasswordApi = (password: IPassword): Promise<boolean> =>
cloudService({
url: '/home-api/password',
method: 'post',
data: password
})
export const updatePasswordApi = (id: number, password: IPassword): Promise<boolean> =>
cloudService({
url: `/home-api/password/${id}`,
method: 'put',
data: password
})
export const deletePasswordApi = (id: number): Promise<boolean> =>
cloudService({
url: `/home-api/password/${id}`,
method: 'delete'
})
export const queryPasswordApi = (query: IPasswordQuery): Promise<IPassword[]> =>
cloudService({
url: '/home-api/password',
method: 'get',
params: query
})
export const queryPasswordCategoryApi = (): Promise<string[]> =>
cloudService({
url: '/home-api/password/category',
method: 'get'
})

42
src/apis/period.ts Normal file
View File

@@ -0,0 +1,42 @@
import { cloudService } from './index'
import type { IMenstrualPeriod, IPeriodDay, IPeriodSetting } from '@/types/period.ts'
export const updatePeriodDayApi = (id: number, periodDay: IPeriodDay): Promise<boolean> =>
cloudService({
url: `/home-api/period/day/${id}`,
method: 'put',
data: periodDay
})
export const queryPeriodDayApi = (month: string): Promise<IPeriodDay[]> =>
cloudService({
url: '/home-api/period/day',
method: 'get',
params: { month }
})
export const getMenstrualDaysApi = (): Promise<IMenstrualPeriod[]> =>
cloudService({
url: '/home-api/period//menstruate/days',
method: 'get'
})
export const getMenstruateFirstDateApi = (month: string): Promise<string> =>
cloudService({
url: '/home-api/period/menstruate',
method: 'get',
params: { month }
})
export const updatePeriodSettingApi = (id: number, setting: IPeriodSetting): Promise<boolean> =>
cloudService({
url: `/home-api/period/setting/${id}`,
method: 'put',
data: setting
})
export const queryPeriodSettingApi = (): Promise<IPeriodSetting> =>
cloudService({
url: '/home-api/period/setting',
method: 'get'
})

42
src/apis/plan.ts Normal file
View File

@@ -0,0 +1,42 @@
import { cloudService } from './index'
import type { IPlanInfo, IPlanQuery } from '@/types/plan.ts'
export const addPlanApi = (plan: IPlanInfo): Promise<any> =>
cloudService({
url: '/home-api/plan',
method: 'post',
data: plan
})
export const updatePlanApi = (id: number, plan: IPlanInfo): Promise<any> =>
cloudService({
url: `/home-api/plan/${id}`,
method: 'put',
data: plan
})
export const setPlanStatusApi = (id: number, status: boolean): Promise<any> =>
cloudService({
url: `/home-api/plan/${id}/status`,
method: 'put',
params: { status }
})
export const deletePlanApi = (id: number): Promise<any> =>
cloudService({
url: `/home-api/plan/${id}`,
method: 'delete'
})
export const queryPlanApi = (query: IPlanQuery): Promise<any> =>
cloudService({
url: '/home-api/plan',
method: 'get',
params: query
})
export const queryPlanStatsApi = (): Promise<any> =>
cloudService({
url: '/home-api/plan/stats',
method: 'get'
})

41
src/apis/product.ts Normal file
View File

@@ -0,0 +1,41 @@
import { cloudService } from './index'
import type { IProduct, IProductQuery } from '@/types/product.ts'
export const addProductApi = (product: IProduct): Promise<boolean> =>
cloudService({
url: '/home-api/product',
method: 'post',
data: product
})
export const updateProductApi = (id: number, product: IProduct): Promise<boolean> =>
cloudService({
url: `/home-api/product/${id}`,
method: 'put',
data: product
})
export const deleteProductApi = (id: number): Promise<boolean> =>
cloudService({
url: `/home-api/product/${id}`,
method: 'delete'
})
export const queryProductApi = (id: number): Promise<IProduct> =>
cloudService({
url: `/home-api/product/${id}`,
method: 'get'
})
export const queryProductListApi = (query: IProductQuery): Promise<IProduct[]> =>
cloudService({
url: '/home-api/product',
method: 'get',
params: query
})
export const queryProductCategoryApi = (): Promise<string[]> =>
cloudService({
url: '/home-api/product/category',
method: 'get'
})

8
src/apis/quartz.ts Normal file
View File

@@ -0,0 +1,8 @@
import { cloudService } from './index'
import type { IQuartzInfo } from '@/types/quartz'
export const queryQuartzApi = (): Promise<IQuartzInfo[]> =>
cloudService({
url: '/home-api/quartz',
method: 'get'
})

16
src/apis/session.ts Normal file
View File

@@ -0,0 +1,16 @@
import { cloudService } from './index'
import type { ISession } from '@/types/auth'
export const loginHomeApi = (name: string, password: string): Promise<ISession> =>
cloudService({
url: '/home-api/session',
method: 'post',
params: { name, password }
})
export const loginAdminApi = (name: string, password: string): Promise<ISession> =>
cloudService({
url: '/admin-api/session',
method: 'post',
params: { name, password }
})

82
src/apis/travel.ts Normal file
View File

@@ -0,0 +1,82 @@
import { cloudService } from './index'
import type { ITravelQuery, ITravelRecord, ITravelSpot, ITravelStats } from '@/types/travel'
export const addTravelSpotApi = (travelSpot: ITravelSpot): Promise<boolean> =>
cloudService({
url: '/home-api/travel/spot',
method: 'post',
data: travelSpot
})
export const updateTravelSpotApi = (id: number, travelSpot: ITravelSpot): Promise<boolean> =>
cloudService({
url: `/home-api/travel/spot/${id}`,
method: 'put',
data: travelSpot
})
export const deleteTravelSpotApi = (id: number): Promise<boolean> =>
cloudService({
url: `/home-api/travel/spot/${id}`,
method: 'DELETE'
})
export const queryTravelSpotApi = (id: number): Promise<ITravelSpot> =>
cloudService({
url: `/home-api/travel/spot/${id}`,
method: 'get'
})
export const queryTravelSpotListApi = (currentPage: number, pageSize: number,
query: ITravelQuery): Promise<any> =>
cloudService({
url: '/home-api/travel/spot/page',
method: 'get',
params: { currentPage, pageSize, ...query }
})
export const queryTravelSpotNameListApi = (): Promise<string[]> =>
cloudService({
url: '/home-api/travel/spot/name',
method: 'get'
})
export const addTravelRecordApi = (travelRecord: ITravelRecord): Promise<boolean> =>
cloudService({
url: '/home-api/travel/record',
method: 'post',
data: travelRecord
})
export const updateTravelRecordApi = (id: number, travelRecord: ITravelRecord): Promise<boolean> =>
cloudService({
url: `/home-api/travel/record/${id}`,
method: 'put',
data: travelRecord
})
export const deleteTravelRecordApi = (id: number): Promise<boolean> =>
cloudService({
url: `/home-api/travel/record/${id}`,
method: 'DELETE'
})
export const queryTravelRecordApi = (startDate: string, endDate: string):
Promise<ITravelRecord[]> =>
cloudService({
url: '/home-api/travel/record',
method: 'get',
params: { startDate, endDate }
})
export const queryTravelCategoryApi = (): Promise<string[]> =>
cloudService({
url: '/home-api/travel/category',
method: 'get'
})
export const queryTravelStatsApi = (): Promise<ITravelStats> =>
cloudService({
url: '/home-api/travel/stats',
method: 'get'
})

15
src/apis/user.ts Normal file
View File

@@ -0,0 +1,15 @@
import { cloudService } from './index'
import type { IUser } from '@/types/auth.ts'
export const queryUserApi = (id: number): Promise<IUser> =>
cloudService({
url: `/home-api/user/${id}`,
method: 'get'
})
export const updateUserApi = (id: number, user: IUser): Promise<boolean> =>
cloudService({
url: `/home-api/user/${id}`,
method: 'put',
data: user
})

Binary file not shown.

BIN
src/assets/hero.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

1
src/assets/vite.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.5 KiB

1
src/assets/vue.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

After

Width:  |  Height:  |  Size: 496 B

View File

@@ -0,0 +1,147 @@
<template>
<div class="bill-container">
<Toolbar class="mb-6">
<template #start>
<Button label="新增" icon="pi pi-plus" class="mr-2" />
</template>
<template #center>
<DatePicker v-model="dates" selectionMode="range"
:manualInput="false" dateFormat="yy-mm-dd"
style="width: 220px;"/>
</template>
<template #end>
<IconField>
<InputIcon>
<i class="pi pi-search" />
</InputIcon>
<InputText v-model="filters['global'].value" placeholder="全局搜索" />
</IconField>
</template>
</Toolbar>
<DataTable :value="billRecordList" :loading="loading"
v-model:filters="filters"
:globalFilterFields="['bookName', 'type', 'category', 'content', 'date', 'payAccount', 'location']"
showGridlines stripedRows
scrollable scrollHeight="70vh"
paginator :rows="10" :rowsPerPageOptions="[5, 10, 20, 50]"
paginatorTemplate="RowsPerPageDropdown FirstPageLink PrevPageLink CurrentPageReport NextPageLink LastPageLink"
currentPageReportTemplate="第 {first} 到 {last}条, 共 {totalRecords} 条">
<template #paginatorstart>
<Button type="button" icon="pi pi-refresh" text label="刷新" @click="refreshClick()"/>
</template>
<template #paginatorend>
<Button type="button" icon="pi pi-download" text label="下载"/>
</template>
<Column field="bookName" header="账本名称" style="width: 8rem"></Column>
<Column field="type" header="账单类型" style="width: 8rem">
<template #body="slotProps">
<Tag v-if="slotProps.data.type === 'expensive'" severity="info" value="支出" />
<Tag v-else severity="success" value="收入" />
</template>
</Column>
<Column field="category" header="账单类别" style="width: 8rem"></Column>
<Column field="content" header="账单内容" style="min-width: 5rem"></Column>
<Column field="date" header="账单日期" style="width: 8rem"></Column>
<Column field="payAccount" header="支付账户" style="width: 8rem"></Column>
<Column field="location" header="账单地点" style="min-width: 5rem"></Column>
<Column field="amount" header="账单金额" sortable style="width: 8rem"></Column>
<Column :exportable="false" style="width: 14rem">
<template #body="slotProps">
<Button icon="pi pi-pencil" label="编辑" style="margin-right: 1rem;"
@click="editClick(slotProps.data)" />
<Button icon="pi pi-trash" label="删除" severity="danger"
@click="deleteClick(slotProps.data)" />
</template>
</Column>
</DataTable>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import DataTable from 'primevue/datatable'
import Column from 'primevue/column'
import DatePicker from 'primevue/datepicker'
import IconField from 'primevue/iconfield'
import InputIcon from 'primevue/inputIcon'
import InputText from 'primevue/inputText'
import { queryBillSummaryApi } from '@/apis/bill'
import { loginHomeApi } from '@/apis/session'
import { useStorage } from '@vueuse/core'
import type { IBillRecord } from '@/types/bill'
import { FilterMatchMode } from '@primevue/core/api'
onMounted(async () => {
const response = await loginHomeApi('Cxx0822', '19940822Cxx')
useStorage('satoken', response.saToken.tokenValue)
dates.value = [new Date(), new Date()]
await refreshClick()
})
const loading = ref(true)
const dates = ref()
const filters = ref({
global: { value: null, matchMode: FilterMatchMode.CONTAINS },
bookName: { value: null, matchMode: FilterMatchMode.CONTAINS },
type: { value: null, matchMode: FilterMatchMode.CONTAINS },
category: { value: null, matchMode: FilterMatchMode.CONTAINS },
content: { value: null, matchMode: FilterMatchMode.CONTAINS },
date: { value: null, matchMode: FilterMatchMode.CONTAINS },
payAccount: { value: null, matchMode: FilterMatchMode.CONTAINS },
location: { value: null, matchMode: FilterMatchMode.CONTAINS }
})
const billRecordList = ref<IBillRecord[]>([])
const refreshClick = async () => {
loading.value = true
billRecordList.value = await queryBillSummaryApi({
startDate: '2026-05-01',
endDate: '2026-07-01'
})
loading.value = false
}
const editClick = (billRecord: IBillRecord) => {
}
const deleteClick = (billRecord: IBillRecord) => {
}
</script>
<style lang="scss">
.bill-container {
height: 100%;
padding: 2rem;
}
.p-datatable {
.p-datatable-header-cell {
background-color: lightseagreen;
color: white;
.p-datatable-column-header-content {
justify-content: center;
.p-datatable-sort-icon {
color: white;
}
}
}
.p-datatable-tbody {
td {
text-align: center;
}
}
}
</style>

25
src/main.ts Normal file
View File

@@ -0,0 +1,25 @@
import { createApp } from 'vue'
import App from './App.vue'
import PrimeVue from 'primevue/config'
import zhCN from '@/utils/zhcn'
import Aura from '@primeuix/themes/aura'
import Button from 'primevue/button'
import Menubar from 'primevue/menubar'
import Tag from 'primevue/tag'
import Toolbar from 'primevue/toolbar'
import 'primeicons/primeicons.css'
const app = createApp(App)
app.use(PrimeVue, {
theme: {
preset: Aura
},
local: zhCN
})
app.component('Button', Button)
app.component('Menubar', Menubar)
app.component('Tag', Tag)
app.component('Toolbar', Toolbar)
app.mount('#app')

View File

@@ -0,0 +1,85 @@
body {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
font-family: CustomFont, sans-serif;
}
*,
*:before,
*:after {
box-sizing: border-box;
}
a:focus,
a:active {
outline: none;
}
a,
a:focus,
a:hover {
cursor: pointer;
color: inherit;
text-decoration: none;
}
html, body {
height: 100%;
margin: 0;
padding: 0;
}
#app {
height: 100%;
}
html {
font-size: 16px;
}
span, input {
font-family: 'CustomFont', serif;
}
// 明亮主题变量
:root {
// 基础颜色
--color-bg: #F5F5F5;
--color-card-bg: #FFFFFF;
--color-record-card-bg: #F5F5DC;
--color-border-bg: #FFFFFF;
--color-text: #000000;
}
// 暗黑主题变量
.dark {
--color-bg: #000000;
--color-card-bg: #000000;
--color-record-card-bg: #4C4D4F;
--color-border-bg: #4C4D4F;
--color-text: #FFFFFF;
}
::view-transition-old(root),
::view-transition-new(root) {
animation: none;
}
.dark::view-transition-old(root) {
z-index: 9999;
}
@media (max-width: 1280px) {
html {
font-size: 13px;
}
}
@font-face {
font-family: 'CustomFont';
src: url('@/assets/font/custom.woff2') format('truetype');
font-weight: normal;
font-style: normal;
}
@import "//at.alicdn.com/t/c/font_4793264_7l6epj0mpz4.css";

33
src/types/anniversary.ts Normal file
View File

@@ -0,0 +1,33 @@
/**
* 纪念日
*/
export interface IAnniversary {
id?: number;
/**
* 标题
*/
title: string;
/**
* 日期
*/
date: string;
/**
* 是否重复
*/
recurring: boolean;
/**
* 类型
*/
type: 'countUp' | 'countDown';
/**
* 分类
*/
category: string;
}
export interface IAnniversaryCategory {
name: string;
title: string;
subTitle: string;
color: string;
}

62
src/types/asset.ts Normal file
View File

@@ -0,0 +1,62 @@
export interface IAssetItem {
id: number;
/**
* 名称
*/
name: string;
/**
* 备注
*/
remark: string;
/**
* 所属人
*/
owner: string;
/**
* 金额
*/
amount: number;
}
/**
* 资产
*/
export interface IAsset {
/**
* 分类
*/
category: string;
totalAmount: number;
itemList: IAssetItem[];
}
export interface IAssetRecord {
id?: number;
/**
* 分类
*/
category: string;
/**
* 名称
*/
name: string;
/**
* 备注
*/
remark: string;
/**
* 所属人
*/
owner: string;
/**
* 金额
*/
amount: number;
}
export interface IAssetCategory {
name: string;
title: string;
subTitle: string;
color: string;
}

48
src/types/auth.ts Normal file
View File

@@ -0,0 +1,48 @@
export type ILoginType = 'common' | 'phone' | 'email' | 'admin'
export type IAccountType = 'QQ' | 'WeChat' | 'Phone' | 'EMail'
export interface IUser {
id: number;
accountId: number;
accountName: string;
username: string;
gender: number;
idNumber: string;
phoneNumber: string;
email: string;
birthDate: string;
avatar: string;
description: string;
area: string[];
address: string;
job: string;
tags: string[];
}
export interface ISession {
saToken: {
tokenName: string;
tokenValue: string;
isLogin: boolean;
loginId: string;
loginType: string;
tokenTimeout: number;
sessionTimeout: number;
tokenSessionTimeout: number;
tokenActiveTimeout: number;
loginDevice: string;
tag: string;
};
userInfo: IUser;
}
export interface IAccount {
id: number;
accountName: string;
password: string;
confirmPassword: string;
username: string;
state: number;
date: string;
}

14
src/types/award.ts Normal file
View File

@@ -0,0 +1,14 @@
export interface IAward {
id: number;
name: string;
type: string;
owner: string;
location: string;
date: string;
imageList: string[];
remark: string;
}
export interface IAwardQuery {
type: string;
}

80
src/types/bill.ts Normal file
View File

@@ -0,0 +1,80 @@
export type IBillType = 'expensive' | 'income' | ''
/**
* 账单记录
*/
export interface IBillRecord {
id?: number;
bookName: string;
type: IBillType | string;
category: string;
date: string;
location: string;
payAccount: string;
amount: number;
content: string;
remark: string;
imageList: string[];
}
/**
* 账户类型
*/
export interface IBillPay {
id?: number;
name: string;
icon: string;
color?: string;
}
/**
* 账本类型
*/
export interface IBillBook {
id?: number;
name: string;
label?: string;
icon: string;
color?: string;
}
/**
* 账单类别
*/
export interface IBillCategory {
id?: number;
bookName: string;
name: string;
type: string;
icon: string;
color?: string;
}
/**
* 查询账单
*/
export interface IBillQuery {
bookName?: string;
type?: IBillType;
startDate: string;
endDate: string;
}
export interface IBillSearch {
content?: string;
category?: string;
location?: string;
}
export interface IBillCalendarDaily {
dailyRecordList: IBillRecord[];
totalExpensive: number;
totalIncome: number;
}
export interface IBillSummaryStats {
expensive: number;
income: number;
balance: number;
rate: number;
}

84
src/types/blog.ts Normal file
View File

@@ -0,0 +1,84 @@
export interface IBlog {
id: number;
title: string;
topValue: number;
isGreat: boolean;
categoryId: number;
category: string;
summary: string;
content: string;
wordCount: number;
readDuration: number;
visitCount: number;
createTime: string;
updateTime: string;
}
export interface INewBlog {
id: number;
title: string;
topValue: number;
isGreat: boolean;
category: string;
content: string;
}
export interface IQueryBlog {
category?: string;
title?: string;
year?: number;
}
export interface IBasicBlog {
category: string;
topValue: number;
isGreat: boolean;
wordCount: number;
readDuration: number;
visitCount: number;
createTime: string;
updateTime: string;
}
export interface IBlogCategory {
name: string;
count: number;
}
export interface IBlogStats {
blogCount: number;
categoryCount: number;
wordCount: number;
}
export interface IBlogLatest {
id: number;
title: string;
}
export interface IBlogVisit {
ip: string;
os: string;
browser: string;
uri: string;
title: string;
visitTime: string;
}
export interface IBlogComment {
id: number;
blogId: number;
name: string;
website: string;
content: string;
ipAddress: string;
userAgent: string;
parentId: number;
isApproved: boolean;
createTime: string;
}
export interface IBlogNestedComment {
comment: IBlogComment;
replies: IBlogComment[];
}

26
src/types/file.ts Normal file
View File

@@ -0,0 +1,26 @@
/**
* 文件记录
*/
export interface IFileRecord {
id: number;
name: string;
md5: string;
path: string;
url: string;
}
/**
* 图片文件
*/
export interface IImageFile {
id: number;
name: string;
url: string;
}
export interface IFileInfo {
name: string;
type: string;
size: string;
createDate: string;
}

77
src/types/food.ts Normal file
View File

@@ -0,0 +1,77 @@
/**
* 食材信息
*/
export interface IFoodMaterial {
type: string;
name: string;
amount: string;
}
/**
* 步骤信息
*/
export interface IFoodStep {
sort: number;
content: string;
imageUrl: string;
}
/**
* 成果信息
*/
export interface IFoodRecord {
id: number;
name: string;
category: string;
date: string;
person: string;
imageUrl: string;
}
/**
* 统计信息
*/
export interface IFoodStats {
recipeCount: number;
categoryCount: number;
workCount: number;
}
/**
* 菜谱信息
*/
export interface IFoodRecipe {
id?: number;
/**
* 美食名称
*/
name: string;
/**
* 美食菜系
*/
category: string;
/**
* 推荐指数
*/
recommendRate: number;
/**
* 备注
*/
remark: string;
/**
* 食材
*/
materialList: IFoodMaterial[];
/**
* 步骤
*/
stepList: IFoodStep[];
/**
* 成果
*/
recordList: IFoodRecord[];
}
export interface IFoodQuery {
category: string;
}

33
src/types/health.ts Normal file
View File

@@ -0,0 +1,33 @@
export interface IHealth {
id?: number;
sportProject: string;
sportDuration: number;
weight: number;
sleepStartTime: string | null;
sleepEndTime: string | null;
sleepDuration: number | null;
date: string;
}
export interface IExercise {
id?: number;
project: string;
duration: number;
date: string;
}
export interface IHealthQuery {
startDate: string;
endDate: string;
}
export interface IWeight {
id?: number;
value: number;
date: string;
}
export interface IWeightQuery {
startDate: string;
endDate: string;
}

31
src/types/index.ts Normal file
View File

@@ -0,0 +1,31 @@
export enum ImagePathEnum {
BLOG= 'blog',
BILL = 'bill',
FOOD = 'food',
TRAVEL = 'travel',
PRODUCT = 'product',
JOURNAL = 'journal',
LEDGER = 'ledger',
AWARD = 'award',
USER = 'user'
}
export interface IPageRecord<T> {
current: number;
size: number;
total: number;
pages: number;
records: T[];
}
export type IDateType = 'month' | 'year'
export interface ICategory {
label: string;
value: string;
}
export interface IStatsChart {
name: string;
value: number;
}

43
src/types/journal.ts Normal file
View File

@@ -0,0 +1,43 @@
/**
* 日记
*/
export interface IJournal {
id?: number;
/**
* 日期
*/
date: string;
/**
* 地点
*/
location: string;
/**
* 分类
*/
category: string;
/**
* 天气
*/
weather: string;
/**
* 心情
*/
mood: string;
/**
* 内容
*/
content: string;
/**
* 图片地址
*/
imageUrl: string;
/**
* 视频地址
*/
videoUrl: string;
}
export interface IJournalQuery {
startDate: string;
endDate: string;
}

27
src/types/kpi.ts Normal file
View File

@@ -0,0 +1,27 @@
/**
* 日记
*/
export interface IKpi {
id?: number;
/**
* 日期
*/
date: string;
/**
* 患者名称
*/
patientName: string;
/**
* 项目
*/
project: string;
/**
* 分类
*/
price: number;
}
export interface IKpiQuery {
startDate: string;
endDate: string;
}

37
src/types/ledger.ts Normal file
View File

@@ -0,0 +1,37 @@
export interface ILegerItem {
id: number;
content: string;
price: number;
number: number;
unit: string;
totalPrice?: number;
payer: string;
provider: string;
imageUrl: string;
remark: string;
}
export interface ILedger {
category: string;
totalAmount: number;
itemList: ILegerItem[];
}
export interface ILedgerRecord {
id: number;
book: string;
category: string;
content: string;
price: number;
number: number;
totalPrice?: number;
unit: string;
payer: string;
provider: string;
imageUrl: string;
remark: string;
}
export interface ILedgerQuery {
book: string;
}

36
src/types/password.ts Normal file
View File

@@ -0,0 +1,36 @@
export interface IPassword {
id?: number;
/**
* 标题
*/
title: string;
/**
* 网站
*/
website: string;
/**
* 账号
*/
username: string;
/**
* 密码
*/
password: string;
/**
* 分类
*/
category: string;
/**
* 所属人
*/
owner: string;
/**
* 备注
*/
remark: string;
}
export interface IPasswordQuery {
category: string;
owner: string;
}

42
src/types/period.ts Normal file
View File

@@ -0,0 +1,42 @@
/**
* 月经天
*/
export interface IPeriodDay {
id?: number;
/**
* 当前日期
*/
date: string;
/**
* 是否来月经
*/
isMenstruate: boolean;
/**
* 是否吃药
*/
isDrug: boolean;
/**
* 流量
*/
flowLevel: string;
/**
* 症状
*/
crampLevel: string;
/**
* 爱爱
*/
sexActivity: string;
}
export interface IPeriodSetting {
id?: number;
cycleLength: number;
duration: number;
isCalculate: boolean;
}
export interface IMenstrualPeriod{
startDate: string;
endDate: string;
}

65
src/types/plan.ts Normal file
View File

@@ -0,0 +1,65 @@
/**
* 计划记录
*/
export interface IPlanInfo {
id?: number;
/**
* 标题
*/
title: string;
/**
* 内容
*/
content: string;
/**
* 日期
*/
date: string | null;
/**
* 开始时间
*/
startTime: string | null;
/**
* 结束时间
*/
endTime: string | null;
/**
* 提醒时间
*/
alterTime: string | null;
/**
* 邮箱地址
*/
email: string;
/**
* 标签
*/
tag: string;
/**
* 是否置顶
*/
isTop: number;
/**
* 优先级
*/
priority: number;
/**
* 是否已经完成
*/
completed: number;
}
export interface IPlanQuery {
planType: string;
orderType: string;
}
export interface IPlanStats {
totalCount: number;
completedCount: number;
currentCount: number;
remainCount: number;
}

40
src/types/product.ts Normal file
View File

@@ -0,0 +1,40 @@
/**
* 物品
*/
export interface IProduct {
id?: number;
/**
* 标题
*/
name: string;
/**
* 购买日期
*/
purchaseDate: string;
/**
* 到期日期
*/
expiryDate: string;
/**
* 金额
*/
price: number;
/**
* 分类
*/
category: string;
/**
* 购买人
*/
purchaser: string;
/**
* 购买渠道
*/
channel: string;
imageUrl: string;
}
export interface IProductQuery {
category: string;
order: string;
}

12
src/types/quartz.ts Normal file
View File

@@ -0,0 +1,12 @@
/**
* 定时任务信息
*/
export interface IQuartzInfo {
type: string;
content: string;
executeTime: string;
cronExpression: string;
name: string;
group: string;
status: string;
}

30
src/types/server.ts Normal file
View File

@@ -0,0 +1,30 @@
export interface IProcesses {
command: string;
cpu: number;
mem: number;
name: string;
pid: number;
user: string;
}
export interface IServerMonitor {
cpuCores: number;
cpuModel: string;
cpuUsage: number;
diskUsage: number;
memoryUsage: number;
networkSpeed: {
rx: number;
tx: number;
};
systemInfo: string;
systemLoad: {
load1m: number;
load5m: number;
load15m: number;
};
topProcesses: IProcesses[];
totalDisk: number;
totalMemory: number;
uptime: number;
}

44
src/types/travel.ts Normal file
View File

@@ -0,0 +1,44 @@
/**
* 旅行景点
*/
export interface ITravelRecord {
id: number;
name: string;
area: string[];
date: string;
persons: string[];
imageList: string[];
videoList: string[];
}
/**
* 景点信息
*/
export interface ITravelSpot {
id: number;
name: string;
area: string[];
address: string;
price: number;
recommendRate: number;
traffic: string;
router: string;
foods: string[];
projects: string[];
remark: string;
recordList: ITravelRecord[];
}
/**
* 统计信息
*/
export interface ITravelStats {
spotCount: number;
cityCount: number;
recordCount: number;
}
export interface ITravelQuery {
category: string;
}

89
src/utils/axiosUtil.ts Normal file
View File

@@ -0,0 +1,89 @@
import axios, { AxiosError } from 'axios'
import type { InternalAxiosRequestConfig, AxiosResponse } from 'axios'
const createService = (baseURL = '', timeout = 5000) => {
const service = axios.create({
baseURL,
timeout
})
// 请求拦截器
service.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
const token = localStorage.getItem('token')
if (token) {
config.headers.satoken = token
}
return config
},
(error: AxiosError) => {
return Promise.reject(error)
}
)
// 响应拦截器
service.interceptors.response.use(
(response: AxiosResponse) => {
return response.data
},
(error: AxiosError) => {
const { code, message } = error
if (error.response) {
// 发送请求成功 并收到服务器的响应
handleServiceError(error.response, message)
return Promise.reject(error)
} else if (error.request) {
// 发送请求成功 未收到服务器的响应
handleHttpError(code, message)
return Promise.reject(error)
} else {
// 其他错误情况 如发送请求失败等
console.error(`前端错误 请联系管理人员:${message}`)
}
}
)
return service
}
/**
* 处理服务器响应错误
* @param response 服务器响应
* @param message 错误消息
*/
const handleServiceError = (response: AxiosResponse, message: string) => {
const { status, data, statusText } = response
switch (status) {
case 401:
console.error('账号已过期 请重新登录')
// window.location.href = '/'
break
case 500:
console.error(`${data.message || statusText}`)
break
default:
console.error(`服务器错误 请联系管理人员:${status}:${message}`)
}
}
/**
* 处理Http错误
* @param code code码
* @param message 消息
*/
const handleHttpError = (code: string | undefined, message: string) => {
switch (code) {
case 'ECONNABORTED':
case 'ETIMEDOUT':
console.error('网络连接错误 请联系管理人员')
break
default:
console.error(`网络错误 请联系管理人员:${code}:${message}`)
break
}
}
export default createService

147
src/utils/zhcn.ts Normal file
View File

@@ -0,0 +1,147 @@
const zhCN = {
startsWith: '以开头',
contains: '包含',
notContains: '不包含',
endsWith: '以结尾',
equals: '等于',
notEquals: '不等于',
noFilter: '无筛选',
lt: '小于',
lte: '小于等于',
gt: '大于',
gte: '大于等于',
dateIs: '日期是',
dateIsNot: '日期不是',
dateBefore: '日期之前',
dateAfter: '日期之后',
clear: '清除',
apply: '应用',
matchAll: '全匹配',
matchAny: '任一匹配',
addRule: '添加规则',
removeRule: '移除规则',
accept: '是',
reject: '否',
choose: '选择',
upload: '上传',
cancel: '取消',
completed: '已完成',
pending: '等待中',
fileSizeTypes: ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
dayNames: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
dayNamesShort: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
dayNamesMin: ['日', '一', '二', '三', '四', '五', '六'],
monthNames: [
'一月',
'二月',
'三月',
'四月',
'五月',
'六月',
'七月',
'八月',
'九月',
'十月',
'十一月',
'十二月'
],
monthNamesShort: [
'一月',
'二月',
'三月',
'四月',
'五月',
'六月',
'七月',
'八月',
'九月',
'十月',
'十一月',
'十二月'
],
chooseYear: '选择年份',
chooseMonth: '选择月份',
chooseDate: '选择日期',
prevDecade: '上十年',
nextDecade: '下十年',
prevYear: '上一年',
nextYear: '下一年',
prevMonth: '上月',
nextMonth: '下月',
prevHour: '上一小时',
nextHour: '下一小时',
prevMinute: '上一分钟',
nextMinute: '下一分钟',
prevSecond: '上一秒',
nextSecond: '下一秒',
am: '上午',
pm: '下午',
today: '今天',
weekHeader: '周',
firstDayOfWeek: 1,
showMonthAfterYear: true,
dateFormat: 'yyyy-MM-dd',
weak: '弱',
medium: '中',
strong: '强',
passwordPrompt: '请输入密码',
searchMessage: '找到 {0} 条结果',
selectionMessage: '已选择 {0} 项',
emptySelectionMessage: '未选择项目',
emptySearchMessage: '未找到结果',
fileChosenMessage: '已选择 {0} 个文件',
noFileChosenMessage: '未选择文件',
emptyMessage: '无可用选项',
aria: {
trueLabel: '是',
falseLabel: '否',
nullLabel: '未选择',
star: '1 星',
stars: '{star} 星',
selectAll: '全选',
unselectAll: '取消全选',
close: '关闭',
previous: '上一个',
next: '下一个',
navigation: '导航',
scrollTop: '滚动到顶部',
moveTop: '移到顶部',
moveUp: '上移',
moveDown: '下移',
moveBottom: '移到底部',
moveToTarget: '移到目标',
moveToSource: '移到源',
moveAllToTarget: '全部移到目标',
moveAllToSource: '全部移到源',
pageLabel: '第 {page} 页',
firstPageLabel: '第一页',
lastPageLabel: '最后一页',
nextPageLabel: '下一页',
prevPageLabel: '上一页',
rowsPerPageLabel: '每页行数',
jumpToPageDropdownLabel: '跳转到页码',
jumpToPageInputLabel: '页码输入',
selectRow: '选择行',
unselectRow: '取消选择行',
expandRow: '展开行',
collapseRow: '折叠行',
showFilterMenu: '显示筛选菜单',
hideFilterMenu: '隐藏筛选菜单',
filterOperator: '筛选运算符',
filterConstraint: '筛选条件',
editRow: '编辑行',
saveEdit: '保存编辑',
cancelEdit: '取消编辑',
listView: '列表视图',
gridView: '网格视图',
slide: '幻灯片',
slideNumber: '幻灯片 {slideNumber}',
zoomImage: '放大图片',
zoomIn: '放大',
zoomOut: '缩小',
rotateRight: '向右旋转',
rotateLeft: '向左旋转'
}
}
export default zhCN

22
tsconfig.app.json Normal file
View File

@@ -0,0 +1,22 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"types": ["vite/client"],
/* Linting */
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
// 设置 @ 路径
"baseUrl": ".",
"paths": {
"@/*": [
"src/*"
]
}
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
}

7
tsconfig.json Normal file
View File

@@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

24
tsconfig.node.json Normal file
View File

@@ -0,0 +1,24 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "es2023",
"lib": ["ES2023"],
"module": "esnext",
"types": ["node"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true
},
"include": ["vite.config.ts"]
}

42
vite.config.ts Normal file
View File

@@ -0,0 +1,42 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
const API_HOST = '192.168.1.7'
// const API_HOST = '127.0.0.1'
// https://vite.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
// 配置路径别名
alias: {
'@': path.resolve(__dirname, './src')
}
},
server: {
// 服务器代理 服务器没有部署Nginx的情况下使用
proxy: {
'/home-api': {
target: `http://${API_HOST}:8081`,
changeOrigin: true,
rewrite: (item) => item.replace(/^\/home-api/, '')
},
'/blog-api': {
target: `http://${API_HOST}:8082`,
changeOrigin: true,
rewrite: (item) => item.replace(/^\/blog-api/, '')
},
'/alist-api': {
target: 'http://192.168.1.7:5244',
changeOrigin: true,
rewrite: (item) => item.replace(/^\/alist-api/, '')
},
'/image-api': {
target: 'http://192.168.1.7:3000',
changeOrigin: true,
rewrite: (item) => item.replace(/^\/image-api/, '')
}
}
}
})