fix: resolved eslint errors

This commit is contained in:
0xJacky 2023-11-29 13:56:40 +08:00
parent 287ef7527d
commit d325dd7493
No known key found for this signature in database
GPG key ID: B6E4A6E4A561BAF0
33 changed files with 643 additions and 584 deletions

9
app/components.d.ts vendored
View file

@ -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
View file

@ -0,0 +1,5 @@
declare module '*.svg' {
import React from 'react'
const content: React.FC<React.SVGProps<SVGElement>>
export default content
}

View file

@ -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
View file

@ -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:

View file

@ -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

View file

@ -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

View file

@ -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)
},
}

View file

@ -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

View file

@ -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,
})
})

View file

@ -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: {

View file

@ -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;

View file

@ -1,5 +1,4 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import { useGettext } from 'vue3-gettext'
import environment from '@/api/environment'

View file

@ -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"
/>

View file

@ -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'])

View file

@ -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>

View file

@ -0,0 +1,9 @@
import StdTable from './StdTable.vue'
import StdCurd from './StdCurd.vue'
import StdBatchEdit from './StdBatchEdit.vue'
export {
StdTable,
StdCurd,
StdBatchEdit,
}

View file

@ -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

View 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

View file

@ -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>
}
},
})

View 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>

View file

@ -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

View file

@ -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'

View file

@ -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
}

View file

@ -1,7 +1,4 @@
<script>
export default {
name: 'BaseRouterView',
}
<script setup lang="ts">
</script>
<template>

View file

@ -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>

View file

@ -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}

View file

@ -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

View file

@ -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>

View file

@ -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>

View file

@ -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;
}

View file

@ -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'),

View file

@ -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",

View file

@ -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}