feat: config management page
参数配置页:纯键值 CRUD,复用 ty-crud + JSON Schema 零新组件。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
1df44fd57e
commit
16b512a3f1
112
src/pages/system/config/index.vue
Normal file
112
src/pages/system/config/index.vue
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
<template>
|
||||||
|
<ty-crud
|
||||||
|
entity-name="配置"
|
||||||
|
:columns="columns"
|
||||||
|
:search-schema="searchSchema"
|
||||||
|
:form-schema="formSchema"
|
||||||
|
:list="list"
|
||||||
|
:create="create"
|
||||||
|
:update="update"
|
||||||
|
:remove="remove"
|
||||||
|
id-key="id"
|
||||||
|
name-key="key"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { JsonSchema, PageQuery, PageResult, TableColumn } from '@/types/crud'
|
||||||
|
|
||||||
|
const columns: TableColumn[] = [
|
||||||
|
{ prop: 'key', label: '键名', minWidth: 160 },
|
||||||
|
{ prop: 'value', label: '值', minWidth: 220 },
|
||||||
|
{ prop: 'remark', label: '备注', minWidth: 160 },
|
||||||
|
{ prop: 'createdAt', label: '创建时间', width: 160, type: 'datetime' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const searchSchema: JsonSchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
key: { type: 'string', title: '键名', placeholder: '请输入键名' },
|
||||||
|
value: { type: 'string', title: '值', placeholder: '请输入值' },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const formSchema: JsonSchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
key: { type: 'string', title: '键名', placeholder: '请输入键名', minLength: 1 },
|
||||||
|
value: { type: 'string', title: '值', format: 'textarea', placeholder: '请输入值' },
|
||||||
|
remark: { type: 'string', title: '备注', placeholder: '请输入备注' },
|
||||||
|
},
|
||||||
|
required: ['key'],
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- 占位数据(后续对接后端时替换为 http 调用)----
|
||||||
|
interface ConfigRow {
|
||||||
|
id: string
|
||||||
|
key: string
|
||||||
|
value: string
|
||||||
|
remark: string
|
||||||
|
createdAt: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const now = Date.now()
|
||||||
|
const day = 24 * 60 * 60 * 1000
|
||||||
|
const configs: ConfigRow[] = [
|
||||||
|
{ id: '1', key: 'sys.name', value: '投云系统管理平台', remark: '系统名称', createdAt: now - 30 * day },
|
||||||
|
{ id: '2', key: 'sys.theme', value: '#2E7FA3', remark: '默认主题色', createdAt: now - 20 * day },
|
||||||
|
{ id: '3', key: 'sys.copyright', value: '云南投云科技有限公司', remark: '版权信息', createdAt: now - 10 * day },
|
||||||
|
{ id: '4', key: 'sys.pageSize', value: '10', remark: '默认分页大小', createdAt: now - 5 * day },
|
||||||
|
{ id: '5', key: 'sys.captcha', value: 'true', remark: '是否开启验证码', createdAt: now - 1 * day },
|
||||||
|
]
|
||||||
|
|
||||||
|
let seq = 0
|
||||||
|
const nextId = () => `c-${Date.now()}-${++seq}`
|
||||||
|
|
||||||
|
function delay<T>(fn: () => T, ms = 300): Promise<T> {
|
||||||
|
return new Promise((resolve) => setTimeout(() => resolve(fn()), ms))
|
||||||
|
}
|
||||||
|
|
||||||
|
const list = (query: PageQuery): Promise<PageResult<Record<string, any>>> =>
|
||||||
|
delay(() => {
|
||||||
|
const { page, pageSize, key, value } = query
|
||||||
|
const filtered = configs.filter((c) => {
|
||||||
|
if (key && !c.key.includes(String(key))) return false
|
||||||
|
if (value && !c.value.includes(String(value))) return false
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
const start = (page - 1) * pageSize
|
||||||
|
return {
|
||||||
|
list: filtered.slice(start, start + pageSize) as unknown as Record<string, any>[],
|
||||||
|
total: filtered.length,
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const create = (data: Record<string, any>): Promise<unknown> =>
|
||||||
|
delay(() => {
|
||||||
|
configs.unshift({
|
||||||
|
id: nextId(),
|
||||||
|
key: data.key,
|
||||||
|
value: data.value ?? '',
|
||||||
|
remark: data.remark ?? '',
|
||||||
|
createdAt: Date.now(),
|
||||||
|
})
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
|
||||||
|
const update = (id: string, data: Record<string, any>): Promise<unknown> =>
|
||||||
|
delay(() => {
|
||||||
|
const c = configs.find((x) => x.id === id)
|
||||||
|
if (c) Object.assign(c, data)
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
|
||||||
|
const remove = (id: string): Promise<unknown> =>
|
||||||
|
delay(() => {
|
||||||
|
const i = configs.findIndex((x) => x.id === id)
|
||||||
|
if (i >= 0) configs.splice(i, 1)
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
</script>
|
||||||
File diff suppressed because one or more lines are too long
13
typed-router.d.ts
vendored
13
typed-router.d.ts
vendored
@ -44,6 +44,13 @@ declare module 'vue-router/auto-routes' {
|
|||||||
Record<never, never>,
|
Record<never, never>,
|
||||||
| never
|
| never
|
||||||
>,
|
>,
|
||||||
|
'/system/config/': RouteRecordInfo<
|
||||||
|
'/system/config/',
|
||||||
|
'/system/config',
|
||||||
|
Record<never, never>,
|
||||||
|
Record<never, never>,
|
||||||
|
| never
|
||||||
|
>,
|
||||||
'/system/user/': RouteRecordInfo<
|
'/system/user/': RouteRecordInfo<
|
||||||
'/system/user/',
|
'/system/user/',
|
||||||
'/system/user',
|
'/system/user',
|
||||||
@ -82,6 +89,12 @@ declare module 'vue-router/auto-routes' {
|
|||||||
views:
|
views:
|
||||||
| never
|
| never
|
||||||
}
|
}
|
||||||
|
'src/pages/system/config/index.vue': {
|
||||||
|
routes:
|
||||||
|
| '/system/config/'
|
||||||
|
views:
|
||||||
|
| never
|
||||||
|
}
|
||||||
'src/pages/system/user/index.vue': {
|
'src/pages/system/user/index.vue': {
|
||||||
routes:
|
routes:
|
||||||
| '/system/user/'
|
| '/system/user/'
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user