mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-11 02:15:48 +02:00
fix: resolved eslint errors
This commit is contained in:
parent
287ef7527d
commit
d325dd7493
33 changed files with 643 additions and 584 deletions
9
app/components.d.ts
vendored
9
app/components.d.ts
vendored
|
@ -77,14 +77,6 @@ declare module 'vue' {
|
|||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
SetLanguageSetLanguage: typeof import('./src/components/SetLanguage/SetLanguage.vue')['default']
|
||||
StdDataDisplayStdBatchEdit: typeof import('./src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue')['default']
|
||||
StdDataDisplayStdCurd: typeof import('./src/components/StdDesign/StdDataDisplay/StdCurd.vue')['default']
|
||||
StdDataDisplayStdPagination: typeof import('./src/components/StdDesign/StdDataDisplay/StdPagination.vue')['default']
|
||||
StdDataDisplayStdTable: typeof import('./src/components/StdDesign/StdDataDisplay/StdTable.vue')['default']
|
||||
StdDataEntryComponentsStdPassword: typeof import('./src/components/StdDesign/StdDataEntry/components/StdPassword.vue')['default']
|
||||
StdDataEntryComponentsStdSelect: typeof import('./src/components/StdDesign/StdDataEntry/components/StdSelect.vue')['default']
|
||||
StdDataEntryComponentsStdSelector: typeof import('./src/components/StdDesign/StdDataEntry/components/StdSelector.vue')['default']
|
||||
StdDataEntryStdFormItem: typeof import('./src/components/StdDesign/StdDataEntry/StdFormItem.vue')['default']
|
||||
StdDesignStdDataDisplayStdBatchEdit: typeof import('./src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue')['default']
|
||||
StdDesignStdDataDisplayStdCurd: typeof import('./src/components/StdDesign/StdDataDisplay/StdCurd.vue')['default']
|
||||
StdDesignStdDataDisplayStdPagination: typeof import('./src/components/StdDesign/StdDataDisplay/StdPagination.vue')['default']
|
||||
|
@ -92,6 +84,7 @@ declare module 'vue' {
|
|||
StdDesignStdDataEntryComponentsStdPassword: typeof import('./src/components/StdDesign/StdDataEntry/components/StdPassword.vue')['default']
|
||||
StdDesignStdDataEntryComponentsStdSelect: typeof import('./src/components/StdDesign/StdDataEntry/components/StdSelect.vue')['default']
|
||||
StdDesignStdDataEntryComponentsStdSelector: typeof import('./src/components/StdDesign/StdDataEntry/components/StdSelector.vue')['default']
|
||||
StdDesignStdDataEntryStdDataEntry: typeof import('./src/components/StdDesign/StdDataEntry/StdDataEntry.vue')['default']
|
||||
StdDesignStdDataEntryStdFormItem: typeof import('./src/components/StdDesign/StdDataEntry/StdFormItem.vue')['default']
|
||||
SwitchAppearanceIconsVPIconMoon: typeof import('./src/components/SwitchAppearance/icons/VPIconMoon.vue')['default']
|
||||
SwitchAppearanceIconsVPIconSun: typeof import('./src/components/SwitchAppearance/icons/VPIconSun.vue')['default']
|
||||
|
|
5
app/env.d.ts
vendored
Normal file
5
app/env.d.ts
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
declare module '*.svg' {
|
||||
import React from 'react'
|
||||
const content: React.FC<React.SVGProps<SVGElement>>
|
||||
export default content
|
||||
}
|
|
@ -1,11 +1,10 @@
|
|||
{
|
||||
"name": "nginx-ui-app-next",
|
||||
"private": true,
|
||||
"version": "2.0.0-beta.4",
|
||||
"type": "commonjs",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"lint": "eslint . -c .eslintrc.js --fix --ext .ts,.vue,.tsx",
|
||||
"typecheck": "vue-tsc --noEmit",
|
||||
"lint": "eslint . -c .eslintrc.js --fix --ext .ts,.vue,.tsx,.d.ts",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"gettext:extract": "vue-gettext-extract",
|
||||
|
@ -14,9 +13,6 @@
|
|||
"dependencies": {
|
||||
"@ant-design/icons-vue": "^7.0.1",
|
||||
"@formkit/auto-animate": "^0.8.0",
|
||||
"@types/lodash": "^4.14.202",
|
||||
"@types/nprogress": "^0.2.0",
|
||||
"@types/sortablejs": "^1.15.0",
|
||||
"@vue/reactivity": "^3.3.9",
|
||||
"@vue/shared": "^3.3.9",
|
||||
"@vueuse/core": "^10.6.1",
|
||||
|
@ -45,6 +41,10 @@
|
|||
"xterm-addon-fit": "^0.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash": "^4.14.202",
|
||||
"@types/nprogress": "^0.2.0",
|
||||
"@types/sortablejs": "^1.15.0",
|
||||
"@vue/tsconfig": "^0.4.0",
|
||||
"@antfu/eslint-config-vue": "^0.43.1",
|
||||
"@typescript-eslint/eslint-plugin": "^6.13.0",
|
||||
"@typescript-eslint/parser": "^6.13.0",
|
||||
|
@ -67,7 +67,7 @@
|
|||
"unplugin-auto-import": "^0.17.1",
|
||||
"unplugin-vue-components": "^0.25.2",
|
||||
"unplugin-vue-define-options": "^1.4.0",
|
||||
"vite": "^5.0.2",
|
||||
"vite": "^5.0.3",
|
||||
"vite-plugin-html": "^3.2.0",
|
||||
"vite-svg-loader": "^5.1.0",
|
||||
"vue-tsc": "^1.8.22"
|
||||
|
|
57
app/pnpm-lock.yaml
generated
57
app/pnpm-lock.yaml
generated
|
@ -11,15 +11,6 @@ dependencies:
|
|||
'@formkit/auto-animate':
|
||||
specifier: ^0.8.0
|
||||
version: 0.8.1
|
||||
'@types/lodash':
|
||||
specifier: ^4.14.202
|
||||
version: 4.14.202
|
||||
'@types/nprogress':
|
||||
specifier: ^0.2.0
|
||||
version: 0.2.3
|
||||
'@types/sortablejs':
|
||||
specifier: ^1.15.0
|
||||
version: 1.15.7
|
||||
'@vue/reactivity':
|
||||
specifier: ^3.3.9
|
||||
version: 3.3.9
|
||||
|
@ -103,6 +94,15 @@ devDependencies:
|
|||
'@antfu/eslint-config-vue':
|
||||
specifier: ^0.43.1
|
||||
version: 0.43.1(@typescript-eslint/eslint-plugin@6.13.0)(@typescript-eslint/parser@6.13.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.54.0)(typescript@5.3.2)
|
||||
'@types/lodash':
|
||||
specifier: ^4.14.202
|
||||
version: 4.14.202
|
||||
'@types/nprogress':
|
||||
specifier: ^0.2.0
|
||||
version: 0.2.3
|
||||
'@types/sortablejs':
|
||||
specifier: ^1.15.0
|
||||
version: 1.15.7
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: ^6.13.0
|
||||
version: 6.13.0(@typescript-eslint/parser@6.13.0)(eslint@8.54.0)(typescript@5.3.2)
|
||||
|
@ -111,13 +111,16 @@ devDependencies:
|
|||
version: 6.13.0(eslint@8.54.0)(typescript@5.3.2)
|
||||
'@vitejs/plugin-vue':
|
||||
specifier: ^4.5.0
|
||||
version: 4.5.0(vite@5.0.2)(vue@3.3.9)
|
||||
version: 4.5.0(vite@5.0.3)(vue@3.3.9)
|
||||
'@vitejs/plugin-vue-jsx':
|
||||
specifier: ^3.1.0
|
||||
version: 3.1.0(vite@5.0.2)(vue@3.3.9)
|
||||
version: 3.1.0(vite@5.0.3)(vue@3.3.9)
|
||||
'@vue/compiler-sfc':
|
||||
specifier: ^3.3.9
|
||||
version: 3.3.9
|
||||
'@vue/tsconfig':
|
||||
specifier: ^0.4.0
|
||||
version: 0.4.0
|
||||
ace-builds:
|
||||
specifier: ^1.31.2
|
||||
version: 1.31.2
|
||||
|
@ -167,11 +170,11 @@ devDependencies:
|
|||
specifier: ^1.4.0
|
||||
version: 1.4.0(vue@3.3.9)
|
||||
vite:
|
||||
specifier: ^5.0.2
|
||||
version: 5.0.2(less@4.2.0)
|
||||
specifier: ^5.0.3
|
||||
version: 5.0.3(less@4.2.0)
|
||||
vite-plugin-html:
|
||||
specifier: ^3.2.0
|
||||
version: 3.2.0(vite@5.0.2)
|
||||
version: 3.2.0(vite@5.0.3)
|
||||
vite-svg-loader:
|
||||
specifier: ^5.1.0
|
||||
version: 5.1.0(vue@3.3.9)
|
||||
|
@ -1297,7 +1300,7 @@ packages:
|
|||
|
||||
/@types/lodash@4.14.202:
|
||||
resolution: {integrity: sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==}
|
||||
dev: false
|
||||
dev: true
|
||||
|
||||
/@types/mdast@3.0.15:
|
||||
resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==}
|
||||
|
@ -1327,7 +1330,7 @@ packages:
|
|||
|
||||
/@types/nprogress@0.2.3:
|
||||
resolution: {integrity: sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA==}
|
||||
dev: false
|
||||
dev: true
|
||||
|
||||
/@types/parse-json@4.0.2:
|
||||
resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
|
||||
|
@ -1343,7 +1346,7 @@ packages:
|
|||
|
||||
/@types/sortablejs@1.15.7:
|
||||
resolution: {integrity: sha512-PvgWCx1Lbgm88FdQ6S7OGvLIjWS66mudKPlfdrWil0TjsO5zmoZmzoKiiwRShs1dwPgrlkr0N4ewuy0/+QUXYQ==}
|
||||
dev: false
|
||||
dev: true
|
||||
|
||||
/@types/unist@2.0.10:
|
||||
resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==}
|
||||
|
@ -1549,7 +1552,7 @@ packages:
|
|||
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
|
||||
dev: true
|
||||
|
||||
/@vitejs/plugin-vue-jsx@3.1.0(vite@5.0.2)(vue@3.3.9):
|
||||
/@vitejs/plugin-vue-jsx@3.1.0(vite@5.0.3)(vue@3.3.9):
|
||||
resolution: {integrity: sha512-w9M6F3LSEU5kszVb9An2/MmXNxocAnUb3WhRr8bHlimhDrXNt6n6D2nJQR3UXpGlZHh/EsgouOHCsM8V3Ln+WA==}
|
||||
engines: {node: ^14.18.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
|
@ -1559,20 +1562,20 @@ packages:
|
|||
'@babel/core': 7.23.3
|
||||
'@babel/plugin-transform-typescript': 7.23.4(@babel/core@7.23.3)
|
||||
'@vue/babel-plugin-jsx': 1.1.5(@babel/core@7.23.3)
|
||||
vite: 5.0.2(less@4.2.0)
|
||||
vite: 5.0.3(less@4.2.0)
|
||||
vue: 3.3.9(typescript@5.3.2)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@vitejs/plugin-vue@4.5.0(vite@5.0.2)(vue@3.3.9):
|
||||
/@vitejs/plugin-vue@4.5.0(vite@5.0.3)(vue@3.3.9):
|
||||
resolution: {integrity: sha512-a2WSpP8X8HTEww/U00bU4mX1QpLINNuz/2KMNpLsdu3BzOpak3AGI1CJYBTXcc4SPhaD0eNRUp7IyQK405L5dQ==}
|
||||
engines: {node: ^14.18.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
vite: ^4.0.0 || ^5.0.0
|
||||
vue: ^3.2.25
|
||||
dependencies:
|
||||
vite: 5.0.2(less@4.2.0)
|
||||
vite: 5.0.3(less@4.2.0)
|
||||
vue: 3.3.9(typescript@5.3.2)
|
||||
dev: true
|
||||
|
||||
|
@ -1734,6 +1737,10 @@ packages:
|
|||
/@vue/shared@3.3.9:
|
||||
resolution: {integrity: sha512-ZE0VTIR0LmYgeyhurPTpy4KzKsuDyQbMSdM49eKkMnT5X4VfFBLysMzjIZhLEFQYjjOVVfbvUDHckwjDFiO2eA==}
|
||||
|
||||
/@vue/tsconfig@0.4.0:
|
||||
resolution: {integrity: sha512-CPuIReonid9+zOG/CGTT05FXrPYATEqoDGNrEaqS4hwcw5BUNM2FguC0mOwJD4Jr16UpRVl9N0pY3P+srIbqmg==}
|
||||
dev: true
|
||||
|
||||
/@vueuse/core@10.6.1(vue@3.3.9):
|
||||
resolution: {integrity: sha512-Pc26IJbqgC9VG1u6VY/xrXXfxD33hnvxBnKrLlA2LJlyHII+BSrRoTPJgGYq7qZOu61itITFUnm6QbacwZ4H8Q==}
|
||||
dependencies:
|
||||
|
@ -5360,7 +5367,7 @@ packages:
|
|||
- terser
|
||||
dev: false
|
||||
|
||||
/vite-plugin-html@3.2.0(vite@5.0.2):
|
||||
/vite-plugin-html@3.2.0(vite@5.0.3):
|
||||
resolution: {integrity: sha512-2VLCeDiHmV/BqqNn5h2V+4280KRgQzCFN47cst3WiNK848klESPQnzuC3okH5XHtgwHH/6s1Ho/YV6yIO0pgoQ==}
|
||||
peerDependencies:
|
||||
vite: '>=2.0.0'
|
||||
|
@ -5377,7 +5384,7 @@ packages:
|
|||
html-minifier-terser: 6.1.0
|
||||
node-html-parser: 5.4.2
|
||||
pathe: 0.2.0
|
||||
vite: 5.0.2(less@4.2.0)
|
||||
vite: 5.0.3(less@4.2.0)
|
||||
dev: true
|
||||
|
||||
/vite-svg-loader@5.1.0(vue@3.3.9):
|
||||
|
@ -5426,8 +5433,8 @@ packages:
|
|||
fsevents: 2.3.3
|
||||
dev: false
|
||||
|
||||
/vite@5.0.2(less@4.2.0):
|
||||
resolution: {integrity: sha512-6CCq1CAJCNM1ya2ZZA7+jS2KgnhbzvxakmlIjN24cF/PXhRMzpM/z8QgsVJA/Dm5fWUWnVEsmtBoMhmerPxT0g==}
|
||||
/vite@5.0.3(less@4.2.0):
|
||||
resolution: {integrity: sha512-WgEq8WEKpZ8c0DL4M1+E+kBZEJyjBmGVrul6z8Ljfhv+PPbNF4aGq014DwNYxGz2FGq6NKL0N8usdiESWd2l2w==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
|
|
|
@ -26,6 +26,7 @@ class Curd<T> {
|
|||
get = this._get.bind(this)
|
||||
save = this._save.bind(this)
|
||||
destroy = this._destroy.bind(this)
|
||||
update_order = this._update_order.bind(this)
|
||||
|
||||
constructor(baseUrl: string, plural: string | null = null) {
|
||||
this.baseUrl = baseUrl
|
||||
|
@ -51,6 +52,14 @@ class Curd<T> {
|
|||
_destroy(id: any = null) {
|
||||
return http.delete(`${this.baseUrl}/${id}`)
|
||||
}
|
||||
|
||||
_update_order(data: {
|
||||
target_id: number
|
||||
direction: number
|
||||
affected_ids: number[]
|
||||
}) {
|
||||
return http.post(`${this.plural}/order`, data)
|
||||
}
|
||||
}
|
||||
|
||||
export default Curd
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
import type { ModelBase } from '@/api/curd'
|
||||
import Curd from '@/api/curd'
|
||||
|
||||
const environment = new Curd('/environment')
|
||||
export interface Environment extends ModelBase {
|
||||
name: string
|
||||
url: string
|
||||
token: string
|
||||
}
|
||||
|
||||
const environment: Curd<Environment> = new Curd('/environment')
|
||||
|
||||
export default environment
|
||||
|
|
|
@ -7,7 +7,7 @@ export interface ChatComplicationMessage {
|
|||
}
|
||||
|
||||
const openai = {
|
||||
store_record(data: { file_name: string; messages: ChatComplicationMessage[] }) {
|
||||
store_record(data: { file_name?: string; messages: ChatComplicationMessage[] }) {
|
||||
return http.post('/chat_gpt_record', data)
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
import type { ModelBase } from '@/api/curd'
|
||||
import Curd from '@/api/curd'
|
||||
|
||||
const user: Curd = new Curd('user')
|
||||
export interface User extends ModelBase {
|
||||
name: string
|
||||
password: string
|
||||
}
|
||||
|
||||
const user: Curd<User> = new Curd('user')
|
||||
|
||||
export default user
|
||||
|
|
|
@ -17,7 +17,7 @@ const breadList = computed(() => {
|
|||
route.matched.forEach(item => {
|
||||
// item.name !== 'index' && this.breadList.push(item)
|
||||
_breadList.push({
|
||||
name: item.name as () => string,
|
||||
name: item.name as never as () => string,
|
||||
path: item.path,
|
||||
})
|
||||
})
|
||||
|
|
|
@ -5,7 +5,7 @@ import type { Ref } from 'vue'
|
|||
import { useSettingsStore } from '@/pinia'
|
||||
import type { Series } from '@/components/Chart/types'
|
||||
|
||||
const { series, max, y_formatter } = defineProps<{
|
||||
const { series, max, yFormatter } = defineProps<{
|
||||
series: Series[]
|
||||
max?: number
|
||||
yFormatter?: (value: number) => string
|
||||
|
@ -69,7 +69,7 @@ let chartOptions = {
|
|||
style: {
|
||||
colors: fontColor(),
|
||||
},
|
||||
formatter: y_formatter,
|
||||
formatter: yFormatter,
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
|
@ -106,7 +106,7 @@ const callback = () => {
|
|||
style: {
|
||||
colors: fontColor(),
|
||||
},
|
||||
formatter: y_formatter,
|
||||
formatter: yFormatter,
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
|
|
|
@ -295,7 +295,7 @@ const show = computed(() => !messages.value || messages.value?.length === 0)
|
|||
v-model:value="ask_buffer"
|
||||
auto-size
|
||||
/>
|
||||
<div class="sned-btn">
|
||||
<div class="send-btn">
|
||||
<AButton
|
||||
size="small"
|
||||
type="text"
|
||||
|
@ -356,7 +356,7 @@ const show = computed(() => !messages.value || messages.value?.length === 0)
|
|||
justify-content: center;
|
||||
}
|
||||
|
||||
.sned-btn {
|
||||
.send-btn {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 3px;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
import environment from '@/api/environment'
|
||||
|
||||
|
|
|
@ -1,82 +1,45 @@
|
|||
<script setup lang="ts">
|
||||
import { provide, reactive, ref } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import type { ComputedRef } from 'vue'
|
||||
import type { StdTableProps } from './StdTable.vue'
|
||||
import StdTable from './StdTable.vue'
|
||||
import gettext from '@/gettext'
|
||||
|
||||
import StdDataEntry from '@/components/StdDesign/StdDataEntry'
|
||||
import type { Column } from '@/components/StdDesign/types'
|
||||
|
||||
const props = defineProps({
|
||||
api: Object,
|
||||
columns: Array,
|
||||
title: String,
|
||||
data_key: {
|
||||
type: String,
|
||||
default: 'data',
|
||||
},
|
||||
disable_search: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
disable_add: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
soft_delete: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
edit_text: String,
|
||||
deletable: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
get_params: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
},
|
||||
},
|
||||
editable: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
beforeSave: {
|
||||
type: Function,
|
||||
default: () => {
|
||||
},
|
||||
},
|
||||
exportCsv: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
modalWidth: {
|
||||
type: Number,
|
||||
default: 600,
|
||||
},
|
||||
useSortable: Boolean,
|
||||
})
|
||||
export interface StdCurdProps {
|
||||
cardTitleKey?: string
|
||||
modalMaxWidth?: string | number
|
||||
disableAdd?: boolean
|
||||
onClickAdd?: () => void
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
onClickEdit?: (id: number | string, record: any, index: number) => void
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
beforeSave?: (data: any) => void
|
||||
}
|
||||
|
||||
const props = defineProps<StdTableProps & StdCurdProps>()
|
||||
|
||||
const { $gettext } = gettext
|
||||
|
||||
const visible = ref(false)
|
||||
const update = ref(0)
|
||||
const data: any = reactive({ id: null })
|
||||
const data = reactive({ id: null })
|
||||
|
||||
provide('data', data)
|
||||
|
||||
const error: any = reactive({})
|
||||
const error = reactive({})
|
||||
const selected = ref([])
|
||||
|
||||
function onSelect(keys: any) {
|
||||
function onSelect(keys) {
|
||||
selected.value = keys
|
||||
}
|
||||
|
||||
function editableColumns() {
|
||||
return props.columns!.filter((c: any) => {
|
||||
const editableColumns = computed(() => {
|
||||
return props.columns!.filter(c => {
|
||||
return c.edit
|
||||
})
|
||||
}
|
||||
}) as ComputedRef<Column[]>
|
||||
|
||||
function add() {
|
||||
Object.keys(data).forEach(v => {
|
||||
|
@ -86,11 +49,9 @@ function add() {
|
|||
clear_error()
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
const table = ref()
|
||||
function get_list() {
|
||||
const t: Table = table.value!
|
||||
|
||||
t!.get_list()
|
||||
table.value?.get_list()
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
|
@ -99,12 +60,6 @@ defineExpose({
|
|||
data,
|
||||
})
|
||||
|
||||
const table = ref(null)
|
||||
|
||||
interface Table {
|
||||
get_list(): void
|
||||
}
|
||||
|
||||
function clear_error() {
|
||||
Object.keys(error).forEach(v => {
|
||||
delete error[v]
|
||||
|
@ -114,12 +69,12 @@ function clear_error() {
|
|||
const ok = async () => {
|
||||
clear_error()
|
||||
await props?.beforeSave?.(data)
|
||||
props.api!.save(data.id, data).then((r: any) => {
|
||||
props.api!.save(data.id, data).then(r => {
|
||||
message.success($gettext('Save Successfully'))
|
||||
Object.assign(data, r)
|
||||
get_list()
|
||||
visible.value = false
|
||||
}).catch((e: any) => {
|
||||
}).catch(e => {
|
||||
message.error($gettext(e?.message ?? 'Server error'), 5)
|
||||
Object.assign(error, e.errors)
|
||||
})
|
||||
|
@ -131,15 +86,15 @@ function cancel() {
|
|||
clear_error()
|
||||
}
|
||||
|
||||
function edit(id: any) {
|
||||
props.api!.get(id).then(async (r: any) => {
|
||||
function edit(id) {
|
||||
props.api!.get(id).then(async r => {
|
||||
Object.keys(data).forEach(k => {
|
||||
delete data[k]
|
||||
})
|
||||
data.id = null
|
||||
Object.assign(data, r)
|
||||
visible.value = true
|
||||
}).catch((e: any) => {
|
||||
}).catch(e => {
|
||||
message.error($gettext(e?.message ?? 'Server error'), 5)
|
||||
})
|
||||
}
|
||||
|
@ -151,7 +106,7 @@ const selectedRowKeys = ref([])
|
|||
<div class="std-curd">
|
||||
<ACard :title="title || $gettext('Table')">
|
||||
<template
|
||||
v-if="!disable_add"
|
||||
v-if="!disableAdd"
|
||||
#extra
|
||||
>
|
||||
<a @click="add">{{ $gettext('Add') }}</a>
|
||||
|
@ -162,7 +117,7 @@ const selectedRowKeys = ref([])
|
|||
v-bind="props"
|
||||
:key="update"
|
||||
v-model:selected-row-keys="selectedRowKeys"
|
||||
@clickEdit="edit"
|
||||
@click-edit="edit"
|
||||
@selected="onSelect"
|
||||
>
|
||||
<template #actions="slotProps">
|
||||
|
@ -177,11 +132,11 @@ const selectedRowKeys = ref([])
|
|||
<AModal
|
||||
class="std-curd-edit-modal"
|
||||
:mask="false"
|
||||
:title="edit_text ? edit_text : (data.id ? $gettext('Modify') : $gettext('Add'))"
|
||||
:title="data.id ? $gettext('Modify') : $gettext('Add')"
|
||||
:open="visible"
|
||||
:cancel-text="$gettext('Cancel')"
|
||||
:ok-text="$gettext('OK')"
|
||||
:width="modalWidth"
|
||||
:width="modalMaxWidth"
|
||||
destroy-on-close
|
||||
@cancel="cancel"
|
||||
@ok="ok"
|
||||
|
@ -197,8 +152,7 @@ const selectedRowKeys = ref([])
|
|||
</div>
|
||||
|
||||
<StdDataEntry
|
||||
ref="std_data_entry"
|
||||
:data-list="editableColumns()"
|
||||
:data-list="editableColumns"
|
||||
:data-source="data"
|
||||
:error="error"
|
||||
/>
|
||||
|
|
|
@ -4,7 +4,7 @@ import type { Pagination } from '@/api/curd'
|
|||
|
||||
const props = defineProps<{
|
||||
pagination: Pagination
|
||||
size?: 'small' | 'default'
|
||||
size?: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['change', 'changePageSize', 'update:pagination'])
|
||||
|
|
|
@ -1,103 +1,71 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref, toRaw, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { message } from 'ant-design-vue'
|
||||
import dayjs from 'dayjs'
|
||||
import Sortable from 'sortablejs'
|
||||
import { HolderOutlined } from '@ant-design/icons-vue'
|
||||
import _ from 'lodash'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
import type { ComputedRef } from 'vue'
|
||||
import type { SorterResult } from 'ant-design-vue/lib/table/interface'
|
||||
import StdPagination from './StdPagination.vue'
|
||||
import { downloadCsv } from '@/lib/helper'
|
||||
import StdDataEntry from '@/components/StdDesign/StdDataEntry'
|
||||
import gettext from '@/gettext'
|
||||
import type { Pagination } from '@/api/curd'
|
||||
import type { Column } from '@/components/StdDesign/types'
|
||||
import exportCsvHandler from '@/components/StdDesign/StdDataDisplay/methods/exportCsv'
|
||||
import useSortable from '@/components/StdDesign/StdDataDisplay/methods/sortable'
|
||||
import type Curd from '@/api/curd'
|
||||
|
||||
const props = defineProps({
|
||||
api: Object,
|
||||
columns: Array,
|
||||
data_key: {
|
||||
type: String,
|
||||
default: 'data',
|
||||
},
|
||||
disable_search: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
disable_query_params: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
disable_add: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
edit_text: String,
|
||||
deletable: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
get_params: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
},
|
||||
},
|
||||
editable: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
selectionType: {
|
||||
type: String,
|
||||
validator(value: string) {
|
||||
return ['checkbox', 'radio'].includes(value)
|
||||
},
|
||||
},
|
||||
pithy: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
scrollX: {
|
||||
type: [Number, Boolean],
|
||||
default: true,
|
||||
},
|
||||
rowKey: {
|
||||
type: String,
|
||||
default: 'id',
|
||||
},
|
||||
exportCsv: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
size: String,
|
||||
selectedRowKeys: {
|
||||
type: Array,
|
||||
},
|
||||
useSortable: Boolean,
|
||||
export interface StdTableProps {
|
||||
title?: string
|
||||
mode?: string
|
||||
rowKey?: string
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
api: Curd<any>
|
||||
columns: Column[]
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
getParams?: Record<string, any>
|
||||
size?: string
|
||||
disableQueryParams?: boolean
|
||||
disableSearch?: boolean
|
||||
pithy?: boolean
|
||||
exportCsv?: boolean
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
overwriteParams?: Record<string, any>
|
||||
disabledModify?: boolean
|
||||
selectionType?: string
|
||||
sortable?: boolean
|
||||
disableDelete?: boolean
|
||||
disablePagination?: boolean
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
selectedRowKeys?: any | any[]
|
||||
sortableMoveHook?: (oldRow: number[], newRow: number[]) => boolean
|
||||
scrollX?: string | number
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<StdTableProps>(), {
|
||||
rowKey: 'id',
|
||||
})
|
||||
|
||||
const emit = defineEmits(['onSelected', 'onSelectedRecord', 'clickEdit', 'update:selectedRowKeys', 'clickBatchModify'])
|
||||
|
||||
const { $gettext, interpolate } = gettext
|
||||
|
||||
const data_source: any = ref([])
|
||||
const expand_keys_list: any = ref([])
|
||||
const rows_key_index_map: any = ref({})
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
const route = useRoute()
|
||||
const dataSource = ref([])
|
||||
const expandKeysList = ref([])
|
||||
const rowsKeyIndexMap = ref({})
|
||||
const loading = ref(true)
|
||||
|
||||
const pagination = reactive({
|
||||
// This can be useful if there are more than one StdTable in the same page.
|
||||
const randomId = ref(Math.random().toString(36).substring(2, 8))
|
||||
|
||||
const pagination: Pagination = reactive({
|
||||
total: 1,
|
||||
per_page: 10,
|
||||
current_page: 1,
|
||||
total_pages: 1,
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
const params = reactive({
|
||||
...props.get_params,
|
||||
...props.getParams,
|
||||
})
|
||||
|
||||
const selectedKeysLocalBuffer: any = ref([])
|
||||
const selectedKeysLocalBuffer = ref([])
|
||||
|
||||
const selectedRowKeysBuffer = computed({
|
||||
get() {
|
||||
|
@ -109,17 +77,47 @@ const selectedRowKeysBuffer = computed({
|
|||
},
|
||||
})
|
||||
|
||||
const searchColumns = getSearchColumns()
|
||||
const pithyColumns = getPithyColumns()
|
||||
const batchColumns = getBatchEditColumns()
|
||||
const searchColumns = computed(() => {
|
||||
const _searchColumns = []
|
||||
|
||||
props.columns?.forEach(column => {
|
||||
if (column.search)
|
||||
_searchColumns.push(column)
|
||||
})
|
||||
|
||||
return _searchColumns
|
||||
})
|
||||
|
||||
const pithyColumns = computed(() => {
|
||||
if (props.pithy) {
|
||||
return props.columns?.filter(c => {
|
||||
return c.pithy === true && !c.hidden
|
||||
})
|
||||
}
|
||||
|
||||
return props.columns?.filter(c => {
|
||||
return !c.hidden
|
||||
})
|
||||
}) as ComputedRef<Column[]>
|
||||
|
||||
const batchColumns = computed(() => {
|
||||
const batch = []
|
||||
|
||||
props.columns?.forEach(column => {
|
||||
if (column.batch)
|
||||
batch.push(column)
|
||||
})
|
||||
|
||||
return batch
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
if (!props.disable_query_params)
|
||||
if (!props.disableQueryParams)
|
||||
Object.assign(params, route.query)
|
||||
|
||||
get_list()
|
||||
|
||||
if (props.useSortable)
|
||||
if (props.sortable)
|
||||
initSortable()
|
||||
})
|
||||
|
||||
|
@ -127,11 +125,11 @@ defineExpose({
|
|||
get_list,
|
||||
})
|
||||
|
||||
function destroy(id: any) {
|
||||
function destroy(id) {
|
||||
props.api!.destroy(id).then(() => {
|
||||
get_list()
|
||||
message.success(interpolate($gettext('Delete ID: %{id}'), { id }))
|
||||
}).catch((e: any) => {
|
||||
message.success($gettext('Deleted successfully'))
|
||||
}).catch(e => {
|
||||
message.error($gettext(e?.message ?? 'Server error'))
|
||||
})
|
||||
}
|
||||
|
@ -142,37 +140,35 @@ function get_list(page_num = null, page_size = 20) {
|
|||
params.page = page_num
|
||||
params.page_size = page_size
|
||||
}
|
||||
props.api!.get_list(params).then(async (r: any) => {
|
||||
data_source.value = r.data
|
||||
rows_key_index_map.value = {}
|
||||
if (props.useSortable) {
|
||||
function buildIndexMap(data: any, level: number = 0, index: number = 0, total: number[] = []) {
|
||||
if (data && data.length > 0) {
|
||||
data.forEach((v: any) => {
|
||||
v.level = level
|
||||
|
||||
const current_index = [...total, index++]
|
||||
|
||||
rows_key_index_map.value[v.id] = current_index
|
||||
if (v.children)
|
||||
buildIndexMap(v.children, level + 1, 0, current_index)
|
||||
})
|
||||
}
|
||||
}
|
||||
props.api?.get_list(params).then(async r => {
|
||||
dataSource.value = r.data
|
||||
rowsKeyIndexMap.value = {}
|
||||
if (props.sortable)
|
||||
|
||||
buildIndexMap(r.data)
|
||||
}
|
||||
|
||||
if (r.pagination !== undefined)
|
||||
if (r.pagination)
|
||||
Object.assign(pagination, r.pagination)
|
||||
|
||||
loading.value = false
|
||||
}).catch((e: any) => {
|
||||
}).catch(e => {
|
||||
message.error(e?.message ?? $gettext('Server error'))
|
||||
})
|
||||
}
|
||||
function buildIndexMap(data, level: number = 0, index: number = 0, total: number[] = []) {
|
||||
if (data && data.length > 0) {
|
||||
data.forEach(v => {
|
||||
v.level = level
|
||||
|
||||
function stdChange(pagination: any, filters: any, sorter: any) {
|
||||
const current_indexes = [...total, index++]
|
||||
|
||||
rowsKeyIndexMap.value[v.id] = current_indexes
|
||||
if (v.children)
|
||||
buildIndexMap(v.children, level + 1, 0, current_indexes)
|
||||
})
|
||||
}
|
||||
}
|
||||
function orderPaginationChange(_pagination: Pagination, filters, sorter: SorterResult) {
|
||||
if (sorter) {
|
||||
selectedRowKeysBuffer.value = []
|
||||
params.order_by = sorter.field
|
||||
|
@ -189,67 +185,29 @@ function stdChange(pagination: any, filters: any, sorter: any) {
|
|||
break
|
||||
}
|
||||
}
|
||||
if (pagination)
|
||||
if (_pagination)
|
||||
selectedRowKeysBuffer.value = []
|
||||
}
|
||||
|
||||
function expandedTable(keys: any) {
|
||||
expand_keys_list.value = keys
|
||||
function expandedTable(keys) {
|
||||
expandKeysList.value = keys
|
||||
}
|
||||
|
||||
function getSearchColumns() {
|
||||
const searchColumns: any = []
|
||||
const crossPageSelect = {}
|
||||
|
||||
props.columns!.forEach((column: any) => {
|
||||
if (column.search)
|
||||
searchColumns.push(column)
|
||||
})
|
||||
|
||||
return searchColumns
|
||||
}
|
||||
|
||||
function getBatchEditColumns() {
|
||||
const batch: any = []
|
||||
|
||||
props.columns!.forEach((column: any) => {
|
||||
if (column.batch)
|
||||
batch.push(column)
|
||||
})
|
||||
|
||||
return batch
|
||||
}
|
||||
|
||||
function getPithyColumns() {
|
||||
if (props.pithy) {
|
||||
return props.columns!.filter((c: any, index: any, columns: any) => {
|
||||
return c.pithy === true && c.display !== false
|
||||
})
|
||||
}
|
||||
|
||||
return props.columns!.filter((c: any, index: any, columns: any) => {
|
||||
return c.display !== false
|
||||
})
|
||||
}
|
||||
|
||||
function checked(c: any) {
|
||||
params[c.target.value] = c.target.checked
|
||||
}
|
||||
|
||||
const crossPageSelect: any = {}
|
||||
|
||||
async function onSelectChange(_selectedRowKeys: any) {
|
||||
async function onSelectChange(_selectedRowKeys) {
|
||||
const page = params.page || 1
|
||||
|
||||
crossPageSelect[page] = await _selectedRowKeys
|
||||
|
||||
let t: any = []
|
||||
let t = []
|
||||
Object.keys(crossPageSelect).forEach(v => {
|
||||
t.push(...crossPageSelect[v])
|
||||
})
|
||||
|
||||
const n: any = [..._selectedRowKeys]
|
||||
const n = [..._selectedRowKeys]
|
||||
|
||||
t = await t.concat(n)
|
||||
t = t.concat(n)
|
||||
|
||||
// console.log(crossPageSelect)
|
||||
const set = new Set(t)
|
||||
|
@ -258,7 +216,7 @@ async function onSelectChange(_selectedRowKeys: any) {
|
|||
emit('onSelected', selectedRowKeysBuffer.value)
|
||||
}
|
||||
|
||||
function onSelect(record: any) {
|
||||
function onSelect(record) {
|
||||
emit('onSelectedRecord', record)
|
||||
}
|
||||
|
||||
|
@ -270,7 +228,7 @@ const reset_search = async () => {
|
|||
})
|
||||
|
||||
Object.assign(params, {
|
||||
...props.get_params,
|
||||
...props.getParams,
|
||||
})
|
||||
|
||||
router.push({ query: {} }).catch(() => {
|
||||
|
@ -278,19 +236,19 @@ const reset_search = async () => {
|
|||
}
|
||||
|
||||
watch(params, () => {
|
||||
if (!props.disable_query_params)
|
||||
if (!props.disableQueryParams)
|
||||
router.push({ query: params })
|
||||
|
||||
get_list()
|
||||
})
|
||||
|
||||
const rowSelection = computed(() => {
|
||||
if (batchColumns.length > 0 || props.selectionType) {
|
||||
if (batchColumns.value.length > 0 || props.selectionType) {
|
||||
return {
|
||||
selectedRowKeys: selectedRowKeysBuffer.value,
|
||||
onChange: onSelectChange,
|
||||
onSelect,
|
||||
type: batchColumns.length > 0 ? 'checkbox' : props.selectionType,
|
||||
type: batchColumns.value.length > 0 ? 'checkbox' : props.selectionType,
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -298,188 +256,27 @@ const rowSelection = computed(() => {
|
|||
}
|
||||
})
|
||||
|
||||
const fn = _.get
|
||||
|
||||
async function export_csv() {
|
||||
const header = []
|
||||
const headerKeys: any[] = []
|
||||
const showColumnsMap: any = {}
|
||||
|
||||
for (const showColumnsKey in pithyColumns) {
|
||||
if (pithyColumns[showColumnsKey].dataIndex === 'action')
|
||||
continue
|
||||
|
||||
let t = pithyColumns[showColumnsKey].title
|
||||
|
||||
if (typeof t === 'function')
|
||||
t = t()
|
||||
|
||||
header.push({
|
||||
title: t,
|
||||
|
||||
key: pithyColumns[showColumnsKey].dataIndex,
|
||||
})
|
||||
|
||||
headerKeys.push(pithyColumns[showColumnsKey].dataIndex)
|
||||
|
||||
showColumnsMap[pithyColumns[showColumnsKey].dataIndex] = pithyColumns[showColumnsKey]
|
||||
}
|
||||
|
||||
let dataSource: any = []
|
||||
let hasMore = true
|
||||
let page = 1
|
||||
while (hasMore) {
|
||||
// 准备 DataSource
|
||||
await props.api!.get_list({ page }).then((response: any) => {
|
||||
if (response.data.length === 0) {
|
||||
hasMore = false
|
||||
|
||||
return
|
||||
}
|
||||
if (response[props.data_key] === undefined)
|
||||
dataSource = dataSource.concat(...response.data)
|
||||
else
|
||||
dataSource = dataSource.concat(...response[props.data_key])
|
||||
}).catch((e: any) => {
|
||||
message.error(e.message ?? $gettext('Server error'))
|
||||
hasMore = false
|
||||
})
|
||||
page += 1
|
||||
}
|
||||
const data: any[] = []
|
||||
|
||||
dataSource.forEach((row: Object) => {
|
||||
const obj: any = {}
|
||||
|
||||
headerKeys.forEach(key => {
|
||||
let data = fn(row, key)
|
||||
const c = showColumnsMap[key]
|
||||
|
||||
data = c?.customRender?.({ text: data }) ?? data
|
||||
obj[c.dataIndex] = data
|
||||
})
|
||||
data.push(obj)
|
||||
})
|
||||
|
||||
downloadCsv(header, data,
|
||||
`${$gettext('Export')}-${dayjs().format('YYYYMMDDHHmmss')}.csv`)
|
||||
}
|
||||
|
||||
const hasSelectedRow = computed(() => {
|
||||
return batchColumns.length > 0 && selectedRowKeysBuffer.value.length > 0
|
||||
return batchColumns.value.length > 0 && selectedRowKeysBuffer.value.length > 0
|
||||
})
|
||||
|
||||
function click_batch_edit() {
|
||||
emit('clickBatchModify', batchColumns, selectedRowKeysBuffer.value)
|
||||
}
|
||||
|
||||
function getLeastIndex(index: number) {
|
||||
return index >= 1 ? index : 1
|
||||
}
|
||||
|
||||
function getTargetData(data: any, indexList: number[]): any {
|
||||
let target: any = { children: data }
|
||||
indexList.forEach((index: number) => {
|
||||
target.children[index].parent = target
|
||||
target = target.children[index]
|
||||
})
|
||||
|
||||
return target
|
||||
function clickBatchEdit() {
|
||||
emit('clickBatchModify', batchColumns.value, selectedRowKeysBuffer.value)
|
||||
}
|
||||
|
||||
function initSortable() {
|
||||
const table: any = document.querySelector('#std-table tbody')
|
||||
|
||||
new Sortable(table, {
|
||||
handle: '.ant-table-drag-icon',
|
||||
animation: 150,
|
||||
sort: true,
|
||||
forceFallback: true,
|
||||
setData(dataTransfer) {
|
||||
dataTransfer.setData('Text', '')
|
||||
},
|
||||
onStart({ item }) {
|
||||
const targetRowKey = Number(item.dataset.rowKey)
|
||||
if (targetRowKey)
|
||||
expand_keys_list.value = expand_keys_list.value.filter((item: number) => item !== targetRowKey)
|
||||
},
|
||||
onMove({ dragged, related }) {
|
||||
const oldRow: number[] = rows_key_index_map.value?.[Number(dragged.dataset.rowKey)]
|
||||
const newRow: number[] = rows_key_index_map.value?.[Number(related.dataset.rowKey)]
|
||||
if (oldRow.length !== newRow.length || oldRow[oldRow.length - 2] != newRow[newRow.length - 2])
|
||||
return false
|
||||
},
|
||||
async onEnd({ item, newIndex, oldIndex }) {
|
||||
if (newIndex === oldIndex)
|
||||
return
|
||||
|
||||
const indexDelta: number = Number(oldIndex) - Number(newIndex)
|
||||
const direction: number = indexDelta > 0 ? +1 : -1
|
||||
|
||||
const rowIndex: number[] = rows_key_index_map.value?.[Number(item.dataset.rowKey)]
|
||||
const newRow = getTargetData(data_source.value, rowIndex)
|
||||
const newRowParent = newRow.parent
|
||||
const level: number = newRow.level
|
||||
|
||||
const currentRowIndex: number[] = [...rows_key_index_map.value?.
|
||||
[Number(table.children[Number(newIndex) + direction].dataset.rowKey)]]
|
||||
|
||||
const currentRow: any = getTargetData(data_source.value, currentRowIndex)
|
||||
|
||||
// Reset parent
|
||||
currentRow.parent = newRow.parent = null
|
||||
newRowParent.children.splice(rowIndex[level], 1)
|
||||
newRowParent.children.splice(currentRowIndex[level], 0, toRaw(newRow))
|
||||
|
||||
const changeIds: number[] = []
|
||||
|
||||
function processChanges(row: any, children: boolean = false, newIndex: number | undefined = undefined) {
|
||||
// Build changes ID list expect new row
|
||||
if (children || newIndex === undefined)
|
||||
changeIds.push(row.id)
|
||||
|
||||
if (newIndex !== undefined)
|
||||
rows_key_index_map.value[row.id][level] = newIndex
|
||||
else if (children)
|
||||
rows_key_index_map.value[row.id][level] += direction
|
||||
|
||||
row.parent = null
|
||||
if (row.children)
|
||||
row.children.forEach((v: any) => processChanges(v, true, newIndex))
|
||||
useSortable(props, randomId, dataSource, rowsKeyIndexMap, expandKeysList)
|
||||
}
|
||||
|
||||
// Replace row index for new row
|
||||
processChanges(newRow, false, currentRowIndex[level])
|
||||
|
||||
// Rebuild row index maps for changes row
|
||||
for (let i = Number(oldIndex); i != newIndex; i -= direction) {
|
||||
const rowIndex: number[] = rows_key_index_map.value?.[table.children[i].dataset.rowKey]
|
||||
|
||||
rowIndex[level] += direction
|
||||
processChanges(getTargetData(data_source.value, rowIndex))
|
||||
function export_csv() {
|
||||
exportCsvHandler(props, pithyColumns)
|
||||
}
|
||||
console.log('Change row id', newRow.id, 'order', newRow.id, '=>', currentRow.id, ', direction: ', direction,
|
||||
', changes IDs:', changeIds)
|
||||
|
||||
props.api!.update_order({
|
||||
target_id: newRow.id,
|
||||
direction,
|
||||
affected_ids: changeIds,
|
||||
}).then(() => {
|
||||
message.success($gettext('Updated successfully'))
|
||||
}).catch((e: any) => {
|
||||
message.error(e?.message ?? $gettext('Server error'))
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="std-table">
|
||||
<StdDataEntry
|
||||
v-if="!disable_search && searchColumns.length"
|
||||
v-if="!disableSearch && searchColumns.length"
|
||||
:data-list="searchColumns"
|
||||
:data-source="params"
|
||||
layout="inline"
|
||||
|
@ -487,7 +284,7 @@ function initSortable() {
|
|||
<template #action>
|
||||
<ASpace class="action-btn">
|
||||
<AButton
|
||||
v-if="exportCsv"
|
||||
v-if="props.exportCsv"
|
||||
type="primary"
|
||||
ghost
|
||||
@click="export_csv"
|
||||
|
@ -499,7 +296,7 @@ function initSortable() {
|
|||
</AButton>
|
||||
<AButton
|
||||
v-if="hasSelectedRow"
|
||||
@click="click_batch_edit"
|
||||
@click="clickBatchEdit"
|
||||
>
|
||||
{{ $gettext('Batch Modify') }}
|
||||
</AButton>
|
||||
|
@ -509,36 +306,36 @@ function initSortable() {
|
|||
<ATable
|
||||
id="std-table"
|
||||
:columns="pithyColumns"
|
||||
:data-source="data_source"
|
||||
:data-source="dataSource"
|
||||
:loading="loading"
|
||||
:pagination="false"
|
||||
:row-key="rowKey"
|
||||
:row-selection="rowSelection"
|
||||
:scroll="{ x: scrollX }"
|
||||
:size="size"
|
||||
:expanded-row-keys="expand_keys_list"
|
||||
@change="stdChange"
|
||||
@expandedRowsChange="expandedTable"
|
||||
:expanded-row-keys="expandKeysList"
|
||||
@change="orderPaginationChange"
|
||||
@expanded-rows-change="expandedTable"
|
||||
>
|
||||
<template #bodyCell="{ text, record, index, column }">
|
||||
<template #bodyCell="{ text, record, column }">
|
||||
<template v-if="column.handle === true">
|
||||
<span class="ant-table-drag-icon"><HolderOutlined /></span>
|
||||
{{ text }}
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'action'">
|
||||
<AButton
|
||||
v-if="props.editable"
|
||||
v-if="!props.disabledModify"
|
||||
type="link"
|
||||
size="small"
|
||||
@click="$emit('clickEdit', record[props.rowKey], record)"
|
||||
>
|
||||
{{ props.edit_text || $gettext('Modify') }}
|
||||
{{ $gettext('Modify') }}
|
||||
</AButton>
|
||||
<slot
|
||||
name="actions"
|
||||
:record="record"
|
||||
/>
|
||||
<template v-if="props.deletable">
|
||||
<template v-if="!props.disableDelete">
|
||||
<ADivider type="vertical" />
|
||||
<APopconfirm
|
||||
:cancel-text="$gettext('No')"
|
||||
|
@ -561,7 +358,7 @@ function initSortable() {
|
|||
:size="size"
|
||||
:pagination="pagination"
|
||||
@change="get_list"
|
||||
@changePageSize="stdChange"
|
||||
@change-page-size="orderPaginationChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import StdTable from './StdTable.vue'
|
||||
import StdCurd from './StdCurd.vue'
|
||||
import StdBatchEdit from './StdBatchEdit.vue'
|
||||
|
||||
export {
|
||||
StdTable,
|
||||
StdCurd,
|
||||
StdBatchEdit,
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
import { message } from 'ant-design-vue'
|
||||
import dayjs from 'dayjs'
|
||||
import type { ComputedRef } from 'vue'
|
||||
import _ from 'lodash'
|
||||
import { downloadCsv } from '@/lib/helper'
|
||||
import type { Column, StdTableResponse } from '@/components/StdDesign/types'
|
||||
import gettext from '@/gettext'
|
||||
import type { StdTableProps } from '@/components/StdDesign/StdDataDisplay/StdTable.vue'
|
||||
|
||||
const { $gettext } = gettext
|
||||
async function exportCsv(props: StdTableProps, pithyColumns: ComputedRef<Column[]>) {
|
||||
const header: { title?: string; key: string }[] = []
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const headerKeys: any[] = []
|
||||
const showColumnsMap: Record<string, Column> = {}
|
||||
|
||||
pithyColumns.value.forEach((column: Column) => {
|
||||
if (column.dataIndex === 'action')
|
||||
return
|
||||
let t = column.title
|
||||
if (typeof t === 'function')
|
||||
t = t()
|
||||
header.push({
|
||||
title: t,
|
||||
key: column.dataIndex,
|
||||
})
|
||||
headerKeys.push(column.dataIndex)
|
||||
showColumnsMap[column.dataIndex] = column
|
||||
})
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const dataSource: any[] = []
|
||||
let hasMore = true
|
||||
let page = 1
|
||||
while (hasMore) {
|
||||
// 准备 DataSource
|
||||
await props.api!.get_list({ page }).then((r: StdTableResponse) => {
|
||||
if (r.data.length === 0) {
|
||||
hasMore = false
|
||||
|
||||
return
|
||||
}
|
||||
dataSource.push(...r.data)
|
||||
}).catch((e: { message?: string }) => {
|
||||
message.error(e.message ?? $gettext('Server error'))
|
||||
hasMore = false
|
||||
})
|
||||
page += 1
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const data: any[] = []
|
||||
|
||||
dataSource.forEach(row => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const obj: Record<string, any> = {}
|
||||
|
||||
headerKeys.forEach(key => {
|
||||
let _data = _.get(row, key)
|
||||
const c = showColumnsMap[key]
|
||||
|
||||
_data = c?.customRender?.({ text: _data }) ?? _data
|
||||
obj[c.dataIndex] = _data
|
||||
})
|
||||
data.push(obj)
|
||||
})
|
||||
|
||||
downloadCsv(header, data,
|
||||
`${$gettext('Export')}-${props.title}-${dayjs().format('YYYYMMDDHHmmss')}.csv`)
|
||||
}
|
||||
|
||||
export default exportCsv
|
132
app/src/components/StdDesign/StdDataDisplay/methods/sortable.ts
Normal file
132
app/src/components/StdDesign/StdDataDisplay/methods/sortable.ts
Normal file
|
@ -0,0 +1,132 @@
|
|||
import { message } from 'ant-design-vue'
|
||||
import SortableJs from 'sortablejs'
|
||||
import type { Ref } from 'vue'
|
||||
import gettext from '@/gettext'
|
||||
import type { StdTableProps } from '@/components/StdDesign/StdDataDisplay/StdTable.vue'
|
||||
|
||||
const { $gettext } = gettext
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function getRowKey(item: any) {
|
||||
return item.children[0].children[0].dataset.rowKey
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function getTargetData(data: any, indexList: number[]): any {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let target: any = { children: data }
|
||||
indexList.forEach((index: number) => {
|
||||
target.children[index].parent = target
|
||||
target = target.children[index]
|
||||
})
|
||||
|
||||
return target
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function useSortable(props: StdTableProps, randomId: Ref<string>, dataSource: Ref<any[]>,
|
||||
rowsKeyIndexMap: Ref<Record<number, number[]>>, expandKeysList: Ref<number[]>) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const table: any = document.querySelector(`#std-table-${randomId.value} tbody`)
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
new SortableJs(table, {
|
||||
handle: '.table-drag-icon',
|
||||
animation: 150,
|
||||
sort: true,
|
||||
forceFallback: true,
|
||||
setData(dataTransfer) {
|
||||
dataTransfer.setData('Text', '')
|
||||
},
|
||||
onStart({ item }) {
|
||||
const targetRowKey = Number(getRowKey(item))
|
||||
if (targetRowKey)
|
||||
expandKeysList.value = expandKeysList.value.filter((_item: number) => _item !== targetRowKey)
|
||||
},
|
||||
onMove({
|
||||
dragged,
|
||||
related,
|
||||
}) {
|
||||
const oldRow: number[] = rowsKeyIndexMap.value?.[Number(getRowKey(dragged))]
|
||||
const newRow: number[] = rowsKeyIndexMap.value?.[Number(getRowKey(related))]
|
||||
|
||||
if (oldRow.length !== newRow.length || oldRow[oldRow.length - 2] !== newRow[newRow.length - 2])
|
||||
return false
|
||||
|
||||
if (props.sortableMoveHook)
|
||||
return props.sortableMoveHook(oldRow, newRow)
|
||||
},
|
||||
async onEnd({
|
||||
item,
|
||||
newIndex,
|
||||
oldIndex,
|
||||
}) {
|
||||
if (newIndex === oldIndex)
|
||||
return
|
||||
|
||||
const indexDelta: number = Number(oldIndex) - Number(newIndex)
|
||||
const direction: number = indexDelta > 0 ? +1 : -1
|
||||
|
||||
const rowIndex: number[] = rowsKeyIndexMap.value?.[Number(getRowKey(item))]
|
||||
const newRow = getTargetData(dataSource.value, rowIndex)
|
||||
const newRowParent = newRow.parent
|
||||
const level: number = newRow.level
|
||||
|
||||
const currentRowIndex: number[] = [...rowsKeyIndexMap.value?.
|
||||
[Number(getRowKey(table.children[Number(newIndex) + direction]))]]
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const currentRow: any = getTargetData(dataSource.value, currentRowIndex)
|
||||
|
||||
// Reset parent
|
||||
currentRow.parent = newRow.parent = null
|
||||
newRowParent.children.splice(rowIndex[level], 1)
|
||||
newRowParent.children.splice(currentRowIndex[level], 0, toRaw(newRow))
|
||||
|
||||
const changeIds: number[] = []
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function processChanges(row: any, children = false, _newIndex: number | undefined = undefined) {
|
||||
// Build changes ID list expect new row
|
||||
if (children || _newIndex === undefined)
|
||||
changeIds.push(row.id)
|
||||
|
||||
if (_newIndex !== undefined)
|
||||
rowsKeyIndexMap.value[row.id][level] = _newIndex
|
||||
else if (children)
|
||||
rowsKeyIndexMap.value[row.id][level] += direction
|
||||
|
||||
row.parent = null
|
||||
if (row.children) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
row.children.forEach((v: any) => processChanges(v, true, _newIndex))
|
||||
}
|
||||
}
|
||||
|
||||
// Replace row index for new row
|
||||
processChanges(newRow, false, currentRowIndex[level])
|
||||
|
||||
// Rebuild row index maps for changes row
|
||||
for (let i = Number(oldIndex); i !== newIndex; i -= direction) {
|
||||
const _rowIndex: number[] = rowsKeyIndexMap.value?.[getRowKey(table.children[i])]
|
||||
|
||||
_rowIndex[level] += direction
|
||||
processChanges(getTargetData(dataSource.value, _rowIndex))
|
||||
}
|
||||
console.log('Change row id', newRow.id, 'order', newRow.id, '=>', currentRow.id, ', direction: ', direction,
|
||||
', changes IDs:', changeIds)
|
||||
|
||||
props.api.update_order({
|
||||
target_id: newRow.id,
|
||||
direction,
|
||||
affected_ids: changeIds,
|
||||
}).then(() => {
|
||||
message.success($gettext('Updated successfully'))
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
}).catch((e: any) => {
|
||||
message.error(e?.message ?? $gettext('Server error'))
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export default useSortable
|
|
@ -1,54 +0,0 @@
|
|||
import { defineComponent } from 'vue'
|
||||
import { Form } from 'ant-design-vue'
|
||||
import StdFormItem from '@/components/StdDesign/StdDataEntry/StdFormItem.vue'
|
||||
import './style.less'
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
dataList: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
dataSource: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
error: {
|
||||
type: Object,
|
||||
required: false,
|
||||
},
|
||||
layout: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
emits: ['update:dataSource'],
|
||||
setup(props, { slots }) {
|
||||
return () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const template: any = []
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
props.dataList.forEach((v: any) => {
|
||||
let show = true
|
||||
if (v.edit.show) {
|
||||
if (typeof v.edit.show === 'boolean')
|
||||
show = v.edit.show
|
||||
else if (typeof v.edit.show === 'function')
|
||||
show = v.edit.show(props.dataSource)
|
||||
}
|
||||
if (v.edit.type && show) {
|
||||
template.push(<StdFormItem dataIndex={v.dataIndex} label={v.title()} extra={v.extra} error={props.error}>
|
||||
{v.edit.type(v.edit, props.dataSource, v.dataIndex)}
|
||||
</StdFormItem>,
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
if (slots.action)
|
||||
template.push(<div class={'std-data-entry-action'}>{slots.action()}</div>)
|
||||
|
||||
return <Form layout={props.layout || 'vertical'}>{template}</Form>
|
||||
}
|
||||
},
|
||||
})
|
83
app/src/components/StdDesign/StdDataEntry/StdDataEntry.vue
Normal file
83
app/src/components/StdDesign/StdDataEntry/StdDataEntry.vue
Normal file
|
@ -0,0 +1,83 @@
|
|||
<script setup lang="tsx">
|
||||
import { Form } from 'ant-design-vue'
|
||||
import type { Column } from '@/components/StdDesign/types'
|
||||
import StdFormItem from '@/components/StdDesign/StdDataEntry/StdFormItem.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
dataList: Column[]
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
dataSource: Record<string, any>
|
||||
errors?: Record<string, string>
|
||||
layout?: 'horizontal' | 'vertical'
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
'update:dataSource': (v: any[]) => void
|
||||
}>()
|
||||
|
||||
const dataSource = computed({
|
||||
get() {
|
||||
return props.dataSource
|
||||
},
|
||||
set(v) {
|
||||
emit('update:dataSource', v)
|
||||
},
|
||||
})
|
||||
|
||||
const slots = useSlots()
|
||||
|
||||
function labelRender(title?: string | (() => string)) {
|
||||
if (typeof title === 'function')
|
||||
return title()
|
||||
|
||||
return title
|
||||
}
|
||||
|
||||
function extraRender(extra?: string | (() => string)) {
|
||||
if (typeof extra === 'function')
|
||||
return extra()
|
||||
|
||||
return extra
|
||||
}
|
||||
|
||||
function Render() {
|
||||
const template = []
|
||||
|
||||
props.dataList.forEach((v: Column) => {
|
||||
let show = true
|
||||
if (v.edit?.show && typeof v.edit.show === 'function')
|
||||
show = v.edit.show(props.dataSource)
|
||||
|
||||
if (v.edit?.type && show) {
|
||||
template.push(<StdFormItem
|
||||
dataIndex={v.dataIndex}
|
||||
label={labelRender(v.title)}
|
||||
extra={extraRender(v.extra)}
|
||||
error={props.errors}>
|
||||
{v.edit.type(v.edit, dataSource.value, v.dataIndex)}
|
||||
</StdFormItem>,
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
if (slots.action)
|
||||
template.push(<div class={'std-data-entry-action'}>{slots.action()}</div>)
|
||||
|
||||
return <Form layout={props.layout || 'vertical'}>{template}</Form>
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Render />
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
.std-data-entry-action {
|
||||
@media (max-width: 375px) {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin: 10px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -2,6 +2,7 @@
|
|||
import StdTable from '@/components/StdDesign/StdDataDisplay/StdTable.vue'
|
||||
import gettext from '@/gettext'
|
||||
import type Curd from '@/api/curd'
|
||||
import type { Column } from '@/components/StdDesign/types'
|
||||
|
||||
const props = defineProps<{
|
||||
selectedKey: string | number
|
||||
|
@ -10,7 +11,7 @@ const props = defineProps<{
|
|||
selectionType: 'radio' | 'checkbox'
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
api: Curd<any>
|
||||
columns: any[]
|
||||
columns: Column[]
|
||||
dataKey: string
|
||||
disableSearch: boolean
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { h } from 'vue'
|
||||
import { Input, InputNumber, Switch, Textarea } from 'ant-design-vue'
|
||||
import _ from 'lodash'
|
||||
import StdDataEntry from './StdDataEntry'
|
||||
import StdDataEntry from './StdDataEntry.vue'
|
||||
import StdSelector from './components/StdSelector.vue'
|
||||
import StdSelect from './components/StdSelect.vue'
|
||||
import StdPassword from './components/StdPassword.vue'
|
||||
|
|
48
app/src/components/StdDesign/types.d.ts
vendored
48
app/src/components/StdDesign/types.d.ts
vendored
|
@ -1,10 +1,13 @@
|
|||
import Curd from '@/api/curd'
|
||||
import {IKeyEvt} from '@/components/StdDesign/StdDataDisplay/types'
|
||||
import Curd, {Pagination} from '@/api/curd'
|
||||
import { Ref } from 'vue'
|
||||
|
||||
export interface StdDesignEdit {
|
||||
type?: function // component type
|
||||
|
||||
show?: function // show component
|
||||
|
||||
batch?: boolean // batch edit
|
||||
|
||||
mask?: {
|
||||
[key: string]: () => string
|
||||
} // use for select-option
|
||||
|
@ -39,7 +42,6 @@ export interface StdDesignEdit {
|
|||
flex?: Flex
|
||||
}
|
||||
|
||||
|
||||
export interface Flex {
|
||||
sm?: string | number | boolean
|
||||
md?: string | number | boolean
|
||||
|
@ -47,3 +49,43 @@ export interface Flex {
|
|||
xl?: string | number | boolean
|
||||
xxl?: string | number | boolean
|
||||
}
|
||||
|
||||
export interface Column {
|
||||
title?: string | (() => string);
|
||||
dataIndex: string;
|
||||
edit?: StdDesignEdit;
|
||||
customRender?: function;
|
||||
extra?: string | (() => string);
|
||||
pithy?: boolean;
|
||||
search?: boolean | StdDesignEdit;
|
||||
sortable?: boolean;
|
||||
hidden?: boolean;
|
||||
width?: string | number;
|
||||
handle?: boolean;
|
||||
hiddenInTrash?: boolean;
|
||||
hiddenInCreate?: boolean;
|
||||
hiddenInModify?: boolean;
|
||||
batch?: boolean;
|
||||
}
|
||||
|
||||
|
||||
export interface StdTableProvideData {
|
||||
displayColumns: Column[];
|
||||
pithyColumns: Column[];
|
||||
columnsMap: { [key: string]: Column };
|
||||
displayKeys: string[];
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
editItem: (id: number, data: any, index: string | number) => void;
|
||||
deleteItem: (id: number, index: string | number) => void;
|
||||
recoverItem: (id: number) => {};
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
params: any;
|
||||
dataSource: any;
|
||||
get_list: () => void;
|
||||
loading: Ref<boolean>;
|
||||
}
|
||||
|
||||
export interface StdTableResponse {
|
||||
data: any[]
|
||||
pagination: Pagination
|
||||
}
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
<script>
|
||||
export default {
|
||||
name: 'BaseRouterView',
|
||||
}
|
||||
<script setup lang="ts">
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -1,13 +1,7 @@
|
|||
<script>
|
||||
export default {
|
||||
name: 'Loading',
|
||||
props: {
|
||||
loading: {
|
||||
type: [Boolean, String],
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
loading: boolean | string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"version":"2.0.0-beta.4","build_id":53,"total_build":257}
|
||||
{"version":"2.0.0-beta.4","build_id":56,"total_build":260}
|
|
@ -97,7 +97,7 @@ function handle_uptime(t: number) {
|
|||
uptime.value = `${uptime_days}d ${uptime_hours}h ${Math.floor(_uptime / 60)}m`
|
||||
}
|
||||
|
||||
function wsOnMessage(m: { data: any }) {
|
||||
function wsOnMessage(m) {
|
||||
const r = JSON.parse(m.data)
|
||||
|
||||
const cpu_usage = r.cpu.system + r.cpu.user
|
||||
|
|
|
@ -5,7 +5,25 @@ import memory from '@/assets/svg/memory.svg'
|
|||
import { bytesToSize } from '@/lib/helper'
|
||||
import UsageProgressLine from '@/components/Chart/UsageProgressLine.vue'
|
||||
|
||||
defineProps(['item'])
|
||||
defineProps<{
|
||||
item: {
|
||||
avg_load: {
|
||||
load1: number
|
||||
load5: number
|
||||
load15: number
|
||||
}
|
||||
network: {
|
||||
bytesSent: number
|
||||
bytesRecv: number
|
||||
}
|
||||
cpu_percent: number
|
||||
cpu_num: number
|
||||
memory_percent: number
|
||||
memory_total: string
|
||||
disk_percent: number
|
||||
disk_total: string
|
||||
}
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -52,10 +52,6 @@ const columns = [{
|
|||
|
||||
const table = ref()
|
||||
|
||||
interface Table {
|
||||
get_list(): void
|
||||
}
|
||||
|
||||
function enable(name) {
|
||||
domain.enable(name).then(() => {
|
||||
message.success($gettext('Enabled successfully'))
|
||||
|
@ -76,9 +72,7 @@ function disable(name) {
|
|||
|
||||
function destroy(site_name) {
|
||||
domain.destroy(site_name).then(() => {
|
||||
const t: Table | null = table.value
|
||||
|
||||
t!.get_list()
|
||||
table.value.get_list()
|
||||
message.success($gettext('Delete site: %{site_name}', { site_name }))
|
||||
}).catch(e => {
|
||||
message.error(e?.message ?? $gettext('Server error'))
|
||||
|
@ -163,7 +157,7 @@ watch(route, () => {
|
|||
<SiteDuplicate
|
||||
v-model:visible="show_duplicator"
|
||||
:name="target"
|
||||
@duplicated="table.get_list()"
|
||||
@duplicated="() => table.get_list()"
|
||||
/>
|
||||
</ACard>
|
||||
</template>
|
||||
|
|
|
@ -11,7 +11,7 @@ const this_year = new Date().getFullYear()
|
|||
|
||||
<template>
|
||||
<ACard
|
||||
style="text-align: center"
|
||||
class="text-center"
|
||||
:bordered="false"
|
||||
>
|
||||
<div class="logo">
|
||||
|
@ -34,26 +34,23 @@ const this_year = new Date().getFullYear()
|
|||
Star
|
||||
</GithubButton>
|
||||
</div>
|
||||
<h3 v-translate>
|
||||
Project Team
|
||||
<h3>
|
||||
{{ $gettext('Project Team') }}
|
||||
</h3>
|
||||
<p><a href="https://jackyu.cn/">@0xJacky</a> <a href="https://blog.kugeek.com/">@Hintay</a></p>
|
||||
<h3 v-translate>
|
||||
Build with
|
||||
<h3>
|
||||
{{ $gettext('Build with') }}
|
||||
</h3>
|
||||
<p>❤️</p>
|
||||
<p>Go</p>
|
||||
<p>Gin</p>
|
||||
<p>Vue3 + Vite + TypeScript</p>
|
||||
<p>Websocket</p>
|
||||
<h3
|
||||
v-translate
|
||||
translate-context="Project"
|
||||
>
|
||||
License
|
||||
<h3>
|
||||
{{ $gettext('License') }}
|
||||
</h3>
|
||||
<p>GNU General Public License v3.0</p>
|
||||
<p>Copyright © 2020 - {{ this_year }} Nginx UI </p>
|
||||
<p>Copyright © 2020 - {{ this_year }} Nginx UI Team</p>
|
||||
</ACard>
|
||||
</template>
|
||||
|
||||
|
@ -64,14 +61,6 @@ const this_year = new Date().getFullYear()
|
|||
}
|
||||
}
|
||||
|
||||
.egg {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.ant-btn {
|
||||
margin: 10px 10px 0 0;
|
||||
}
|
||||
|
||||
.star-on-github {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
|
|
@ -4,13 +4,14 @@ import gettext from '@/gettext'
|
|||
import user from '@/api/user'
|
||||
import { datetime } from '@/components/StdDesign/StdDataDisplay/StdTableTransformer'
|
||||
import { input, password } from '@/components/StdDesign/StdDataEntry'
|
||||
import type { Column } from '@/components/StdDesign/types'
|
||||
|
||||
const { $gettext } = gettext
|
||||
|
||||
const columns = [{
|
||||
const columns: Column[] = [{
|
||||
title: () => $gettext('Username'),
|
||||
dataIndex: 'name',
|
||||
sorter: true,
|
||||
sortable: true,
|
||||
pithy: true,
|
||||
edit: {
|
||||
type: input,
|
||||
|
@ -19,25 +20,27 @@ const columns = [{
|
|||
}, {
|
||||
title: () => $gettext('Password'),
|
||||
dataIndex: 'password',
|
||||
sorter: true,
|
||||
sortable: true,
|
||||
pithy: true,
|
||||
edit: {
|
||||
type: password,
|
||||
config: {
|
||||
placeholder: () => $gettext('Leave blank for no change'),
|
||||
generate: true,
|
||||
},
|
||||
display: false,
|
||||
},
|
||||
hidden: true,
|
||||
}, {
|
||||
title: () => $gettext('Created at'),
|
||||
dataIndex: 'created_at',
|
||||
customRender: datetime,
|
||||
sorter: true,
|
||||
sortable: true,
|
||||
pithy: true,
|
||||
}, {
|
||||
title: () => $gettext('Updated at'),
|
||||
dataIndex: 'updated_at',
|
||||
customRender: datetime,
|
||||
sorter: true,
|
||||
sortable: true,
|
||||
pithy: true,
|
||||
}, {
|
||||
title: () => $gettext('Action'),
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
|
@ -20,9 +21,11 @@
|
|||
"@/*": [
|
||||
"./src/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"types": ["vite/client"]
|
||||
},
|
||||
"include": [
|
||||
"env.d.ts",
|
||||
"src/**/*.ts",
|
||||
"src/**/*.d.ts",
|
||||
"src/**/*.tsx",
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"version":"2.0.0-beta.4","build_id":53,"total_build":257}
|
||||
{"version":"2.0.0-beta.4","build_id":56,"total_build":260}
|
Loading…
Add table
Add a link
Reference in a new issue