merge ui
This commit is contained in:
@@ -2,17 +2,4 @@
|
||||
|
||||
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.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
|
||||
|
||||
## Type Support For `.vue` Imports in TS
|
||||
|
||||
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
|
||||
|
||||
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
|
||||
|
||||
1. Disable the built-in TypeScript Extension
|
||||
1. Run `Extensions: Show Built-in Extensions` from VSCode's command palette
|
||||
2. Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
|
||||
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
|
||||
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).
|
||||
|
||||
@@ -20,7 +20,7 @@ export function Count(): Promise<number> & { cancel(): void } {
|
||||
|
||||
export function Create(item: $models.Author): Promise<$models.Author> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3684602449, item) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
let $typingPromise = $resultPromise.then(($result) => {
|
||||
return $$createType0($result);
|
||||
}) as any;
|
||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||
@@ -34,7 +34,7 @@ export function Delete(id: number): Promise<void> & { cancel(): void } {
|
||||
|
||||
export function GetAll(): Promise<($models.Author | null)[]> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3248293926) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
let $typingPromise = $resultPromise.then(($result) => {
|
||||
return $$createType2($result);
|
||||
}) as any;
|
||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||
@@ -43,7 +43,7 @@ export function GetAll(): Promise<($models.Author | null)[]> & { cancel(): void
|
||||
|
||||
export function GetById(id: number): Promise<$models.Author | null> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1703016211, id) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
let $typingPromise = $resultPromise.then(($result) => {
|
||||
return $$createType1($result);
|
||||
}) as any;
|
||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||
@@ -52,7 +52,7 @@ export function GetById(id: number): Promise<$models.Author | null> & { cancel()
|
||||
|
||||
export function Update(item: $models.Author): Promise<$models.Author> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(2240704960, item) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
let $typingPromise = $resultPromise.then(($result) => {
|
||||
return $$createType0($result);
|
||||
}) as any;
|
||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||
|
||||
@@ -20,7 +20,7 @@ export function Count(): Promise<number> & { cancel(): void } {
|
||||
|
||||
export function Create(item: $models.Post): Promise<$models.Post> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1443399856, item) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
let $typingPromise = $resultPromise.then(($result) => {
|
||||
return $$createType0($result);
|
||||
}) as any;
|
||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||
@@ -39,7 +39,7 @@ export function ExportToExcel(): Promise<void> & { cancel(): void } {
|
||||
|
||||
export function GetAll(): Promise<($models.Post | null)[]> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(65691059) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
let $typingPromise = $resultPromise.then(($result) => {
|
||||
return $$createType2($result);
|
||||
}) as any;
|
||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||
@@ -48,7 +48,7 @@ export function GetAll(): Promise<($models.Post | null)[]> & { cancel(): void }
|
||||
|
||||
export function GetById(id: number): Promise<$models.Post | null> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(4074736792, id) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
let $typingPromise = $resultPromise.then(($result) => {
|
||||
return $$createType1($result);
|
||||
}) as any;
|
||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||
@@ -57,7 +57,7 @@ export function GetById(id: number): Promise<$models.Post | null> & { cancel():
|
||||
|
||||
export function Update(item: $models.Post): Promise<$models.Post> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(137798821, item) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
let $typingPromise = $resultPromise.then(($result) => {
|
||||
return $$createType0($result);
|
||||
}) as any;
|
||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||
|
||||
BIN
frontend/bun.lockb
Executable file
BIN
frontend/bun.lockb
Executable file
Binary file not shown.
BIN
frontend/dist/Inter-Medium.ttf
vendored
BIN
frontend/dist/Inter-Medium.ttf
vendored
Binary file not shown.
3537
frontend/dist/assets/index-Z-UIusQp.js
vendored
Normal file
3537
frontend/dist/assets/index-Z-UIusQp.js
vendored
Normal file
File diff suppressed because one or more lines are too long
22
frontend/dist/assets/index-dNSs0EXc.js
vendored
22
frontend/dist/assets/index-dNSs0EXc.js
vendored
File diff suppressed because one or more lines are too long
1
frontend/dist/assets/index-hwTd8BmI.css
vendored
Normal file
1
frontend/dist/assets/index-hwTd8BmI.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
frontend/dist/assets/index-tI2uSkFL.css
vendored
1
frontend/dist/assets/index-tI2uSkFL.css
vendored
@@ -1 +0,0 @@
|
||||
.logo[data-v-7fd835af]{height:6em;padding:1.5em;will-change:filter}.logo[data-v-7fd835af]:hover{filter:drop-shadow(0 0 2em #e80000aa)}.logo.vue[data-v-7fd835af]:hover{filter:drop-shadow(0 0 2em #42b883aa)}
|
||||
BIN
frontend/dist/assets/primeicons-C6QP2o4f.woff2
vendored
Normal file
BIN
frontend/dist/assets/primeicons-C6QP2o4f.woff2
vendored
Normal file
Binary file not shown.
BIN
frontend/dist/assets/primeicons-DMOk5skT.eot
vendored
Normal file
BIN
frontend/dist/assets/primeicons-DMOk5skT.eot
vendored
Normal file
Binary file not shown.
345
frontend/dist/assets/primeicons-Dr5RGzOO.svg
vendored
Normal file
345
frontend/dist/assets/primeicons-Dr5RGzOO.svg
vendored
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 334 KiB |
BIN
frontend/dist/assets/primeicons-MpK4pl85.ttf
vendored
Normal file
BIN
frontend/dist/assets/primeicons-MpK4pl85.ttf
vendored
Normal file
Binary file not shown.
BIN
frontend/dist/assets/primeicons-WjwUDZjB.woff
vendored
Normal file
BIN
frontend/dist/assets/primeicons-WjwUDZjB.woff
vendored
Normal file
Binary file not shown.
11
frontend/dist/index.html
vendored
11
frontend/dist/index.html
vendored
@@ -1,13 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/wails.png" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<title>Wails + Vue + TS</title>
|
||||
<script type="module" crossorigin src="/assets/index-dNSs0EXc.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-tI2uSkFL.css">
|
||||
<title>Vite + Vue + TS</title>
|
||||
<script type="module" crossorigin src="/assets/index-Z-UIusQp.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-hwTd8BmI.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
146
frontend/dist/style.css
vendored
146
frontend/dist/style.css
vendored
@@ -1,146 +0,0 @@
|
||||
:root {
|
||||
font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
|
||||
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: 400;
|
||||
color-scheme: light dark;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background-color: rgba(27, 38, 54, 1);
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
* {
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local(""),
|
||||
url("./Inter-Medium.ttf") format("truetype");
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 3em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: #646cff;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #535bf2;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 60px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
border-radius: 3px;
|
||||
border: none;
|
||||
margin: 0 0 0 20px;
|
||||
padding: 0 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.result {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
place-content: center;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3.2em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.result {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
margin: 1.5rem auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 1rem;
|
||||
align-content: center;
|
||||
text-align: center;
|
||||
color: rgba(255, 255, 255, 0.67);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #747bff;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.input-box .btn:hover {
|
||||
background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%);
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.input-box .input {
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
outline: none;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding: 0 10px;
|
||||
color: black;
|
||||
background-color: rgba(240, 240, 240, 1);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.input-box .input:hover {
|
||||
border: none;
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
.input-box .input:focus {
|
||||
border: none;
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
1
frontend/dist/vue.svg
vendored
1
frontend/dist/vue.svg
vendored
@@ -1 +0,0 @@
|
||||
<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>
|
||||
|
Before Width: | Height: | Size: 496 B |
BIN
frontend/dist/wails.png
vendored
BIN
frontend/dist/wails.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 8.8 KiB |
@@ -1,11 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/wails.png" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<title>Wails + Vue + TS</title>
|
||||
<title>Vite + Vue + TS</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
2473
frontend/package-lock.json
generated
2473
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,21 +1,29 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"name": "nto-ui",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc && vite build",
|
||||
"build": "vue-tsc -b && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.2.45"
|
||||
"@primevue/themes": "^4.2.5",
|
||||
"pinia": "^2.3.0",
|
||||
"primeicons": "^7.0.0",
|
||||
"primevue": "^4.2.5",
|
||||
"vue": "^3.5.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"typescript": "^4.9.3",
|
||||
"vite": "^5.0.0",
|
||||
"@wailsio/runtime": "latest",
|
||||
"vue-tsc": "^1.0.11"
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"@vue/tsconfig": "^0.7.0",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"install": "^0.13.0",
|
||||
"postcss": "^8.4.49",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"typescript": "~5.6.2",
|
||||
"vite": "^6.0.5",
|
||||
"vue-tsc": "^2.2.0"
|
||||
}
|
||||
}
|
||||
6
frontend/postcss.config.js
Normal file
6
frontend/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
Binary file not shown.
@@ -1,146 +0,0 @@
|
||||
:root {
|
||||
font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
|
||||
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: 400;
|
||||
color-scheme: light dark;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background-color: rgba(27, 38, 54, 1);
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
* {
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local(""),
|
||||
url("./Inter-Medium.ttf") format("truetype");
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 3em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: #646cff;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #535bf2;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 60px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
border-radius: 3px;
|
||||
border: none;
|
||||
margin: 0 0 0 20px;
|
||||
padding: 0 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.result {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
place-content: center;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3.2em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.result {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
margin: 1.5rem auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 1rem;
|
||||
align-content: center;
|
||||
text-align: center;
|
||||
color: rgba(255, 255, 255, 0.67);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #747bff;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.input-box .btn:hover {
|
||||
background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%);
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.input-box .input {
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
outline: none;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding: 0 10px;
|
||||
color: black;
|
||||
background-color: rgba(240, 240, 240, 1);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.input-box .input:hover {
|
||||
border: none;
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
.input-box .input:focus {
|
||||
border: none;
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<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>
|
||||
|
Before Width: | Height: | Size: 496 B |
Binary file not shown.
|
Before Width: | Height: | Size: 8.8 KiB |
@@ -1,31 +1,62 @@
|
||||
<script setup lang="ts">
|
||||
import HelloWorld from './components/HelloWorld.vue'
|
||||
import { ref } from 'vue';
|
||||
import type { IEntity } from './types/entity.type';
|
||||
import type { IService } from './types/service.type';
|
||||
import type { Scheme } from './types/scheme.type';
|
||||
import Table from './table/Table.vue';
|
||||
import { getDefaultValues } from './utils/structs/defaults.util';
|
||||
|
||||
class Entity implements IEntity {
|
||||
constructor(public Id: number, public Name: string, public Region: string) { }
|
||||
}
|
||||
|
||||
class Service implements IService<Entity> {
|
||||
private readonly entities = ref<Entity[]>([])
|
||||
private maxId = 0
|
||||
|
||||
async create(data: Entity): Promise<void | never> {
|
||||
this.maxId++
|
||||
this.entities.value.push({ ...data, Id: this.maxId })
|
||||
}
|
||||
async delete(id: number): Promise<void | never> {
|
||||
this.entities.value = this.entities.value.filter(el => el.Id != id)
|
||||
}
|
||||
async read(id: number): Promise<Entity | undefined> {
|
||||
return this.entities.value.find(el => el.Id == id)
|
||||
}
|
||||
async readAll(): Promise<Entity[]> {
|
||||
return this.entities.value
|
||||
}
|
||||
async update(data: Entity): Promise<void | never> {
|
||||
this.entities.value = this.entities.value.map(el => el.Id == data.Id ? data : el)
|
||||
}
|
||||
}
|
||||
|
||||
const service = new Service
|
||||
|
||||
const scheme: Scheme<Entity> = {
|
||||
Id: {
|
||||
hidden: true,
|
||||
russian: 'Id'
|
||||
},
|
||||
Name: {
|
||||
type: {
|
||||
primitive: 'string'
|
||||
},
|
||||
russian: 'Имя'
|
||||
},
|
||||
Region: {
|
||||
type: {
|
||||
nested: {
|
||||
field: [],
|
||||
values: ['kemerovo', 'kuzbass', 'berlin']
|
||||
}
|
||||
},
|
||||
russian: "Место жительства"
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container">
|
||||
<div>
|
||||
<a wml-openURL="https://wails.io">
|
||||
<img src="/wails.png" class="logo" alt="Wails logo"/>
|
||||
</a>
|
||||
<a wml-openURL="https://vuejs.org/">
|
||||
<img src="/vue.svg" class="logo vue" alt="Vue logo"/>
|
||||
</a>
|
||||
</div>
|
||||
<HelloWorld msg="Wails + Vue" />
|
||||
</div>
|
||||
<Table :scheme :service :get-defaults="() => getDefaultValues(scheme)"></Table>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.logo {
|
||||
height: 6em;
|
||||
padding: 1.5em;
|
||||
will-change: filter;
|
||||
}
|
||||
.logo:hover {
|
||||
filter: drop-shadow(0 0 2em #e80000aa);
|
||||
}
|
||||
.logo.vue:hover {
|
||||
filter: drop-shadow(0 0 2em #42b883aa);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from "vue";
|
||||
import { Events } from "@wailsio/runtime";
|
||||
import * as PostService from "../../bindings/app/internal/services/postservice";
|
||||
|
||||
const name = ref("");
|
||||
const result = ref("Please enter your name below 👇");
|
||||
const time = ref("Listening for Time event...");
|
||||
|
||||
const doGreet = () => {
|
||||
let localName = name.value;
|
||||
if (!localName) {
|
||||
localName = "anonymous";
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
console.log(await PostService.GetById(5));
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>Kuzbass</h1>
|
||||
|
||||
<div class="result"></div>
|
||||
<div class="card">
|
||||
<div class="input-box">
|
||||
<input
|
||||
class="input"
|
||||
v-model="name"
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<button class="btn" @click="doGreet">Greet</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<div><p>Click on the Wails logo to learn more</p></div>
|
||||
<div><p></p></div>
|
||||
</div>
|
||||
</template>
|
||||
4
frontend/src/components/buttons/FloatingButton.vue
Normal file
4
frontend/src/components/buttons/FloatingButton.vue
Normal file
@@ -0,0 +1,4 @@
|
||||
<template>
|
||||
<button class="fixed bottom-6 right-6 aspect-square h-10 text-3xl flex items-center justify-center rounded-full focus-visible:outline-none text-black"
|
||||
style="background:var(--p-primary-color)"><span class="pi pi-plus"></span></button>
|
||||
</template>
|
||||
@@ -1,4 +1,12 @@
|
||||
import { createApp } from 'vue'
|
||||
import './style.css'
|
||||
import App from './App.vue'
|
||||
import { Config } from 'primevue'
|
||||
import Aura from '@primevue/themes/aura'
|
||||
import 'primeicons/primeicons.css'
|
||||
|
||||
createApp(App).mount('#app')
|
||||
createApp(App).use(Config, {
|
||||
theme: {
|
||||
preset: Aura
|
||||
}
|
||||
}).mount('#app')
|
||||
|
||||
43
frontend/src/post/PostScheme.vue
Normal file
43
frontend/src/post/PostScheme.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<script setup lang="ts">
|
||||
import Table from '../table/Table.vue'
|
||||
import { onMounted, reactive } from 'vue'
|
||||
import { getDefaultValues } from '../utils/structs/defaults.util'
|
||||
import S from './post.service.ts'
|
||||
import type { Scheme } from '../types/scheme.type'
|
||||
import { Post } from '../../bindings/app/internal/services/models.ts'
|
||||
|
||||
const service = new S
|
||||
|
||||
onMounted(async () => {
|
||||
|
||||
})
|
||||
|
||||
|
||||
const scheme: Scheme<Post> = reactive({
|
||||
Id:{
|
||||
type: {
|
||||
primitive: "number",
|
||||
},
|
||||
},
|
||||
Text:{
|
||||
type: {
|
||||
primitive: "string",
|
||||
},
|
||||
},
|
||||
CreatedAt:{
|
||||
type: {
|
||||
primitive: "number",
|
||||
},
|
||||
},
|
||||
|
||||
})
|
||||
|
||||
const getDefaults = () => getDefaultValues(scheme)
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="w-screen h-screen">
|
||||
<Table :scheme :service :getDefaults></Table>
|
||||
</main>
|
||||
</template>
|
||||
27
frontend/src/post/post.service.ts
Normal file
27
frontend/src/post/post.service.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { GetAll, Create, Delete, GetById, Update, Count } from "../../bindings/app/internal/services/postservice.ts"
|
||||
import type { Post } from "../../bindings/app/internal/services/models.ts"
|
||||
import type { IService } from "../types/service.type.ts"
|
||||
|
||||
export default class PostService implements IService<Post> {
|
||||
async read(id: number) {
|
||||
return await GetById(id)
|
||||
}
|
||||
|
||||
async readAll() {
|
||||
return await GetAll()
|
||||
}
|
||||
|
||||
async create(item: Post) {
|
||||
await Create(item)
|
||||
}
|
||||
|
||||
async delete(id: number) {
|
||||
return await Delete(id)
|
||||
}
|
||||
async update(item: Post) {
|
||||
await Update(item)
|
||||
}
|
||||
async count() {
|
||||
return await Count()
|
||||
}
|
||||
}
|
||||
3
frontend/src/style.css
Normal file
3
frontend/src/style.css
Normal file
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
94
frontend/src/table/DialogWindow.vue
Normal file
94
frontend/src/table/DialogWindow.vue
Normal file
@@ -0,0 +1,94 @@
|
||||
<script setup lang="ts" generic="T extends IEntity">
|
||||
import { Button, DatePicker, Dialog, InputNumber, InputText, MultiSelect, Select, Textarea, ToggleSwitch } from 'primevue';
|
||||
import type { IEntity } from '../types/entity.type';
|
||||
import type { Scheme } from '../types/scheme.type';
|
||||
import type { IService } from '../types/service.type';
|
||||
import { manyStructsView } from '../utils/structs/structs-view.util';
|
||||
import { type UnwrapRef } from 'vue';
|
||||
|
||||
const showCreate = defineModel<boolean>('show')
|
||||
const createItem = defineModel<T>('item')
|
||||
|
||||
const items = defineModel<UnwrapRef<T[]>>('items')
|
||||
|
||||
const props = defineProps<{
|
||||
scheme: Scheme<T>,
|
||||
getDefaults: () => Partial<T>,
|
||||
service: IService<T>,
|
||||
updateMode?: boolean
|
||||
}>()
|
||||
|
||||
type Key = keyof T
|
||||
const keys = Object.keys(props.scheme) as Key[]
|
||||
|
||||
const emits = defineEmits<{
|
||||
(e: 'onSave', data: any): void
|
||||
(e: 'onSaveUpdate', data: any): void
|
||||
(e: 'onSaveCreate', data: any): void
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Dialog v-model:visible="showCreate">
|
||||
<div class="flex flex-col justify-center gap-5">
|
||||
<div v-for="key in keys" v-show="!props.scheme[key].hidden && !props.scheme[key].readonly"
|
||||
class="flex items-center gap-5">
|
||||
<h1 class="w-[200px]">{{ props.scheme[key].russian }}</h1>
|
||||
<div>
|
||||
<div v-if="props.scheme[key]?.customWindow?.[props.updateMode ? 'update' : 'create']">
|
||||
<slot :name="<string>key + (props.updateMode ? 'Update' : 'Create')"></slot>
|
||||
</div>
|
||||
<div v-else-if="props.scheme[key]?.customWindow?.common">
|
||||
<slot :name="<string>key"></slot>
|
||||
</div>
|
||||
<InputNumber class="w-[300px]" v-model:model-value="<number>createItem![key]"
|
||||
v-else-if="props.scheme[key]?.type?.primitive === 'number'" />
|
||||
<InputText class="w-[300px]" v-model:model-value="<string>createItem![key]"
|
||||
v-else-if="props.scheme[key].type?.primitive === 'string'" />
|
||||
<DatePicker class="w-[300px]" v-model:model-value="<Date>createItem![key]"
|
||||
v-else-if="props.scheme[key].type?.primitive === 'date'" />
|
||||
<Textarea class="w-[300px]" v-model="<string>createItem![key]"
|
||||
v-else-if="props.scheme[key].type?.primitive === 'multiple'" />
|
||||
<ToggleSwitch class="w-[300px]" v-model:model-value="<boolean>createItem![key]"
|
||||
v-else-if="props.scheme[key].type?.primitive === 'boolean'" />
|
||||
<Select v-else-if="props.scheme[key].type?.nested?.values && !props.scheme[key].type?.many"
|
||||
v-model:model-value="createItem![key]" :options="props.scheme[key].type.nested.values"
|
||||
:placeholder="`Выберите ${props.scheme[key].russian}`" class="w-[300px]">
|
||||
<template #option="{ option }">
|
||||
{{ manyStructsView(option, props.scheme[key].type.nested.field) }}
|
||||
</template>
|
||||
<template #value="{ value }">
|
||||
{{ manyStructsView(value, props.scheme[key].type.nested.field) }}
|
||||
</template>
|
||||
</Select>
|
||||
<MultiSelect v-else-if="props.scheme[key].type?.many" v-model:model-value="createItem![key]"
|
||||
:options="props.scheme[key].type?.nested?.values"
|
||||
class="w-[300px] h-11"
|
||||
:placeholder="`Выберите ${props.scheme[key].russian}`">
|
||||
<template #option="{ option }">
|
||||
{{ manyStructsView(option, props.scheme[key]?.type?.nested?.field) }}
|
||||
</template>
|
||||
<template #value="{ value }">
|
||||
{{ manyStructsView(value, props.scheme[key]?.type?.nested?.field) }}
|
||||
</template>
|
||||
</MultiSelect>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<Button severity="success" @click="async () => {
|
||||
if (props.updateMode) {
|
||||
props.service.update(createItem as T)
|
||||
emits('onSaveUpdate', createItem as T)
|
||||
emits('onSave', createItem as T)
|
||||
} else {
|
||||
props.service.create(createItem as T)
|
||||
emits('onSaveCreate', createItem as T)
|
||||
emits('onSave', createItem as T)
|
||||
}
|
||||
items = await service.readAll() as UnwrapRef<T[]>
|
||||
showCreate = false
|
||||
}">Сохранить</Button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
155
frontend/src/table/Table.vue
Normal file
155
frontend/src/table/Table.vue
Normal file
@@ -0,0 +1,155 @@
|
||||
<script setup lang="ts" generic="T extends IEntity">
|
||||
import { onMounted, ref, watch, type UnwrapRef } from "vue";
|
||||
import type { TableProps } from "../types/table-props.type";
|
||||
import { DataTable, Column, Button } from "primevue";
|
||||
import { manyStructsView } from "../utils/structs/structs-view.util";
|
||||
import type { TableEmits } from "../types/table-emits.type";
|
||||
import FloatingButton from "../components/buttons/FloatingButton.vue";
|
||||
import type { IEntity } from "../types/entity.type";
|
||||
import DialogWindow from "./DialogWindow.vue";
|
||||
|
||||
const props = defineProps<TableProps<T>>();
|
||||
|
||||
const items = ref<T[]>([]);
|
||||
|
||||
onMounted(async () => {
|
||||
items.value = await props.service.readAll();
|
||||
});
|
||||
|
||||
type Key = keyof T;
|
||||
const keys = Object.keys(props.scheme) as Key[];
|
||||
const emits = defineEmits<TableEmits>();
|
||||
|
||||
const showCreate = ref(false);
|
||||
const createItem = ref<null | T>(null);
|
||||
|
||||
const showUpdate = ref(false);
|
||||
const updateItem = ref<null | T>(null);
|
||||
|
||||
watch(showUpdate, (value) => {
|
||||
if (!value) {
|
||||
updateItem.value = null;
|
||||
}
|
||||
});
|
||||
|
||||
watch(updateItem, (value) => {
|
||||
if (value) {
|
||||
showUpdate.value = true;
|
||||
} else {
|
||||
showUpdate.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
watch(showCreate, (value) => {
|
||||
if (value) {
|
||||
createItem.value = props.getDefaults();
|
||||
} else {
|
||||
createItem.value = null;
|
||||
}
|
||||
});
|
||||
|
||||
watch(createItem, (value) => {
|
||||
if (value) {
|
||||
showCreate.value = true;
|
||||
} else {
|
||||
showCreate.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
const handleFloatingButtonClick = () => {
|
||||
emits('onCreateOpen')
|
||||
emits('onOpen')
|
||||
showCreate.value = true;
|
||||
};
|
||||
const slots = defineSlots();
|
||||
|
||||
const createSlotName = (key: any) => key + "Create";
|
||||
const updateSlotName = (key: any) => key + "Update";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DialogWindow
|
||||
:scheme="props.scheme"
|
||||
:service="props.service"
|
||||
:get-defaults="props.getDefaults"
|
||||
v-model:item="<T>createItem"
|
||||
v-model:show="showCreate"
|
||||
v-model:items="items"
|
||||
@on-save="data => emits('onSave', data)"
|
||||
@on-save-create="data => emits('onSaveCreate', data)"
|
||||
>
|
||||
<template v-for="key in keys" #[key]>
|
||||
<slot :name="<string>key"></slot>
|
||||
</template>
|
||||
<template v-for="key in keys" #[createSlotName(key)]>
|
||||
<slot :name="createSlotName(key)"></slot>
|
||||
</template>
|
||||
</DialogWindow>
|
||||
<DialogWindow
|
||||
:scheme="props.scheme"
|
||||
update-mode
|
||||
:service="props.service"
|
||||
:get-defaults="props.getDefaults"
|
||||
v-model:item="<T>updateItem"
|
||||
v-model:show="showUpdate"
|
||||
v-model:items="items"
|
||||
@on-save="data => emits('onSave', data)"
|
||||
@on-save-update="data => emits('onSaveUpdate', data)"
|
||||
>
|
||||
<template v-for="key in keys" #[key]>
|
||||
<slot :name="<string>key"></slot>
|
||||
</template>
|
||||
<template v-for="key in keys" #[updateSlotName(key)]>
|
||||
<slot :name="updateSlotName(key)"></slot>
|
||||
</template>
|
||||
</DialogWindow>
|
||||
<div>
|
||||
<DataTable :value="<[]>items">
|
||||
<template #header v-if="props.name">
|
||||
<p>{{ props.name }}</p>
|
||||
</template>
|
||||
<template v-for="key in keys">
|
||||
<Column
|
||||
:header="props.scheme[key]?.russian"
|
||||
v-if="!props.scheme[key].hidden"
|
||||
>
|
||||
<template #body="{ data }">
|
||||
<p>
|
||||
{{
|
||||
manyStructsView(
|
||||
data[key],
|
||||
props.scheme[key]?.type?.nested?.field,
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
</template>
|
||||
</Column>
|
||||
</template>
|
||||
<Column header="Действия">
|
||||
<template #body="{ data }">
|
||||
<div class="flex gap-2">
|
||||
<Button
|
||||
severity="secondary"
|
||||
icon="pi pi-pencil"
|
||||
@click="() => {
|
||||
emits('onUpdateOpen')
|
||||
emits('onOpen')
|
||||
updateItem = data
|
||||
}"
|
||||
></Button>
|
||||
<Button
|
||||
severity="danger"
|
||||
icon="pi pi-trash"
|
||||
@click="async () => {
|
||||
emits('onDelete', data)
|
||||
await props.service.delete(data.Id)
|
||||
items = await props.service.readAll() as UnwrapRef<T[]>
|
||||
}"
|
||||
></Button>
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
</DataTable>
|
||||
<FloatingButton @click="handleFloatingButtonClick" />
|
||||
</div>
|
||||
</template>
|
||||
3
frontend/src/types/entity.type.ts
Normal file
3
frontend/src/types/entity.type.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface IEntity {
|
||||
Id: number
|
||||
}
|
||||
1
frontend/src/types/primitive-field-type.type.ts
Normal file
1
frontend/src/types/primitive-field-type.type.ts
Normal file
@@ -0,0 +1 @@
|
||||
export type PrimitiveFieldType = "date" | "number" | "string" | "boolean" | "multiple"
|
||||
21
frontend/src/types/scheme-field.type.ts
Normal file
21
frontend/src/types/scheme-field.type.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import type { IEntity } from "./entity.type"
|
||||
import type { PrimitiveFieldType } from "./primitive-field-type.type"
|
||||
|
||||
export interface ISchemeField<T extends IEntity> {
|
||||
type?: {
|
||||
primitive?: PrimitiveFieldType
|
||||
nested?: {
|
||||
field: string[]
|
||||
values: T[]
|
||||
}
|
||||
many?: boolean
|
||||
}
|
||||
russian?: string
|
||||
hidden?: boolean
|
||||
readonly?: boolean
|
||||
customWindow?: {
|
||||
common?: boolean
|
||||
create?: boolean
|
||||
update?: boolean
|
||||
}
|
||||
}
|
||||
4
frontend/src/types/scheme.type.ts
Normal file
4
frontend/src/types/scheme.type.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import type { IEntity } from "./entity.type";
|
||||
import type { ISchemeField } from "./scheme-field.type";
|
||||
|
||||
export type Scheme<T extends IEntity, S extends IEntity=any> = Record<keyof T, ISchemeField<S>>
|
||||
7
frontend/src/types/service.type.ts
Normal file
7
frontend/src/types/service.type.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export interface IService<T> {
|
||||
read(id: number): Promise<T | undefined>
|
||||
readAll(): Promise<T[]>
|
||||
create(data: T): Promise<void | never>
|
||||
update(data: T): Promise<void | never>
|
||||
delete(id: number): Promise<void | never>
|
||||
}
|
||||
12
frontend/src/types/table-emits.type.ts
Normal file
12
frontend/src/types/table-emits.type.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export type TableEmits = {
|
||||
(e: 'onCreateOpen'): void
|
||||
(e: 'onCreateClose', data: any): void
|
||||
(e: 'onUpdateOpen'): void
|
||||
(e: 'onUpdateClose', data: any): void
|
||||
(e: 'onOpen'): void
|
||||
(e: 'onClose', data: any): void
|
||||
(e: 'onDelete', data: any): void
|
||||
(e: 'onSaveUpdate', data: any): void
|
||||
(e: 'onSaveCreate', data: any): void
|
||||
(e: 'onSave', data: any): void
|
||||
}
|
||||
11
frontend/src/types/table-props.type.ts
Normal file
11
frontend/src/types/table-props.type.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import type { IEntity } from "./entity.type";
|
||||
import type { Scheme } from "./scheme.type";
|
||||
import type { IService } from "./service.type";
|
||||
|
||||
export interface TableProps<T extends IEntity> {
|
||||
service: IService<T>
|
||||
scheme: Scheme<T>
|
||||
name?: string
|
||||
getDefaults: () => Partial<T>
|
||||
validate?: (data: T) => never | void
|
||||
}
|
||||
10
frontend/src/utils/date/converters.util.ts
Normal file
10
frontend/src/utils/date/converters.util.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
const getFullTimestamp = (n: number): number => {
|
||||
const length = String(n).length
|
||||
let str = ''
|
||||
while (str.length + length < 13) {
|
||||
str += '0'
|
||||
}
|
||||
return parseInt(`${n}${str}`)
|
||||
}
|
||||
export const toDate = (n: number) => new Date(getFullTimestamp(n))
|
||||
export const toTimestamp = (d: Date) => d.getTime()
|
||||
6
frontend/src/utils/date/getters.ts
Normal file
6
frontend/src/utils/date/getters.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export const getTomorrow = (): Date => {
|
||||
const today = new Date();
|
||||
const tomorrow = new Date(today);
|
||||
tomorrow.setDate(today.getDate() + 1);
|
||||
return tomorrow;
|
||||
}
|
||||
27
frontend/src/utils/structs/defaults.util.ts
Normal file
27
frontend/src/utils/structs/defaults.util.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import type { IEntity } from "../../types/entity.type";
|
||||
import type { Scheme } from "../../types/scheme.type";
|
||||
import { getTomorrow } from "../date/getters";
|
||||
|
||||
export const getDefaultValues = <T extends IEntity>(scheme: Scheme<T>) => {
|
||||
const keys = Object.keys(scheme) as (keyof typeof scheme)[]
|
||||
let obj: any = {}
|
||||
|
||||
for (let key of keys) {
|
||||
const primitive = scheme[key]?.type?.primitive
|
||||
if (primitive == 'string' || primitive == 'multiple') {
|
||||
obj[key] = ''
|
||||
} else if (primitive == 'date') {
|
||||
obj[key] = getTomorrow()
|
||||
} else if (primitive == 'boolean') {
|
||||
obj[key] = false
|
||||
} else if (primitive == 'number') {
|
||||
obj[key] = 1
|
||||
} else if (scheme[key].type?.many) {
|
||||
obj[key] = []
|
||||
} else if (scheme[key]?.type?.nested?.values) {
|
||||
obj[key] = scheme[key].type.nested.values[0]
|
||||
}
|
||||
}
|
||||
|
||||
return obj as T
|
||||
}
|
||||
18
frontend/src/utils/structs/structs-view.util.ts
Normal file
18
frontend/src/utils/structs/structs-view.util.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
export const structView = (item: any, path: any) => {
|
||||
if (!item) return;
|
||||
if (!path?.length) return item;
|
||||
let result = item
|
||||
let i = 0
|
||||
let current
|
||||
while (current != path[path.length - 1] && result) {
|
||||
current = path[i]
|
||||
result = result[current]
|
||||
i++
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export const manyStructsView = (items: any, path: any) => {
|
||||
if (!Array.isArray(items)) return structView(items, path);
|
||||
return items.map(item => structView(item, path)).join(", ")
|
||||
}
|
||||
1
frontend/src/utils/sugar/if-null.util.ts
Normal file
1
frontend/src/utils/sugar/if-null.util.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const ifNull = (data: any, elseData: any) => data? data : elseData
|
||||
11
frontend/tailwind.config.js
Normal file
11
frontend/tailwind.config.js
Normal file
@@ -0,0 +1,11 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{vue,js,ts,jsx,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
14
frontend/tsconfig.app.json
Normal file
14
frontend/tsconfig.app.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
|
||||
}
|
||||
@@ -1,20 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"strict": true,
|
||||
"noUnusedParameters": false,
|
||||
"noImplicitAny": false,
|
||||
"jsx": "preserve",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"skipLibCheck": true,
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,9 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2023"],
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"allowSyntheticDefaultImports": true
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user