0.1.0 to dev from front-base
This commit is contained in:
commit
a9c20be67a
@ -5,6 +5,7 @@ public class ChangeRecordDto
|
|||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
public Guid UserId { get; set; }
|
public Guid UserId { get; set; }
|
||||||
public Guid? SpendingGroupId { get; set; }
|
public Guid? SpendingGroupId { get; set; }
|
||||||
|
public string? SpendingGroupName { get; set; }
|
||||||
public decimal Sum { get; set; }
|
public decimal Sum { get; set; }
|
||||||
public DateTime ChangedAt { get; set; }
|
public DateTime ChangedAt { get; set; }
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
namespace Contracts.DTO;
|
namespace Contracts.DTO;
|
||||||
|
|
||||||
public class UserLoginDTO
|
public class UserLoginDto
|
||||||
{
|
{
|
||||||
public string Name { get; set; } = string.Empty;
|
public string Name { get; set; } = string.Empty;
|
||||||
public string Password { get; set; } = string.Empty;
|
public string Password { get; set; } = string.Empty;
|
||||||
|
@ -11,5 +11,6 @@ public static class ChangeRecordMapper
|
|||||||
Id = dto.Id,
|
Id = dto.Id,
|
||||||
Sum = dto.Sum,
|
Sum = dto.Sum,
|
||||||
ChangedAt = dto.ChangedAt.ToString("dd.MM.yyyy"),
|
ChangedAt = dto.ChangedAt.ToString("dd.MM.yyyy"),
|
||||||
|
SpendingGroupName = dto.SpendingGroupName ?? string.Empty
|
||||||
};
|
};
|
||||||
}
|
}
|
@ -6,4 +6,5 @@ public class ChangeRecordSearch
|
|||||||
public Guid? SpendingGroupId { get; set; }
|
public Guid? SpendingGroupId { get; set; }
|
||||||
public DateTime? From { get; set; }
|
public DateTime? From { get; set; }
|
||||||
public DateTime? To { get; set; }
|
public DateTime? To { get; set; }
|
||||||
|
public Guid? UserId { get; set; }
|
||||||
}
|
}
|
@ -4,4 +4,5 @@ public class SpendingGroupSearch
|
|||||||
{
|
{
|
||||||
public Guid? Id { get; set; }
|
public Guid? Id { get; set; }
|
||||||
public string? Name { get; set; }
|
public string? Name { get; set; }
|
||||||
|
public Guid? UserId { get; set; }
|
||||||
}
|
}
|
@ -6,6 +6,6 @@ namespace Contracts.Services;
|
|||||||
|
|
||||||
public interface IAuthService
|
public interface IAuthService
|
||||||
{
|
{
|
||||||
public Task<UserViewModel> Login(UserLoginDTO loginData);
|
public Task<UserViewModel> Login(UserLoginDto loginData);
|
||||||
public Task<UserViewModel> Register(UserDto user);
|
public Task<UserViewModel> Register(UserDto user);
|
||||||
}
|
}
|
@ -5,4 +5,5 @@ public class ChangeRecordViewModel
|
|||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
public decimal Sum { get; set; }
|
public decimal Sum { get; set; }
|
||||||
public string ChangedAt { get; set; } = null!;
|
public string ChangedAt { get; set; } = null!;
|
||||||
|
public string SpendingGroupName { get; set; } = string.Empty;
|
||||||
}
|
}
|
@ -18,7 +18,7 @@ public class AuthController : ControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<ActionResult<UserViewModel>> Login([FromBody] UserLoginDTO loginData)
|
public async Task<ActionResult<UserViewModel>> Login([FromBody] UserLoginDto loginData)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -20,9 +20,10 @@ if (app.Environment.IsDevelopment())
|
|||||||
app.UseSwagger();
|
app.UseSwagger();
|
||||||
app.UseSwaggerUI();
|
app.UseSwaggerUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
app.MigrateDb();
|
app.MigrateDb();
|
||||||
|
|
||||||
|
app.UseCors(builder => builder.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin());
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
@ -70,8 +70,12 @@ public class ChangeRecordRepo : IChangeRecordRepo
|
|||||||
{
|
{
|
||||||
query = query.Where(x => x.ChangedAt >= search.From && x.ChangedAt <= search.To);
|
query = query.Where(x => x.ChangedAt >= search.From && x.ChangedAt <= search.To);
|
||||||
}
|
}
|
||||||
|
if (search.UserId.HasValue)
|
||||||
|
{
|
||||||
|
query = query.Where(x => x.UserId == search.UserId);
|
||||||
}
|
}
|
||||||
return await query.Select(x => x.ToDto()).ToListAsync();
|
}
|
||||||
|
return await query.Include(x => x.SpendingGroup).Select(x => x.ToDto()).ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ChangeRecordDto?> Update(ChangeRecordDto changeRecord)
|
public async Task<ChangeRecordDto?> Update(ChangeRecordDto changeRecord)
|
||||||
|
@ -49,7 +49,9 @@ public class SpendingGroupRepo : ISpendingGroupRepo
|
|||||||
.Include(x => x.ChangeRecords)
|
.Include(x => x.ChangeRecords)
|
||||||
.Include(x => x.SpendingPlans)
|
.Include(x => x.SpendingPlans)
|
||||||
.FirstOrDefaultAsync(x => x.Id == search.Id
|
.FirstOrDefaultAsync(x => x.Id == search.Id
|
||||||
|| x.Name == search.Name);
|
|| (!string.IsNullOrWhiteSpace(search.Name)
|
||||||
|
&& x.Name == search.Name
|
||||||
|
&& x.UserId == search.UserId));
|
||||||
|
|
||||||
return group?.ToDto();
|
return group?.ToDto();
|
||||||
}
|
}
|
||||||
@ -67,9 +69,10 @@ public class SpendingGroupRepo : ISpendingGroupRepo
|
|||||||
query = query.Where(x => x.Id == search.Id);
|
query = query.Where(x => x.Id == search.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(search.Name))
|
if (!string.IsNullOrWhiteSpace(search.Name) && search.UserId.HasValue)
|
||||||
{
|
{
|
||||||
query = query.Where(x => x.Name.Contains(search.Name, StringComparison.OrdinalIgnoreCase));
|
query = query.Where(x => x.Name.Contains(search.Name, StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& x.UserId == search.UserId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,8 @@ public static class ChangeRecordMapper
|
|||||||
Sum = changeRecord.Sum,
|
Sum = changeRecord.Sum,
|
||||||
ChangedAt = changeRecord.ChangedAt,
|
ChangedAt = changeRecord.ChangedAt,
|
||||||
SpendingGroupId = changeRecord.SpendingGroupId,
|
SpendingGroupId = changeRecord.SpendingGroupId,
|
||||||
UserId = changeRecord.UserId
|
UserId = changeRecord.UserId,
|
||||||
|
SpendingGroupName = changeRecord.SpendingGroup?.Name
|
||||||
};
|
};
|
||||||
|
|
||||||
public static ChangeRecord ToModel(this ChangeRecordDto changeRecord)
|
public static ChangeRecord ToModel(this ChangeRecordDto changeRecord)
|
||||||
|
@ -17,7 +17,7 @@ public class AuthService : IAuthService
|
|||||||
_userRepo = userRepo;
|
_userRepo = userRepo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<UserViewModel> Login(UserLoginDTO loginData)
|
public async Task<UserViewModel> Login(UserLoginDto loginData)
|
||||||
{
|
{
|
||||||
if (loginData == null || string.IsNullOrWhiteSpace(loginData.Name)
|
if (loginData == null || string.IsNullOrWhiteSpace(loginData.Name)
|
||||||
|| string.IsNullOrWhiteSpace(loginData.Password))
|
|| string.IsNullOrWhiteSpace(loginData.Password))
|
||||||
|
2
front/.gitignore
vendored
2
front/.gitignore
vendored
@ -22,3 +22,5 @@ dist-ssr
|
|||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
|
.env
|
46
front/components.d.ts
vendored
Normal file
46
front/components.d.ts
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
// @ts-nocheck
|
||||||
|
// Generated by unplugin-vue-components
|
||||||
|
// Read more: https://github.com/vuejs/core/pull/3399
|
||||||
|
export {}
|
||||||
|
|
||||||
|
/* prettier-ignore */
|
||||||
|
declare module 'vue' {
|
||||||
|
export interface GlobalComponents {
|
||||||
|
AButton: typeof import('ant-design-vue/es')['Button']
|
||||||
|
ACol: typeof import('ant-design-vue/es')['Col']
|
||||||
|
ADatePicker: typeof import('ant-design-vue/es')['DatePicker']
|
||||||
|
AFlex: typeof import('ant-design-vue/es')['Flex']
|
||||||
|
AForm: typeof import('ant-design-vue/es')['Form']
|
||||||
|
AFormItem: typeof import('ant-design-vue/es')['FormItem']
|
||||||
|
AInput: typeof import('ant-design-vue/es')['Input']
|
||||||
|
AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
|
||||||
|
AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
|
||||||
|
ALayout: typeof import('ant-design-vue/es')['Layout']
|
||||||
|
ALayoutContent: typeof import('ant-design-vue/es')['LayoutContent']
|
||||||
|
ALayoutHeader: typeof import('ant-design-vue/es')['LayoutHeader']
|
||||||
|
AMenu: typeof import('ant-design-vue/es')['Menu']
|
||||||
|
AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
|
||||||
|
APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
|
||||||
|
ARow: typeof import('ant-design-vue/es')['Row']
|
||||||
|
ASelect: typeof import('ant-design-vue/es')['Select']
|
||||||
|
ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
|
||||||
|
ASpace: typeof import('ant-design-vue/es')['Space']
|
||||||
|
ASpin: typeof import('ant-design-vue/es')['Spin']
|
||||||
|
ATable: typeof import('ant-design-vue/es')['Table']
|
||||||
|
ATypographyText: typeof import('ant-design-vue/es')['TypographyText']
|
||||||
|
ChangeRecordManager: typeof import('./src/components/support/ChangeRecordManager.vue')['default']
|
||||||
|
ChangeRecordMenu: typeof import('./src/components/support/ChangeRecordMenu.vue')['default']
|
||||||
|
Groups: typeof import('./src/components/pages/Groups.vue')['default']
|
||||||
|
Header: typeof import('./src/components/main/Header.vue')['default']
|
||||||
|
Home: typeof import('./src/components/pages/Home.vue')['default']
|
||||||
|
Login: typeof import('./src/components/pages/Login.vue')['default']
|
||||||
|
Manager: typeof import('./src/components/support/Manager.vue')['default']
|
||||||
|
PlanManager: typeof import('./src/components/support/PlanManager.vue')['default']
|
||||||
|
Plans: typeof import('./src/components/pages/Plans.vue')['default']
|
||||||
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
|
SignUp: typeof import('./src/components/pages/SignUp.vue')['default']
|
||||||
|
SpendingGroupManager: typeof import('./src/components/support/SpendingGroupManager.vue')['default']
|
||||||
|
}
|
||||||
|
}
|
1716
front/package-lock.json
generated
1716
front/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -6,14 +6,22 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vue-tsc -b && vite build",
|
"build": "vue-tsc -b && vite build",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview",
|
||||||
|
"gen-api": "swagger-typescript-api -r -o ./src/core/api/ --modular -p "
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"vue": "^3.5.12"
|
"@vueuse/core": "^12.0.0",
|
||||||
|
"ant-design-vue": "^4.2.6",
|
||||||
|
"dayjs": "^1.11.13",
|
||||||
|
"pinia": "^2.2.8",
|
||||||
|
"vue": "^3.5.12",
|
||||||
|
"vue-router": "^4.5.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^5.1.4",
|
"@vitejs/plugin-vue": "^5.1.4",
|
||||||
|
"swagger-typescript-api": "^13.0.23",
|
||||||
"typescript": "~5.6.2",
|
"typescript": "~5.6.2",
|
||||||
|
"unplugin-vue-components": "^0.27.5",
|
||||||
"vite": "^5.4.10",
|
"vite": "^5.4.10",
|
||||||
"vue-tsc": "^2.1.8"
|
"vue-tsc": "^2.1.8"
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,26 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import Header from './components/main/Header.vue';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<a-layout class="layout">
|
||||||
|
<Header />
|
||||||
|
<a-layout-content>
|
||||||
|
<RouterView />
|
||||||
|
</a-layout-content>
|
||||||
|
</a-layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
main {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 5vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.base-page {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-width: 80dvw;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
59
front/src/components/main/Header.vue
Normal file
59
front/src/components/main/Header.vue
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { inject } from 'vue';
|
||||||
|
import { useUserStore } from '../../store';
|
||||||
|
import { AuthService } from '../../core/services/auth-service';
|
||||||
|
import router from '../../router';
|
||||||
|
|
||||||
|
const store = useUserStore();
|
||||||
|
const authService = inject(AuthService.name) as AuthService;
|
||||||
|
|
||||||
|
function logout() {
|
||||||
|
authService.logout();
|
||||||
|
router.push({ name: 'login' });
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<a-layout-header class="header">
|
||||||
|
<div class="base-nav">
|
||||||
|
<div>ДомБюдж</div>
|
||||||
|
<nav>
|
||||||
|
<RouterLink :to="{ name: 'home' }">Главная</RouterLink>
|
||||||
|
<RouterLink :to="{ name: 'groups' }">Группы расходов</RouterLink>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<div v-if="!store.user.id">
|
||||||
|
<RouterLink :to="{ name: 'login' }">Войти</RouterLink>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<label for="logout">Привет, {{ store.user.name }}! Ваш текущий баланс: {{ store.user.balance }}</label>
|
||||||
|
<a-button
|
||||||
|
name="logout"
|
||||||
|
@click="logout()"
|
||||||
|
danger
|
||||||
|
style="margin-left: 30px"
|
||||||
|
>
|
||||||
|
Выйти
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</a-layout-header>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.header {
|
||||||
|
background-color: white;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.base-nav {
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: left;
|
||||||
|
}
|
||||||
|
.base-nav a {
|
||||||
|
margin-left: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
72
front/src/components/pages/Groups.vue
Normal file
72
front/src/components/pages/Groups.vue
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useAsyncState } from '@vueuse/core';
|
||||||
|
import { inject } from 'vue';
|
||||||
|
import { GroupService } from '../../core/services/group-service';
|
||||||
|
import SpendingGroupManager from '../support/SpendingGroupManager.vue';
|
||||||
|
import { DeleteOutlined } from '@ant-design/icons-vue';
|
||||||
|
|
||||||
|
const groupService = inject(GroupService.name) as GroupService;
|
||||||
|
|
||||||
|
const { state, isReady } = useAsyncState(() => groupService.getList(), []);
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: "Название группы",
|
||||||
|
dataIndex: "name",
|
||||||
|
key: "name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Планы группы",
|
||||||
|
dataIndex: "plans",
|
||||||
|
key: "plans",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Операция',
|
||||||
|
dataIndex: 'operation',
|
||||||
|
key: 'operation',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const refreshData = () => {
|
||||||
|
groupService.getList().then(data => {
|
||||||
|
state.value = data;
|
||||||
|
isReady.value = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDelete = (key: string) => {
|
||||||
|
groupService.deleteGroup(key)
|
||||||
|
.then(() => {
|
||||||
|
refreshData();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="base-page">
|
||||||
|
<h1>Группы расходов</h1>
|
||||||
|
<SpendingGroupManager :refreshData="refreshData" />
|
||||||
|
<a-table :dataSource="state" :columns="columns" v-if="isReady">
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'plans'">
|
||||||
|
<RouterLink :to="{ name: 'plans', params: { groupId: record.id } }" >Планы</RouterLink>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.dataIndex === 'operation'">
|
||||||
|
<a-popconfirm
|
||||||
|
v-if="state?.length"
|
||||||
|
title="Точно удалить?"
|
||||||
|
@confirm="onDelete(record.id)"
|
||||||
|
>
|
||||||
|
<a><DeleteOutlined /> Удалить</a>
|
||||||
|
</a-popconfirm>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
<div v-else>
|
||||||
|
<a-spin size="large" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
76
front/src/components/pages/Home.vue
Normal file
76
front/src/components/pages/Home.vue
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useAsyncState } from '@vueuse/core';
|
||||||
|
import ChangeRecordMenu from '../support/ChangeRecordManager.vue';
|
||||||
|
import { ChangeRecordService } from '../../core/services/change-record-service';
|
||||||
|
import { inject } from 'vue';
|
||||||
|
import { DeleteOutlined } from '@ant-design/icons-vue';
|
||||||
|
|
||||||
|
const changeRecordService = inject(ChangeRecordService.name) as ChangeRecordService;
|
||||||
|
|
||||||
|
const { state, isReady } = useAsyncState(() => changeRecordService.getList(), []);
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: 'Дата',
|
||||||
|
dataIndex: 'changedAt',
|
||||||
|
key: 'changedAt',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Сумма',
|
||||||
|
dataIndex: 'sum',
|
||||||
|
key: 'sum',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Группа расходов',
|
||||||
|
dataIndex: 'spendingGroupName',
|
||||||
|
key: 'spendingGroupName',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Операция',
|
||||||
|
dataIndex: 'operation',
|
||||||
|
key: 'operation',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const refreshData = () => {
|
||||||
|
changeRecordService.getList().then(data => {
|
||||||
|
state.value = data;
|
||||||
|
isReady.value = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDelete = (key: string) => {
|
||||||
|
changeRecordService.deleteRecord(key)
|
||||||
|
.then(() => {
|
||||||
|
refreshData();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="base-page">
|
||||||
|
<h1>История изменений баланса</h1>
|
||||||
|
<ChangeRecordMenu :refreshData="refreshData" />
|
||||||
|
<a-table :dataSource="state" :columns="columns" v-if="isReady" >
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.dataIndex === 'operation'">
|
||||||
|
<a-popconfirm
|
||||||
|
v-if="state?.length"
|
||||||
|
title="Точно удалить?"
|
||||||
|
@confirm="onDelete(record.id)"
|
||||||
|
>
|
||||||
|
<a><DeleteOutlined /> Удалить</a>
|
||||||
|
</a-popconfirm>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
<div v-else>
|
||||||
|
<a-spin size="large" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.layout {
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
76
front/src/components/pages/Login.vue
Normal file
76
front/src/components/pages/Login.vue
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<template>
|
||||||
|
<a-form
|
||||||
|
:model="formState"
|
||||||
|
name="login"
|
||||||
|
class="login-form"
|
||||||
|
@finish="onFinish"
|
||||||
|
@finishFailed="onFinishFailed"
|
||||||
|
>
|
||||||
|
<a-form-item
|
||||||
|
label="Логин"
|
||||||
|
name="name"
|
||||||
|
:rules="[{ required: true, message: 'Пожалуйста, введите свой логин' }]"
|
||||||
|
>
|
||||||
|
<a-input v-model:value="formState.name">
|
||||||
|
<template #prefix>
|
||||||
|
<UserOutlined class="site-form-item-icon" />
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
label="Пароль"
|
||||||
|
name="password"
|
||||||
|
:rules="[{ required: true, message: 'Пароль тоже нужен!' }]"
|
||||||
|
>
|
||||||
|
<a-input-password v-model:value="formState.password">
|
||||||
|
<template #prefix>
|
||||||
|
<LockOutlined class="site-form-item-icon" />
|
||||||
|
</template>
|
||||||
|
</a-input-password>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<a-button :disabled="disabled" type="primary" html-type="submit" class="login-form-button">
|
||||||
|
Войти
|
||||||
|
</a-button>
|
||||||
|
Или
|
||||||
|
<RouterLink :to="{ name: 'signup' }">создать аккаунт</RouterLink>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive, computed, inject } from 'vue';
|
||||||
|
import { UserOutlined, LockOutlined } from '@ant-design/icons-vue';
|
||||||
|
import { UserLoginDto } from '../../core/api/data-contracts';
|
||||||
|
import { RouterLink } from 'vue-router';
|
||||||
|
import { AuthService } from '../../core/services/auth-service';
|
||||||
|
import router from '../../router';
|
||||||
|
|
||||||
|
const formState = reactive<UserLoginDto>({
|
||||||
|
name: '',
|
||||||
|
password: '',
|
||||||
|
});
|
||||||
|
const authService = inject(AuthService.name) as AuthService;
|
||||||
|
console.log(authService);
|
||||||
|
// Логика формы
|
||||||
|
const onFinish = async (values: any) => {
|
||||||
|
console.log('Success:', values);
|
||||||
|
await authService.login(formState);
|
||||||
|
router.push({ name: 'home' });
|
||||||
|
};
|
||||||
|
const onFinishFailed = (errorInfo: any) => {
|
||||||
|
console.log('Failed:', errorInfo);
|
||||||
|
};
|
||||||
|
const disabled = computed(() => {
|
||||||
|
return !(formState.name && formState.password);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.login-form {
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form-button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
79
front/src/components/pages/Plans.vue
Normal file
79
front/src/components/pages/Plans.vue
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useAsyncState } from '@vueuse/core';
|
||||||
|
import { inject } from 'vue';
|
||||||
|
import { PlanService } from '../../core/services/plans-service';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import PlanManager from '../support/PlanManager.vue';
|
||||||
|
import { DeleteOutlined } from '@ant-design/icons-vue';
|
||||||
|
|
||||||
|
const planService = inject(PlanService.name) as PlanService;
|
||||||
|
const groupId = useRoute().params.groupId as string;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const { state, isReady } = useAsyncState(() => planService.getList(groupId), []);
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: "Планируемые расходы",
|
||||||
|
dataIndex: "sum",
|
||||||
|
key: "sum",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Начало плана",
|
||||||
|
dataIndex: "startAt",
|
||||||
|
key: "startAt",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Конец плана",
|
||||||
|
dataIndex: "endAt",
|
||||||
|
key: "endAt",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: 'Операция',
|
||||||
|
dataIndex: 'operation',
|
||||||
|
key: 'operation',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const refreshData = () => {
|
||||||
|
planService.getList(groupId).then(data => {
|
||||||
|
state.value = data;
|
||||||
|
isReady.value = true;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDelete = (key: string) => {
|
||||||
|
planService.deletePlan(key)
|
||||||
|
.then(() => {
|
||||||
|
refreshData();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="base-page">
|
||||||
|
<h1>Планы группы</h1>
|
||||||
|
<PlanManager :groupId="groupId" :refreshData="refreshData"/>
|
||||||
|
<a-table :dataSource="state" :columns="columns" v-if="isReady">
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.dataIndex === 'operation'">
|
||||||
|
<a-popconfirm
|
||||||
|
v-if="state?.length"
|
||||||
|
title="Точно удалить?"
|
||||||
|
@confirm="onDelete(record.id)"
|
||||||
|
>
|
||||||
|
<a><DeleteOutlined /> Удалить</a>
|
||||||
|
</a-popconfirm>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
<div v-else>
|
||||||
|
<a-spin size="large" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
86
front/src/components/pages/SignUp.vue
Normal file
86
front/src/components/pages/SignUp.vue
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<template>
|
||||||
|
<a-form
|
||||||
|
:model="formState"
|
||||||
|
name="signup"
|
||||||
|
class="signup-form"
|
||||||
|
@finish="onFinish"
|
||||||
|
@finishFailed="onFinishFailed"
|
||||||
|
>
|
||||||
|
<a-form-item
|
||||||
|
label="Логин"
|
||||||
|
name="name"
|
||||||
|
:rules="[{ required: true, message: 'Пожалуйста, введите свой логин' }]"
|
||||||
|
>
|
||||||
|
<a-input v-model:value="formState.name">
|
||||||
|
<template #prefix>
|
||||||
|
<UserOutlined class="site-form-item-icon" />
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
label="Пароль"
|
||||||
|
name="password"
|
||||||
|
:rules="[{ required: true, message: 'Пароль тоже нужен!' }]"
|
||||||
|
>
|
||||||
|
<a-input-password v-model:value="formState.password">
|
||||||
|
<template #prefix>
|
||||||
|
<LockOutlined class="site-form-item-icon" />
|
||||||
|
</template>
|
||||||
|
</a-input-password>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
label="Баланс"
|
||||||
|
name="balance"
|
||||||
|
:rules="[{ required: true, message: 'Пожалуйста, введите свой баланс' }]"
|
||||||
|
>
|
||||||
|
<a-input-number prefix="₽" v-model:value="formState.balance" min="0"/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item>
|
||||||
|
<a-button :disabled="disabled" type="primary" html-type="submit" class="signup-form-button">
|
||||||
|
Создать
|
||||||
|
</a-button>
|
||||||
|
Или
|
||||||
|
<RouterLink :to="{ name: 'login' }">войти в свой аккаунт</RouterLink>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive, computed, inject } from 'vue';
|
||||||
|
import { UserOutlined, LockOutlined } from '@ant-design/icons-vue';
|
||||||
|
import { UserDto } from '../../core/api/data-contracts';
|
||||||
|
import { RouterLink } from 'vue-router';
|
||||||
|
import { AuthService } from '../../core/services/auth-service';
|
||||||
|
import router from '../../router';
|
||||||
|
|
||||||
|
const formState = reactive<UserDto>({
|
||||||
|
name: '',
|
||||||
|
password: '',
|
||||||
|
balance: 0
|
||||||
|
});
|
||||||
|
const authService = inject(typeof(AuthService)) as AuthService;
|
||||||
|
|
||||||
|
// Логика формы
|
||||||
|
const onFinish = async (values: any) => {
|
||||||
|
console.log('Success:', values);
|
||||||
|
await authService.register(formState);
|
||||||
|
router.push({ name: 'home' });
|
||||||
|
};
|
||||||
|
const onFinishFailed = (errorInfo: any) => {
|
||||||
|
console.log('Failed:', errorInfo);
|
||||||
|
};
|
||||||
|
const disabled = computed(() => {
|
||||||
|
return !(formState.name && formState.password && formState.balance);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.signup-form {
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signup-form-button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
95
front/src/components/support/ChangeRecordManager.vue
Normal file
95
front/src/components/support/ChangeRecordManager.vue
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
<style scoped>
|
||||||
|
</style>
|
||||||
|
<template>
|
||||||
|
<a-space direction="vertical" :size="10">
|
||||||
|
<label for="change-record">Записать изменения баланса</label>
|
||||||
|
<a-form
|
||||||
|
:model="formState"
|
||||||
|
name="change-record"
|
||||||
|
@finish="onFinish"
|
||||||
|
@finishFailed="onFinishFailed"
|
||||||
|
>
|
||||||
|
<a-form-item
|
||||||
|
label="Сумма"
|
||||||
|
name="sum"
|
||||||
|
:rules="[{ required: true, message: 'Пожалуйста, введите сумму' }]"
|
||||||
|
>
|
||||||
|
<a-input-number v-model:value="formState.sum" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
label="Группа расходов"
|
||||||
|
name="spendingGroupId"
|
||||||
|
v-if="isReady"
|
||||||
|
:rules="[{ required: true, message: 'Пожалуйста, выберите группу расходов' }]"
|
||||||
|
>
|
||||||
|
|
||||||
|
<a-select v-model:value="formState.spendingGroupId" :disabled="disabled">
|
||||||
|
<a-select-option v-for="group in groupList" :key="group.id" :value="group.id">
|
||||||
|
{{ group.name }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item v-else>
|
||||||
|
<a-spin size="small" />
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
name="changedAt"
|
||||||
|
label="Дата"
|
||||||
|
:rules="[{ required: true, message: 'Пожалуйста, выберите дату' }]"
|
||||||
|
>
|
||||||
|
<a-date-picker v-model:value="pickedChangedAt" />
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item>
|
||||||
|
<a-button html-type="submit" :disabled="disabledForm" type="primary">Сохранить</a-button>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, inject, reactive, ref } from 'vue';
|
||||||
|
import { ChangeRecordDto, SpendingGroupViewModel } from '../../core/api/data-contracts';
|
||||||
|
import { useUserStore } from '../../store';
|
||||||
|
import { ChangeRecordService } from '../../core/services/change-record-service';
|
||||||
|
import { GroupService } from '../../core/services/group-service';
|
||||||
|
import { useAsyncState } from '@vueuse/core';
|
||||||
|
import type { Dayjs } from 'dayjs';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
refreshData: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const { refreshData } = defineProps<IProps>();
|
||||||
|
|
||||||
|
const store = useUserStore();
|
||||||
|
// Сервисы
|
||||||
|
const changeRecordService = inject(ChangeRecordService.name) as ChangeRecordService;
|
||||||
|
const groupService = inject(GroupService.name) as GroupService;
|
||||||
|
|
||||||
|
const { state: groupList, isReady } = useAsyncState(() => groupService.getList(), []);
|
||||||
|
const pickedChangedAt = ref<Dayjs>();
|
||||||
|
const formState = reactive<ChangeRecordDto>({
|
||||||
|
userId: store.user.id,
|
||||||
|
sum: -1,
|
||||||
|
changedAt: new Date().toISOString(),
|
||||||
|
spendingGroupId: null
|
||||||
|
});
|
||||||
|
|
||||||
|
// Логика формы
|
||||||
|
const onFinish = async (values: any) => {
|
||||||
|
console.log('Success:', values);
|
||||||
|
formState.changedAt = pickedChangedAt.value?.toISOString();
|
||||||
|
await changeRecordService.createRecord(formState);
|
||||||
|
refreshData();
|
||||||
|
};
|
||||||
|
const onFinishFailed = (errorInfo: any) => {
|
||||||
|
console.log('Failed:', errorInfo);
|
||||||
|
};
|
||||||
|
const disabled = computed(() => {
|
||||||
|
return formState.sum && formState.sum >= 0;
|
||||||
|
});
|
||||||
|
const disabledForm = computed(() => {
|
||||||
|
return !(formState.sum && formState.changedAt && (formState.spendingGroupId || formState.sum > 0));
|
||||||
|
})
|
||||||
|
</script>
|
80
front/src/components/support/PlanManager.vue
Normal file
80
front/src/components/support/PlanManager.vue
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<style scoped>
|
||||||
|
</style>
|
||||||
|
<template>
|
||||||
|
<a-space direction="vertical" :size="10">
|
||||||
|
<label for="change-record">Добавить план расходов</label>
|
||||||
|
<a-form
|
||||||
|
:model="formState"
|
||||||
|
name="change-record"
|
||||||
|
@finish="onFinish"
|
||||||
|
@finishFailed="onFinishFailed"
|
||||||
|
>
|
||||||
|
<a-form-item
|
||||||
|
label="Сумма"
|
||||||
|
name="sum"
|
||||||
|
:rules="[{ required: true, message: 'Пожалуйста, введите сумму' }]"
|
||||||
|
>
|
||||||
|
<a-input-number v-model:value="formState.sum" />
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
name="startAt"
|
||||||
|
label="Дата начала плана"
|
||||||
|
:rules="[{ required: true, message: 'Пожалуйста, введите дату начала плана' }]"
|
||||||
|
>
|
||||||
|
<a-date-picker v-model:value="startAt" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
name="endAt"
|
||||||
|
label="Дата окончания плана"
|
||||||
|
:rules="[{ required: true, message: 'Пожалуйста, введите дату окончания плана' }]"
|
||||||
|
>
|
||||||
|
<a-date-picker v-model:value="endAt" />
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item>
|
||||||
|
<a-button :disabled="disabledForm" html-type="submit" type="primary">Добавить</a-button>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, inject, reactive, ref } from 'vue';
|
||||||
|
import { SpendingPlanDto } from '../../core/api/data-contracts';
|
||||||
|
import { Dayjs } from 'dayjs';
|
||||||
|
import { PlanService } from '../../core/services/plans-service';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
groupId: string,
|
||||||
|
refreshData: () => void
|
||||||
|
}
|
||||||
|
const { groupId, refreshData } = defineProps<IProps>();
|
||||||
|
// Сервисы
|
||||||
|
const planService = inject(PlanService.name) as PlanService;
|
||||||
|
|
||||||
|
const startAt = ref<Dayjs>();
|
||||||
|
const endAt = ref<Dayjs>();
|
||||||
|
|
||||||
|
const formState = reactive<SpendingPlanDto>({
|
||||||
|
sum: 0,
|
||||||
|
startAt: new Date().toISOString(),
|
||||||
|
endAt: new Date().toISOString(),
|
||||||
|
spendingGroupId: groupId
|
||||||
|
});
|
||||||
|
|
||||||
|
// Логика формы
|
||||||
|
const onFinish = async (values: any) => {
|
||||||
|
console.log('Success:', values);
|
||||||
|
formState.startAt = startAt.value?.toISOString();
|
||||||
|
formState.endAt = endAt.value?.toISOString();
|
||||||
|
await planService.createPlan(formState);
|
||||||
|
refreshData();
|
||||||
|
};
|
||||||
|
const onFinishFailed = (errorInfo: any) => {
|
||||||
|
console.log('Failed:', errorInfo);
|
||||||
|
};
|
||||||
|
|
||||||
|
const disabledForm = computed(() => {
|
||||||
|
return !(formState.sum && formState.startAt && formState.endAt);
|
||||||
|
});
|
||||||
|
</script>
|
57
front/src/components/support/SpendingGroupManager.vue
Normal file
57
front/src/components/support/SpendingGroupManager.vue
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<template>
|
||||||
|
<a-space direction="vertical" :size="10">
|
||||||
|
<label for="spending-group">Создать группу расходов</label>
|
||||||
|
<a-form
|
||||||
|
:model="formState"
|
||||||
|
name="spending-group"
|
||||||
|
@finish="onFinish"
|
||||||
|
@finishFailed="onFinishFailed"
|
||||||
|
>
|
||||||
|
<a-form-item
|
||||||
|
label="Название"
|
||||||
|
name="name"
|
||||||
|
:rules="[{ required: true, message: 'Пожалуйста, введите название' }]"
|
||||||
|
>
|
||||||
|
<a-input v-model:value="formState.name" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<a-button :disabled="disabledForm" html-type="submit" type="primary">Сохранить</a-button>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, inject, reactive } from 'vue';
|
||||||
|
import { useUserStore } from '../../store';
|
||||||
|
import { GroupService } from '../../core/services/group-service';
|
||||||
|
import { SpendingGroupDto } from '../../core/api/data-contracts';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
refreshData: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const { refreshData } = defineProps<IProps>();
|
||||||
|
|
||||||
|
const store = useUserStore();
|
||||||
|
// Сервисы
|
||||||
|
const groupService = inject(GroupService.name) as GroupService;
|
||||||
|
|
||||||
|
const formState = reactive<SpendingGroupDto>({
|
||||||
|
userId: store.user.id,
|
||||||
|
name: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Логика формы
|
||||||
|
const onFinish = async (values: any) => {
|
||||||
|
console.log('Success:', values);
|
||||||
|
await groupService.createGroup(formState);
|
||||||
|
refreshData();
|
||||||
|
};
|
||||||
|
const onFinishFailed = (errorInfo: any) => {
|
||||||
|
console.log('Failed:', errorInfo);
|
||||||
|
};
|
||||||
|
|
||||||
|
const disabledForm = computed(() => {
|
||||||
|
return !formState.name;
|
||||||
|
});
|
||||||
|
</script>
|
470
front/src/core/api/Api.ts
Normal file
470
front/src/core/api/Api.ts
Normal file
@ -0,0 +1,470 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
/* tslint:disable */
|
||||||
|
/*
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
|
||||||
|
* ## ##
|
||||||
|
* ## AUTHOR: acacode ##
|
||||||
|
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
ChangeRecordDto,
|
||||||
|
ChangeRecordViewModel,
|
||||||
|
SpendingGroupDto,
|
||||||
|
SpendingGroupViewModel,
|
||||||
|
SpendingPlanDto,
|
||||||
|
SpendingPlanViewModel,
|
||||||
|
UserDto,
|
||||||
|
UserLoginDto,
|
||||||
|
UserViewModel,
|
||||||
|
} from "./data-contracts";
|
||||||
|
import { ContentType, HttpClient, RequestParams } from "./http-client";
|
||||||
|
|
||||||
|
export class Api<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
|
||||||
|
/**
|
||||||
|
* No description
|
||||||
|
*
|
||||||
|
* @tags Auth
|
||||||
|
* @name AuthCreate
|
||||||
|
* @request POST:/api/Auth
|
||||||
|
* @response `200` `UserViewModel` Success
|
||||||
|
*/
|
||||||
|
auth = (data: UserLoginDto, params: RequestParams = {}) =>
|
||||||
|
this.request<UserViewModel, any>({
|
||||||
|
path: `/api/Auth`,
|
||||||
|
method: "POST",
|
||||||
|
body: data,
|
||||||
|
type: ContentType.Json,
|
||||||
|
format: "json",
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* No description
|
||||||
|
*
|
||||||
|
* @tags Auth
|
||||||
|
* @name AuthRegisterCreate
|
||||||
|
* @request POST:/api/Auth/register
|
||||||
|
* @response `200` `UserViewModel` Success
|
||||||
|
*/
|
||||||
|
authRegister = (data: UserDto, params: RequestParams = {}) =>
|
||||||
|
this.request<UserViewModel, any>({
|
||||||
|
path: `/api/Auth/register`,
|
||||||
|
method: "POST",
|
||||||
|
body: data,
|
||||||
|
type: ContentType.Json,
|
||||||
|
format: "json",
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* No description
|
||||||
|
*
|
||||||
|
* @tags ChangeRecord
|
||||||
|
* @name ChangeRecordCreate
|
||||||
|
* @request POST:/api/ChangeRecord
|
||||||
|
* @response `200` `ChangeRecordViewModel` Success
|
||||||
|
*/
|
||||||
|
changeRecordCreate = (data: ChangeRecordDto, params: RequestParams = {}) =>
|
||||||
|
this.request<ChangeRecordViewModel, any>({
|
||||||
|
path: `/api/ChangeRecord`,
|
||||||
|
method: "POST",
|
||||||
|
body: data,
|
||||||
|
type: ContentType.Json,
|
||||||
|
format: "json",
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* No description
|
||||||
|
*
|
||||||
|
* @tags ChangeRecord
|
||||||
|
* @name ChangeRecordList
|
||||||
|
* @request GET:/api/ChangeRecord
|
||||||
|
* @response `200` `(ChangeRecordViewModel)[]` Success
|
||||||
|
*/
|
||||||
|
changeRecordList = (params: RequestParams = {}) =>
|
||||||
|
this.request<ChangeRecordViewModel[], any>({
|
||||||
|
path: `/api/ChangeRecord`,
|
||||||
|
method: "GET",
|
||||||
|
format: "json",
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* No description
|
||||||
|
*
|
||||||
|
* @tags ChangeRecord
|
||||||
|
* @name ChangeRecordPartialUpdate
|
||||||
|
* @request PATCH:/api/ChangeRecord
|
||||||
|
* @response `200` `ChangeRecordViewModel` Success
|
||||||
|
*/
|
||||||
|
changeRecordPartialUpdate = (data: ChangeRecordDto, params: RequestParams = {}) =>
|
||||||
|
this.request<ChangeRecordViewModel, any>({
|
||||||
|
path: `/api/ChangeRecord`,
|
||||||
|
method: "PATCH",
|
||||||
|
body: data,
|
||||||
|
type: ContentType.Json,
|
||||||
|
format: "json",
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* No description
|
||||||
|
*
|
||||||
|
* @tags ChangeRecord
|
||||||
|
* @name ChangeRecordDelete
|
||||||
|
* @request DELETE:/api/ChangeRecord
|
||||||
|
* @response `200` `void` Success
|
||||||
|
*/
|
||||||
|
changeRecordDelete = (
|
||||||
|
query?: {
|
||||||
|
/** @format uuid */
|
||||||
|
Id?: string;
|
||||||
|
/** @format uuid */
|
||||||
|
SpendingGroupId?: string;
|
||||||
|
/** @format date-time */
|
||||||
|
From?: string;
|
||||||
|
/** @format date-time */
|
||||||
|
To?: string;
|
||||||
|
/** @format uuid */
|
||||||
|
UserId?: string;
|
||||||
|
},
|
||||||
|
params: RequestParams = {},
|
||||||
|
) =>
|
||||||
|
this.request<void, any>({
|
||||||
|
path: `/api/ChangeRecord`,
|
||||||
|
method: "DELETE",
|
||||||
|
query: query,
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* No description
|
||||||
|
*
|
||||||
|
* @tags ChangeRecord
|
||||||
|
* @name ChangeRecordFilterList
|
||||||
|
* @request GET:/api/ChangeRecord/filter
|
||||||
|
* @response `200` `(ChangeRecordViewModel)[]` Success
|
||||||
|
*/
|
||||||
|
changeRecordFilterList = (
|
||||||
|
query?: {
|
||||||
|
/** @format uuid */
|
||||||
|
Id?: string;
|
||||||
|
/** @format uuid */
|
||||||
|
SpendingGroupId?: string;
|
||||||
|
/** @format date-time */
|
||||||
|
From?: string;
|
||||||
|
/** @format date-time */
|
||||||
|
To?: string;
|
||||||
|
/** @format uuid */
|
||||||
|
UserId?: string;
|
||||||
|
},
|
||||||
|
params: RequestParams = {},
|
||||||
|
) =>
|
||||||
|
this.request<ChangeRecordViewModel[], any>({
|
||||||
|
path: `/api/ChangeRecord/filter`,
|
||||||
|
method: "GET",
|
||||||
|
query: query,
|
||||||
|
format: "json",
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* No description
|
||||||
|
*
|
||||||
|
* @tags SpendingGroup
|
||||||
|
* @name SpendingGroupDetail
|
||||||
|
* @request GET:/api/SpendingGroup/{id}
|
||||||
|
* @response `200` `SpendingGroupViewModel` Success
|
||||||
|
*/
|
||||||
|
spendingGroupDetail = (
|
||||||
|
id: string,
|
||||||
|
query?: {
|
||||||
|
/** @format uuid */
|
||||||
|
Id?: string;
|
||||||
|
Name?: string;
|
||||||
|
/** @format uuid */
|
||||||
|
UserId?: string;
|
||||||
|
},
|
||||||
|
params: RequestParams = {},
|
||||||
|
) =>
|
||||||
|
this.request<SpendingGroupViewModel, any>({
|
||||||
|
path: `/api/SpendingGroup/${id}`,
|
||||||
|
method: "GET",
|
||||||
|
query: query,
|
||||||
|
format: "json",
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* No description
|
||||||
|
*
|
||||||
|
* @tags SpendingGroup
|
||||||
|
* @name SpendingGroupList
|
||||||
|
* @request GET:/api/SpendingGroup
|
||||||
|
* @response `200` `(SpendingGroupViewModel)[]` Success
|
||||||
|
*/
|
||||||
|
spendingGroupList = (params: RequestParams = {}) =>
|
||||||
|
this.request<SpendingGroupViewModel[], any>({
|
||||||
|
path: `/api/SpendingGroup`,
|
||||||
|
method: "GET",
|
||||||
|
format: "json",
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* No description
|
||||||
|
*
|
||||||
|
* @tags SpendingGroup
|
||||||
|
* @name SpendingGroupCreate
|
||||||
|
* @request POST:/api/SpendingGroup
|
||||||
|
* @response `200` `SpendingGroupViewModel` Success
|
||||||
|
*/
|
||||||
|
spendingGroupCreate = (data: SpendingGroupDto, params: RequestParams = {}) =>
|
||||||
|
this.request<SpendingGroupViewModel, any>({
|
||||||
|
path: `/api/SpendingGroup`,
|
||||||
|
method: "POST",
|
||||||
|
body: data,
|
||||||
|
type: ContentType.Json,
|
||||||
|
format: "json",
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* No description
|
||||||
|
*
|
||||||
|
* @tags SpendingGroup
|
||||||
|
* @name SpendingGroupPartialUpdate
|
||||||
|
* @request PATCH:/api/SpendingGroup
|
||||||
|
* @response `200` `SpendingGroupViewModel` Success
|
||||||
|
*/
|
||||||
|
spendingGroupPartialUpdate = (data: SpendingGroupDto, params: RequestParams = {}) =>
|
||||||
|
this.request<SpendingGroupViewModel, any>({
|
||||||
|
path: `/api/SpendingGroup`,
|
||||||
|
method: "PATCH",
|
||||||
|
body: data,
|
||||||
|
type: ContentType.Json,
|
||||||
|
format: "json",
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* No description
|
||||||
|
*
|
||||||
|
* @tags SpendingGroup
|
||||||
|
* @name SpendingGroupDelete
|
||||||
|
* @request DELETE:/api/SpendingGroup
|
||||||
|
* @response `200` `void` Success
|
||||||
|
*/
|
||||||
|
spendingGroupDelete = (
|
||||||
|
query?: {
|
||||||
|
/** @format uuid */
|
||||||
|
Id?: string;
|
||||||
|
Name?: string;
|
||||||
|
/** @format uuid */
|
||||||
|
UserId?: string;
|
||||||
|
},
|
||||||
|
params: RequestParams = {},
|
||||||
|
) =>
|
||||||
|
this.request<void, any>({
|
||||||
|
path: `/api/SpendingGroup`,
|
||||||
|
method: "DELETE",
|
||||||
|
query: query,
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* No description
|
||||||
|
*
|
||||||
|
* @tags SpendingGroup
|
||||||
|
* @name SpendingGroupFilterList
|
||||||
|
* @request GET:/api/SpendingGroup/filter
|
||||||
|
* @response `200` `(SpendingGroupViewModel)[]` Success
|
||||||
|
*/
|
||||||
|
spendingGroupFilterList = (
|
||||||
|
query?: {
|
||||||
|
/** @format uuid */
|
||||||
|
Id?: string;
|
||||||
|
Name?: string;
|
||||||
|
/** @format uuid */
|
||||||
|
UserId?: string;
|
||||||
|
},
|
||||||
|
params: RequestParams = {},
|
||||||
|
) =>
|
||||||
|
this.request<SpendingGroupViewModel[], any>({
|
||||||
|
path: `/api/SpendingGroup/filter`,
|
||||||
|
method: "GET",
|
||||||
|
query: query,
|
||||||
|
format: "json",
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* No description
|
||||||
|
*
|
||||||
|
* @tags SpendingPlan
|
||||||
|
* @name SpendingPlanDetail
|
||||||
|
* @request GET:/api/SpendingPlan/{id}
|
||||||
|
* @response `200` `SpendingPlanViewModel` Success
|
||||||
|
*/
|
||||||
|
spendingPlanDetail = (
|
||||||
|
id: string,
|
||||||
|
query?: {
|
||||||
|
/** @format uuid */
|
||||||
|
Id?: string;
|
||||||
|
},
|
||||||
|
params: RequestParams = {},
|
||||||
|
) =>
|
||||||
|
this.request<SpendingPlanViewModel, any>({
|
||||||
|
path: `/api/SpendingPlan/${id}`,
|
||||||
|
method: "GET",
|
||||||
|
query: query,
|
||||||
|
format: "json",
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* No description
|
||||||
|
*
|
||||||
|
* @tags SpendingPlan
|
||||||
|
* @name SpendingPlanList
|
||||||
|
* @request GET:/api/SpendingPlan
|
||||||
|
* @response `200` `(SpendingPlanViewModel)[]` Success
|
||||||
|
*/
|
||||||
|
spendingPlanList = (params: RequestParams = {}) =>
|
||||||
|
this.request<SpendingPlanViewModel[], any>({
|
||||||
|
path: `/api/SpendingPlan`,
|
||||||
|
method: "GET",
|
||||||
|
format: "json",
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* No description
|
||||||
|
*
|
||||||
|
* @tags SpendingPlan
|
||||||
|
* @name SpendingPlanCreate
|
||||||
|
* @request POST:/api/SpendingPlan
|
||||||
|
* @response `200` `SpendingPlanViewModel` Success
|
||||||
|
*/
|
||||||
|
spendingPlanCreate = (data: SpendingPlanDto, params: RequestParams = {}) =>
|
||||||
|
this.request<SpendingPlanViewModel, any>({
|
||||||
|
path: `/api/SpendingPlan`,
|
||||||
|
method: "POST",
|
||||||
|
body: data,
|
||||||
|
type: ContentType.Json,
|
||||||
|
format: "json",
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* No description
|
||||||
|
*
|
||||||
|
* @tags SpendingPlan
|
||||||
|
* @name SpendingPlanPartialUpdate
|
||||||
|
* @request PATCH:/api/SpendingPlan
|
||||||
|
* @response `200` `SpendingPlanViewModel` Success
|
||||||
|
*/
|
||||||
|
spendingPlanPartialUpdate = (data: SpendingPlanDto, params: RequestParams = {}) =>
|
||||||
|
this.request<SpendingPlanViewModel, any>({
|
||||||
|
path: `/api/SpendingPlan`,
|
||||||
|
method: "PATCH",
|
||||||
|
body: data,
|
||||||
|
type: ContentType.Json,
|
||||||
|
format: "json",
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* No description
|
||||||
|
*
|
||||||
|
* @tags SpendingPlan
|
||||||
|
* @name SpendingPlanDelete
|
||||||
|
* @request DELETE:/api/SpendingPlan
|
||||||
|
* @response `200` `void` Success
|
||||||
|
*/
|
||||||
|
spendingPlanDelete = (
|
||||||
|
query?: {
|
||||||
|
/** @format uuid */
|
||||||
|
Id?: string;
|
||||||
|
},
|
||||||
|
params: RequestParams = {},
|
||||||
|
) =>
|
||||||
|
this.request<void, any>({
|
||||||
|
path: `/api/SpendingPlan`,
|
||||||
|
method: "DELETE",
|
||||||
|
query: query,
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* No description
|
||||||
|
*
|
||||||
|
* @tags SpendingPlan
|
||||||
|
* @name SpendingPlanFilterList
|
||||||
|
* @request GET:/api/SpendingPlan/filter
|
||||||
|
* @response `200` `(SpendingPlanViewModel)[]` Success
|
||||||
|
*/
|
||||||
|
spendingPlanFilterList = (
|
||||||
|
query?: {
|
||||||
|
/** @format uuid */
|
||||||
|
Id?: string;
|
||||||
|
},
|
||||||
|
params: RequestParams = {},
|
||||||
|
) =>
|
||||||
|
this.request<SpendingPlanViewModel[], any>({
|
||||||
|
path: `/api/SpendingPlan/filter`,
|
||||||
|
method: "GET",
|
||||||
|
query: query,
|
||||||
|
format: "json",
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* No description
|
||||||
|
*
|
||||||
|
* @tags User
|
||||||
|
* @name UserList
|
||||||
|
* @request GET:/api/User
|
||||||
|
* @response `200` `UserViewModel` Success
|
||||||
|
*/
|
||||||
|
userGet = (
|
||||||
|
query?: {
|
||||||
|
/** @format uuid */
|
||||||
|
Id?: string;
|
||||||
|
Name?: string;
|
||||||
|
},
|
||||||
|
params: RequestParams = {},
|
||||||
|
) =>
|
||||||
|
this.request<UserViewModel, any>({
|
||||||
|
path: `/api/User`,
|
||||||
|
method: "GET",
|
||||||
|
query: query,
|
||||||
|
format: "json",
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* No description
|
||||||
|
*
|
||||||
|
* @tags User
|
||||||
|
* @name UserPartialUpdate
|
||||||
|
* @request PATCH:/api/User
|
||||||
|
* @response `200` `UserViewModel` Success
|
||||||
|
*/
|
||||||
|
userPartialUpdate = (data: UserDto, params: RequestParams = {}) =>
|
||||||
|
this.request<UserViewModel, any>({
|
||||||
|
path: `/api/User`,
|
||||||
|
method: "PATCH",
|
||||||
|
body: data,
|
||||||
|
type: ContentType.Json,
|
||||||
|
format: "json",
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* No description
|
||||||
|
*
|
||||||
|
* @tags User
|
||||||
|
* @name UserDelete
|
||||||
|
* @request DELETE:/api/User
|
||||||
|
* @response `200` `UserViewModel` Success
|
||||||
|
*/
|
||||||
|
userDelete = (
|
||||||
|
query?: {
|
||||||
|
/** @format uuid */
|
||||||
|
Id?: string;
|
||||||
|
Name?: string;
|
||||||
|
},
|
||||||
|
params: RequestParams = {},
|
||||||
|
) =>
|
||||||
|
this.request<UserViewModel, any>({
|
||||||
|
path: `/api/User`,
|
||||||
|
method: "DELETE",
|
||||||
|
query: query,
|
||||||
|
format: "json",
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
}
|
98
front/src/core/api/data-contracts.ts
Normal file
98
front/src/core/api/data-contracts.ts
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
/* tslint:disable */
|
||||||
|
/*
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
|
||||||
|
* ## ##
|
||||||
|
* ## AUTHOR: acacode ##
|
||||||
|
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface ChangeRecordDto {
|
||||||
|
/** @format uuid */
|
||||||
|
id?: string;
|
||||||
|
/** @format uuid */
|
||||||
|
userId?: string;
|
||||||
|
/** @format uuid */
|
||||||
|
spendingGroupId?: string | null;
|
||||||
|
spendingGroupName?: string | null;
|
||||||
|
/** @format double */
|
||||||
|
sum?: number;
|
||||||
|
/** @format date-time */
|
||||||
|
changedAt?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChangeRecordViewModel {
|
||||||
|
/** @format uuid */
|
||||||
|
id?: string;
|
||||||
|
/** @format double */
|
||||||
|
sum?: number;
|
||||||
|
/** @format date-time */
|
||||||
|
changedAt?: string;
|
||||||
|
spendingGroupName?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SpendingGroupDto {
|
||||||
|
/** @format uuid */
|
||||||
|
id?: string;
|
||||||
|
name?: string | null;
|
||||||
|
/** @format uuid */
|
||||||
|
userId?: string;
|
||||||
|
changeRecords?: ChangeRecordDto[] | null;
|
||||||
|
spendingPlans?: SpendingPlanDto[] | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SpendingGroupViewModel {
|
||||||
|
/** @format uuid */
|
||||||
|
id?: string;
|
||||||
|
name?: string | null;
|
||||||
|
changeRecords?: ChangeRecordViewModel[] | null;
|
||||||
|
spendingPlans?: SpendingPlanViewModel[] | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SpendingPlanDto {
|
||||||
|
/** @format uuid */
|
||||||
|
id?: string;
|
||||||
|
/** @format uuid */
|
||||||
|
spendingGroupId?: string;
|
||||||
|
/** @format double */
|
||||||
|
sum?: number;
|
||||||
|
/** @format date-time */
|
||||||
|
startAt?: string;
|
||||||
|
/** @format date-time */
|
||||||
|
endAt?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SpendingPlanViewModel {
|
||||||
|
/** @format uuid */
|
||||||
|
id?: string;
|
||||||
|
/** @format date-time */
|
||||||
|
startAt?: string;
|
||||||
|
/** @format date-time */
|
||||||
|
endAt?: string;
|
||||||
|
/** @format double */
|
||||||
|
sum?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserDto {
|
||||||
|
/** @format uuid */
|
||||||
|
id?: string;
|
||||||
|
name?: string | null;
|
||||||
|
password?: string | null;
|
||||||
|
/** @format double */
|
||||||
|
balance?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserLoginDto {
|
||||||
|
name?: string | null;
|
||||||
|
password?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserViewModel {
|
||||||
|
/** @format uuid */
|
||||||
|
id?: string;
|
||||||
|
name?: string | null;
|
||||||
|
/** @format double */
|
||||||
|
balance?: number;
|
||||||
|
}
|
220
front/src/core/api/http-client.ts
Normal file
220
front/src/core/api/http-client.ts
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
/* tslint:disable */
|
||||||
|
/*
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
|
||||||
|
* ## ##
|
||||||
|
* ## AUTHOR: acacode ##
|
||||||
|
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type QueryParamsType = Record<string | number, any>;
|
||||||
|
export type ResponseFormat = keyof Omit<Body, "body" | "bodyUsed">;
|
||||||
|
|
||||||
|
export interface FullRequestParams extends Omit<RequestInit, "body"> {
|
||||||
|
/** set parameter to `true` for call `securityWorker` for this request */
|
||||||
|
secure?: boolean;
|
||||||
|
/** request path */
|
||||||
|
path: string;
|
||||||
|
/** content type of request body */
|
||||||
|
type?: ContentType;
|
||||||
|
/** query params */
|
||||||
|
query?: QueryParamsType;
|
||||||
|
/** format of response (i.e. response.json() -> format: "json") */
|
||||||
|
format?: ResponseFormat;
|
||||||
|
/** request body */
|
||||||
|
body?: unknown;
|
||||||
|
/** base url */
|
||||||
|
baseUrl?: string;
|
||||||
|
/** request cancellation token */
|
||||||
|
cancelToken?: CancelToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RequestParams = Omit<FullRequestParams, "body" | "method" | "query" | "path">;
|
||||||
|
|
||||||
|
export interface ApiConfig<SecurityDataType = unknown> {
|
||||||
|
baseUrl?: string;
|
||||||
|
baseApiParams?: Omit<RequestParams, "baseUrl" | "cancelToken" | "signal">;
|
||||||
|
securityWorker?: (securityData: SecurityDataType | null) => Promise<RequestParams | void> | RequestParams | void;
|
||||||
|
customFetch?: typeof fetch;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HttpResponse<D extends unknown, E extends unknown = unknown> extends Response {
|
||||||
|
data: D;
|
||||||
|
error: E;
|
||||||
|
}
|
||||||
|
|
||||||
|
type CancelToken = Symbol | string | number;
|
||||||
|
|
||||||
|
export enum ContentType {
|
||||||
|
Json = "application/json",
|
||||||
|
FormData = "multipart/form-data",
|
||||||
|
UrlEncoded = "application/x-www-form-urlencoded",
|
||||||
|
Text = "text/plain",
|
||||||
|
}
|
||||||
|
|
||||||
|
export class HttpClient<SecurityDataType = unknown> {
|
||||||
|
public baseUrl: string = "http://localhost:5125";
|
||||||
|
private securityData: SecurityDataType | null = null;
|
||||||
|
private securityWorker?: ApiConfig<SecurityDataType>["securityWorker"];
|
||||||
|
private abortControllers = new Map<CancelToken, AbortController>();
|
||||||
|
private customFetch = (...fetchParams: Parameters<typeof fetch>) => fetch(...fetchParams);
|
||||||
|
|
||||||
|
private baseApiParams: RequestParams = {
|
||||||
|
credentials: "same-origin",
|
||||||
|
headers: {},
|
||||||
|
redirect: "follow",
|
||||||
|
referrerPolicy: "no-referrer",
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(apiConfig: ApiConfig<SecurityDataType> = {}) {
|
||||||
|
Object.assign(this, apiConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
public setSecurityData = (data: SecurityDataType | null) => {
|
||||||
|
this.securityData = data;
|
||||||
|
};
|
||||||
|
|
||||||
|
protected encodeQueryParam(key: string, value: any) {
|
||||||
|
const encodedKey = encodeURIComponent(key);
|
||||||
|
return `${encodedKey}=${encodeURIComponent(typeof value === "number" ? value : `${value}`)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected addQueryParam(query: QueryParamsType, key: string) {
|
||||||
|
return this.encodeQueryParam(key, query[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected addArrayQueryParam(query: QueryParamsType, key: string) {
|
||||||
|
const value = query[key];
|
||||||
|
return value.map((v: any) => this.encodeQueryParam(key, v)).join("&");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected toQueryString(rawQuery?: QueryParamsType): string {
|
||||||
|
const query = rawQuery || {};
|
||||||
|
const keys = Object.keys(query).filter((key) => "undefined" !== typeof query[key]);
|
||||||
|
return keys
|
||||||
|
.map((key) => (Array.isArray(query[key]) ? this.addArrayQueryParam(query, key) : this.addQueryParam(query, key)))
|
||||||
|
.join("&");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected addQueryParams(rawQuery?: QueryParamsType): string {
|
||||||
|
const queryString = this.toQueryString(rawQuery);
|
||||||
|
return queryString ? `?${queryString}` : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private contentFormatters: Record<ContentType, (input: any) => any> = {
|
||||||
|
[ContentType.Json]: (input: any) =>
|
||||||
|
input !== null && (typeof input === "object" || typeof input === "string") ? JSON.stringify(input) : input,
|
||||||
|
[ContentType.Text]: (input: any) => (input !== null && typeof input !== "string" ? JSON.stringify(input) : input),
|
||||||
|
[ContentType.FormData]: (input: any) =>
|
||||||
|
Object.keys(input || {}).reduce((formData, key) => {
|
||||||
|
const property = input[key];
|
||||||
|
formData.append(
|
||||||
|
key,
|
||||||
|
property instanceof Blob
|
||||||
|
? property
|
||||||
|
: typeof property === "object" && property !== null
|
||||||
|
? JSON.stringify(property)
|
||||||
|
: `${property}`,
|
||||||
|
);
|
||||||
|
return formData;
|
||||||
|
}, new FormData()),
|
||||||
|
[ContentType.UrlEncoded]: (input: any) => this.toQueryString(input),
|
||||||
|
};
|
||||||
|
|
||||||
|
protected mergeRequestParams(params1: RequestParams, params2?: RequestParams): RequestParams {
|
||||||
|
return {
|
||||||
|
...this.baseApiParams,
|
||||||
|
...params1,
|
||||||
|
...(params2 || {}),
|
||||||
|
headers: {
|
||||||
|
...(this.baseApiParams.headers || {}),
|
||||||
|
...(params1.headers || {}),
|
||||||
|
...((params2 && params2.headers) || {}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected createAbortSignal = (cancelToken: CancelToken): AbortSignal | undefined => {
|
||||||
|
if (this.abortControllers.has(cancelToken)) {
|
||||||
|
const abortController = this.abortControllers.get(cancelToken);
|
||||||
|
if (abortController) {
|
||||||
|
return abortController.signal;
|
||||||
|
}
|
||||||
|
return void 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const abortController = new AbortController();
|
||||||
|
this.abortControllers.set(cancelToken, abortController);
|
||||||
|
return abortController.signal;
|
||||||
|
};
|
||||||
|
|
||||||
|
public abortRequest = (cancelToken: CancelToken) => {
|
||||||
|
const abortController = this.abortControllers.get(cancelToken);
|
||||||
|
|
||||||
|
if (abortController) {
|
||||||
|
abortController.abort();
|
||||||
|
this.abortControllers.delete(cancelToken);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public request = async <T = any, E = any>({
|
||||||
|
body,
|
||||||
|
secure,
|
||||||
|
path,
|
||||||
|
type,
|
||||||
|
query,
|
||||||
|
format,
|
||||||
|
baseUrl,
|
||||||
|
cancelToken,
|
||||||
|
...params
|
||||||
|
}: FullRequestParams): Promise<HttpResponse<T, E>> => {
|
||||||
|
const secureParams =
|
||||||
|
((typeof secure === "boolean" ? secure : this.baseApiParams.secure) &&
|
||||||
|
this.securityWorker &&
|
||||||
|
(await this.securityWorker(this.securityData))) ||
|
||||||
|
{};
|
||||||
|
const requestParams = this.mergeRequestParams(params, secureParams);
|
||||||
|
const queryString = query && this.toQueryString(query);
|
||||||
|
const payloadFormatter = this.contentFormatters[type || ContentType.Json];
|
||||||
|
const responseFormat = format || requestParams.format;
|
||||||
|
|
||||||
|
return this.customFetch(`${baseUrl || this.baseUrl || ""}${path}${queryString ? `?${queryString}` : ""}`, {
|
||||||
|
...requestParams,
|
||||||
|
headers: {
|
||||||
|
...(requestParams.headers || {}),
|
||||||
|
...(type && type !== ContentType.FormData ? { "Content-Type": type } : {}),
|
||||||
|
},
|
||||||
|
signal: (cancelToken ? this.createAbortSignal(cancelToken) : requestParams.signal) || null,
|
||||||
|
body: typeof body === "undefined" || body === null ? null : payloadFormatter(body),
|
||||||
|
}).then(async (response) => {
|
||||||
|
const r = response.clone() as HttpResponse<T, E>;
|
||||||
|
r.data = null as unknown as T;
|
||||||
|
r.error = null as unknown as E;
|
||||||
|
|
||||||
|
const data = !responseFormat
|
||||||
|
? r
|
||||||
|
: await response[responseFormat]()
|
||||||
|
.then((data) => {
|
||||||
|
if (r.ok) {
|
||||||
|
r.data = data;
|
||||||
|
} else {
|
||||||
|
r.error = data;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
r.error = e;
|
||||||
|
return r;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (cancelToken) {
|
||||||
|
this.abortControllers.delete(cancelToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.ok) throw data;
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
30
front/src/core/services/auth-service.ts
Normal file
30
front/src/core/services/auth-service.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { useUserStore } from "../../store";
|
||||||
|
import { Api } from "../api/Api";
|
||||||
|
import { UserDto, UserLoginDto, UserViewModel } from "../api/data-contracts";
|
||||||
|
|
||||||
|
export class AuthService {
|
||||||
|
private readonly _api: Api;
|
||||||
|
|
||||||
|
constructor(api: Api) {
|
||||||
|
this._api = api;
|
||||||
|
|
||||||
|
}
|
||||||
|
async login(data: UserLoginDto) {
|
||||||
|
let result = await this._api.auth(data);
|
||||||
|
console.log(result);
|
||||||
|
const store = useUserStore();
|
||||||
|
store.updateUser(result.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
logout() {
|
||||||
|
const store = useUserStore();
|
||||||
|
store.updateUser({} as UserViewModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
async register(data: UserDto) {
|
||||||
|
let result = await this._api.authRegister(data);
|
||||||
|
const store = useUserStore();
|
||||||
|
store.updateUser(result.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
42
front/src/core/services/change-record-service.ts
Normal file
42
front/src/core/services/change-record-service.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { useUserStore } from "../../store";
|
||||||
|
import { Api } from "../api/Api";
|
||||||
|
import { ChangeRecordDto, ChangeRecordViewModel } from "../api/data-contracts";
|
||||||
|
|
||||||
|
export class ChangeRecordService {
|
||||||
|
private readonly _api: Api
|
||||||
|
constructor(api: Api) {
|
||||||
|
this._api = api;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _updateUser() {
|
||||||
|
const store = useUserStore();
|
||||||
|
let updatedUser = await this._api.userGet({ Id: store.user.id });
|
||||||
|
|
||||||
|
store.updateUser(updatedUser.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async createRecord(data: ChangeRecordDto) {
|
||||||
|
let result = await this._api.changeRecordCreate(data);
|
||||||
|
this._updateUser();
|
||||||
|
console.log(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteRecord(Id: string) {
|
||||||
|
console.log(Id);
|
||||||
|
let result = await this._api.changeRecordDelete({ Id });
|
||||||
|
this._updateUser();
|
||||||
|
console.log("delete");
|
||||||
|
console.log(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getList(): Promise<ChangeRecordViewModel[] | null> {
|
||||||
|
const store = useUserStore();
|
||||||
|
if (!store.user.id) return null;
|
||||||
|
|
||||||
|
let UserId = store.user.id;
|
||||||
|
let result = await this._api.changeRecordFilterList({ UserId });
|
||||||
|
|
||||||
|
console.log(result);
|
||||||
|
return result.data;
|
||||||
|
}
|
||||||
|
}
|
33
front/src/core/services/group-service.ts
Normal file
33
front/src/core/services/group-service.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { useUserStore } from "../../store";
|
||||||
|
import { Api } from "../api/Api";
|
||||||
|
import { SpendingGroupDto, SpendingGroupViewModel } from "../api/data-contracts";
|
||||||
|
|
||||||
|
export class GroupService {
|
||||||
|
private readonly _api: Api;
|
||||||
|
constructor(api: Api) {
|
||||||
|
this._api = api;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getList(): Promise<SpendingGroupViewModel[] | null> {
|
||||||
|
const store = useUserStore();
|
||||||
|
console.log("get list " + store.user.id);
|
||||||
|
if (!store.user.id) return null;
|
||||||
|
|
||||||
|
let UserId = store.user.id;
|
||||||
|
let result = await this._api.spendingGroupFilterList({ UserId });
|
||||||
|
|
||||||
|
console.log(result);
|
||||||
|
return result.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteGroup(Id: string) {
|
||||||
|
let result = await this._api.spendingGroupDelete({ Id });
|
||||||
|
console.log("delete");
|
||||||
|
console.log(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
async createGroup(data: SpendingGroupDto) {
|
||||||
|
let result = await this._api.spendingGroupCreate(data);
|
||||||
|
console.log(result);
|
||||||
|
}
|
||||||
|
}
|
26
front/src/core/services/plans-service.ts
Normal file
26
front/src/core/services/plans-service.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { Api } from "../api/Api";
|
||||||
|
import { SpendingPlanDto, SpendingPlanViewModel } from "../api/data-contracts";
|
||||||
|
|
||||||
|
export class PlanService {
|
||||||
|
|
||||||
|
private readonly _api: Api
|
||||||
|
constructor(api: Api) {
|
||||||
|
this._api = api;
|
||||||
|
}
|
||||||
|
|
||||||
|
async deletePlan(Id: string) {
|
||||||
|
let result = await this._api.spendingPlanDelete({ Id });
|
||||||
|
console.log("delete");
|
||||||
|
console.log(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getList(groupId: string): Promise<SpendingPlanViewModel[] | null> {
|
||||||
|
const result = await this._api.spendingGroupDetail(groupId);
|
||||||
|
return result.data.spendingPlans || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async createPlan(data: SpendingPlanDto) {
|
||||||
|
const result = await this._api.spendingPlanCreate(data);
|
||||||
|
console.log(result);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,24 @@
|
|||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import './style.css'
|
import './style.css'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
import router from './router'
|
||||||
|
import { Api } from './core/api/Api'
|
||||||
|
import { createPinia } from 'pinia'
|
||||||
|
import { AuthService } from './core/services/auth-service'
|
||||||
|
import { ChangeRecordService } from './core/services/change-record-service'
|
||||||
|
import { GroupService } from './core/services/group-service'
|
||||||
|
import { PlanService } from './core/services/plans-service'
|
||||||
|
|
||||||
createApp(App).mount('#app')
|
const app = createApp(App)
|
||||||
|
|
||||||
|
app.use(router)
|
||||||
|
app.use(createPinia())
|
||||||
|
|
||||||
|
// Di
|
||||||
|
const api = new Api();
|
||||||
|
app.provide(AuthService.name, new AuthService(api));
|
||||||
|
app.provide(ChangeRecordService.name, new ChangeRecordService(api));
|
||||||
|
app.provide(GroupService.name, new GroupService(api));
|
||||||
|
app.provide(PlanService.name, new PlanService(api));
|
||||||
|
|
||||||
|
app.mount('#app')
|
||||||
|
46
front/src/router.ts
Normal file
46
front/src/router.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { createRouter, createWebHistory } from 'vue-router';
|
||||||
|
import { useUserStore } from './store';
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(),
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
name: 'home',
|
||||||
|
component: () => import('./components/pages/Home.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/login',
|
||||||
|
name: 'login',
|
||||||
|
component: () => import('./components/pages/Login.vue'),
|
||||||
|
meta: { notRequiresAuth: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/signup',
|
||||||
|
name: 'signup',
|
||||||
|
component: () => import('./components/pages/SignUp.vue'),
|
||||||
|
meta: { notRequiresAuth: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/groups',
|
||||||
|
name: 'groups',
|
||||||
|
component: () => import('./components/pages/Groups.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/plans/:groupId',
|
||||||
|
name: 'plans',
|
||||||
|
component: () => import('./components/pages/Plans.vue'),
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
router.beforeEach((to, from, next) => {
|
||||||
|
const store = useUserStore();
|
||||||
|
if (!to.meta.notRequiresAuth && !store.user.id) {
|
||||||
|
next({ name: 'login' });
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
19
front/src/store.ts
Normal file
19
front/src/store.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { UserViewModel } from './core/api/data-contracts'
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
|
export interface State {
|
||||||
|
user: UserViewModel
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useUserStore = defineStore('user', {
|
||||||
|
state: (): State => ({
|
||||||
|
user: {} as UserViewModel
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
updateUser(payload: UserViewModel) {
|
||||||
|
if (!payload) return;
|
||||||
|
|
||||||
|
this.user = payload;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
@ -1,7 +1,18 @@
|
|||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import Components from 'unplugin-vue-components/vite'
|
||||||
|
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'
|
||||||
|
|
||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [vue()],
|
plugins: [
|
||||||
|
vue(),
|
||||||
|
Components({
|
||||||
|
resolvers: [
|
||||||
|
AntDesignVueResolver({
|
||||||
|
importStyle: false, // css in js
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user