feat
This commit is contained in:
77
.kilo/templates/Detail.vue
Normal file
77
.kilo/templates/Detail.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<div class="detail-container">
|
||||
<a-descriptions :column="2" bordered>
|
||||
<a-descriptions-item label="ID">{{ record.id }}</a-descriptions-item>
|
||||
<a-descriptions-item label="名称">{{ record.name }}</a-descriptions-item>
|
||||
<a-descriptions-item label="描述" :span="2">{{ record.description || '-' }}</a-descriptions-item>
|
||||
<a-descriptions-item label="启用状态">
|
||||
<a-tag :color="record.enabled ? 'green' : 'gray'">
|
||||
{{ record.enabled ? '已启用' : '已禁用' }}
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="创建时间">{{ formatTime(record.created_at) }}</a-descriptions-item>
|
||||
<a-descriptions-item label="更新时间">{{ formatTime(record.updated_at) }}</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
|
||||
<div class="actions">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleEdit">
|
||||
<template #icon>
|
||||
<icon-edit />
|
||||
</template>
|
||||
编辑
|
||||
</a-button>
|
||||
<a-button status="danger" @click="handleDelete">
|
||||
<template #icon>
|
||||
<icon-delete />
|
||||
</template>
|
||||
删除
|
||||
</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { IconEdit, IconDelete } from '@arco-design/web-vue/es/icon'
|
||||
import type { {{Module}}Item } from '@/api/ops/{{module}}'
|
||||
|
||||
const props = defineProps<{
|
||||
record: {{Module}}Item
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'edit'): void
|
||||
(e: 'delete'): void
|
||||
}>()
|
||||
|
||||
const formatTime = (time?: string) => {
|
||||
if (!time) return '-'
|
||||
const date = new Date(time)
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
const hours = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0')
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||
}
|
||||
|
||||
const handleEdit = () => {
|
||||
emit('edit')
|
||||
}
|
||||
|
||||
const handleDelete = () => {
|
||||
emit('delete')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.detail-container {
|
||||
.actions {
|
||||
margin-top: 20px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid var(--color-border);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
107
.kilo/templates/FormDialog.vue
Normal file
107
.kilo/templates/FormDialog.vue
Normal file
@@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
:title="isEdit ? '编辑{{ModuleTitle}}' : '新增{{ModuleTitle}}'"
|
||||
:width="600"
|
||||
:mask-closable="false"
|
||||
unmount-on-close
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<a-form ref="formRef" :model="formData" :rules="rules" layout="vertical">
|
||||
<a-form-item field="name" label="名称" required>
|
||||
<a-input v-model="formData.name" placeholder="请输入名称" :max-length="100" />
|
||||
</a-form-item>
|
||||
<a-form-item field="description" label="描述">
|
||||
<a-textarea v-model="formData.description" placeholder="请输入描述" :max-length="500" :auto-size="{ minRows: 3, maxRows: 5 }" />
|
||||
</a-form-item>
|
||||
<a-form-item field="enabled" label="启用状态">
|
||||
<a-switch v-model="formData.enabled" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import type { {{Module}}Item, {{Module}}CreateData, {{Module}}UpdateData } from '@/api/ops/{{module}}'
|
||||
import { create{{Module}}, update{{Module}} } from '@/api/ops/{{module}}'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
record: {{Module}}Item | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', value: boolean): void
|
||||
(e: 'success'): void
|
||||
}>()
|
||||
|
||||
const formRef = ref()
|
||||
const isEdit = computed(() => !!props.record)
|
||||
|
||||
const formData = ref<{{Module}}CreateData>({
|
||||
name: '',
|
||||
description: '',
|
||||
enabled: true,
|
||||
})
|
||||
|
||||
const rules = {
|
||||
name: [
|
||||
{ required: true, message: '请输入名称' },
|
||||
{ max_length: 100, message: '名称不能超过100个字符' },
|
||||
],
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(val) => {
|
||||
if (val && props.record) {
|
||||
formData.value = {
|
||||
name: props.record.name,
|
||||
description: props.record.description || '',
|
||||
enabled: props.record.enabled,
|
||||
}
|
||||
} else if (val) {
|
||||
formData.value = {
|
||||
name: '',
|
||||
description: '',
|
||||
enabled: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const handleOk = async () => {
|
||||
try {
|
||||
const valid = await formRef.value?.validate()
|
||||
if (valid) return
|
||||
|
||||
if (isEdit.value && props.record) {
|
||||
const res = await update{{Module}}(props.record.id, formData.value as {{Module}}UpdateData)
|
||||
if (res && res.code === 0) {
|
||||
Message.success('更新成功')
|
||||
emit('success')
|
||||
emit('update:visible', false)
|
||||
}
|
||||
} else {
|
||||
const res = await create{{Module}}(formData.value)
|
||||
if (res && res.code === 0) {
|
||||
Message.success('创建成功')
|
||||
emit('success')
|
||||
emit('update:visible', false)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('操作失败:', error)
|
||||
Message.error('操作失败')
|
||||
}
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
emit('update:visible', false)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
66
.kilo/templates/api-module.ts
Normal file
66
.kilo/templates/api-module.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { request } from '@/api/request'
|
||||
|
||||
/** {{Module}}类型 */
|
||||
export interface {{Module}}Item {
|
||||
id: number
|
||||
created_at: string
|
||||
updated_at: string
|
||||
name: string
|
||||
description?: string
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
/** {{Module}}列表响应 */
|
||||
export interface {{Module}}ListResponse {
|
||||
total: number
|
||||
page: number
|
||||
page_size: number
|
||||
data: {{Module}}Item[]
|
||||
}
|
||||
|
||||
/** {{Module}}列表请求参数 */
|
||||
export interface {{Module}}ListParams {
|
||||
page?: number
|
||||
size?: number
|
||||
keyword?: string
|
||||
enabled?: boolean
|
||||
}
|
||||
|
||||
/** 创建{{Module}}请求参数 */
|
||||
export interface {{Module}}CreateData {
|
||||
name: string
|
||||
description?: string
|
||||
enabled?: boolean
|
||||
}
|
||||
|
||||
/** 更新{{Module}}请求参数 */
|
||||
export interface {{Module}}UpdateData {
|
||||
name?: string
|
||||
description?: string
|
||||
enabled?: boolean
|
||||
}
|
||||
|
||||
/** 获取{{Module}}列表(分页) */
|
||||
export const fetch{{Module}}List = (params?: {{Module}}ListParams) => {
|
||||
return request.get<{{Module}}ListResponse>('/DC-Control/v1/{{module}}s', { params })
|
||||
}
|
||||
|
||||
/** 获取{{Module}}详情 */
|
||||
export const fetch{{Module}}Detail = (id: number) => {
|
||||
return request.get<{{Module}}Item>(`/DC-Control/v1/{{module}}s/${id}`)
|
||||
}
|
||||
|
||||
/** 创建{{Module}} */
|
||||
export const create{{Module}} = (data: {{Module}}CreateData) => {
|
||||
return request.post<{ message: string; id: number }>('/DC-Control/v1/{{module}}s', data)
|
||||
}
|
||||
|
||||
/** 更新{{Module}} */
|
||||
export const update{{Module}} = (id: number, data: {{Module}}UpdateData) => {
|
||||
return request.put<{ message: string }>(`/DC-Control/v1/{{module}}s/${id}`, data)
|
||||
}
|
||||
|
||||
/** 删除{{Module}} */
|
||||
export const delete{{Module}} = (id: number) => {
|
||||
return request.delete<{ message: string }>(`/DC-Control/v1/{{module}}s/${id}`)
|
||||
}
|
||||
39
.kilo/templates/columns.ts
Normal file
39
.kilo/templates/columns.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
export const columns = [
|
||||
{
|
||||
dataIndex: 'id',
|
||||
title: 'ID',
|
||||
width: 80,
|
||||
slotName: 'id',
|
||||
},
|
||||
{
|
||||
dataIndex: 'name',
|
||||
title: '名称',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
dataIndex: 'description',
|
||||
title: '描述',
|
||||
width: 200,
|
||||
ellipsis: true,
|
||||
tooltip: true,
|
||||
},
|
||||
{
|
||||
dataIndex: 'enabled',
|
||||
title: '启用状态',
|
||||
width: 100,
|
||||
slotName: 'enabled',
|
||||
},
|
||||
{
|
||||
dataIndex: 'created_at',
|
||||
title: '创建时间',
|
||||
width: 180,
|
||||
slotName: 'created_at',
|
||||
},
|
||||
{
|
||||
dataIndex: 'actions',
|
||||
title: '操作',
|
||||
width: 180,
|
||||
fixed: 'right' as const,
|
||||
slotName: 'actions',
|
||||
},
|
||||
]
|
||||
235
.kilo/templates/index.vue
Normal file
235
.kilo/templates/index.vue
Normal file
@@ -0,0 +1,235 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<search-table
|
||||
:form-model="formModel"
|
||||
:form-items="formItems"
|
||||
:data="tableData"
|
||||
:columns="columns"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
title="{{ModuleTitle}}管理"
|
||||
search-button-text="查询"
|
||||
reset-button-text="重置"
|
||||
@update:form-model="handleFormModelUpdate"
|
||||
@search="handleSearch"
|
||||
@reset="handleReset"
|
||||
@page-change="handlePageChange"
|
||||
@refresh="handleRefresh"
|
||||
>
|
||||
<template #toolbar-left>
|
||||
<a-button type="primary" @click="handleAdd">
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
</template>
|
||||
新增
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<template #id="{ record }">
|
||||
{{ record.id }}
|
||||
</template>
|
||||
|
||||
<template #enabled="{ record }">
|
||||
<a-tag :color="record.enabled ? 'green' : 'gray'">
|
||||
{{ record.enabled ? '已启用' : '已禁用' }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<template #created_at="{ record }">
|
||||
{{ formatTime(record.created_at) }}
|
||||
</template>
|
||||
|
||||
<template #actions="{ record }">
|
||||
<a-space>
|
||||
<a-button type="text" size="small" @click="handleDetail(record)">
|
||||
<template #icon>
|
||||
<icon-eye />
|
||||
</template>
|
||||
详情
|
||||
</a-button>
|
||||
<a-button type="text" size="small" @click="handleEdit(record)">
|
||||
<template #icon>
|
||||
<icon-edit />
|
||||
</template>
|
||||
编辑
|
||||
</a-button>
|
||||
<a-button type="text" size="small" status="danger" @click="handleDelete(record)">
|
||||
<template #icon>
|
||||
<icon-delete />
|
||||
</template>
|
||||
删除
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</search-table>
|
||||
|
||||
<FormDialog v-model:visible="formDialogVisible" :record="currentRecord" @success="handleFormSuccess" />
|
||||
|
||||
<a-drawer v-model:visible="detailVisible" :width="600" title="{{ModuleTitle}}详情" :footer="false" unmount-on-close>
|
||||
<Detail v-if="currentRecord" :record="currentRecord" @edit="handleDetailEdit" @delete="handleDetailDelete" />
|
||||
</a-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, computed } from 'vue'
|
||||
import { Message, Modal } from '@arco-design/web-vue'
|
||||
import { IconPlus, IconEdit, IconDelete, IconEye } from '@arco-design/web-vue/es/icon'
|
||||
import type { FormItem } from '@/components/search-form/types'
|
||||
import SearchTable from '@/components/search-table/index.vue'
|
||||
import FormDialog from './components/FormDialog.vue'
|
||||
import Detail from './components/Detail.vue'
|
||||
import { searchFormConfig } from './config/search-form'
|
||||
import { columns as columnsConfig } from './config/columns'
|
||||
import { fetch{{Module}}List, delete{{Module}}, type {{Module}}Item, type {{Module}}ListParams } from '@/api/ops/{{module}}'
|
||||
|
||||
const loading = ref(false)
|
||||
const tableData = ref<{{Module}}Item[]>([])
|
||||
const formDialogVisible = ref(false)
|
||||
const detailVisible = ref(false)
|
||||
const currentRecord = ref<{{Module}}Item | null>(null)
|
||||
const formModel = ref({
|
||||
keyword: '',
|
||||
enabled: undefined as boolean | undefined,
|
||||
})
|
||||
|
||||
const pagination = reactive({
|
||||
current: 1,
|
||||
pageSize: 20,
|
||||
total: 0,
|
||||
})
|
||||
|
||||
const formItems = computed<FormItem[]>(() => searchFormConfig)
|
||||
const columns = computed(() => columnsConfig)
|
||||
|
||||
const formatTime = (time?: string) => {
|
||||
if (!time) return '-'
|
||||
const date = new Date(time)
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
const hours = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0')
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||
}
|
||||
|
||||
const fetchData = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const params: {{Module}}ListParams = {
|
||||
page: pagination.current,
|
||||
size: pagination.pageSize,
|
||||
keyword: formModel.value.keyword,
|
||||
enabled: formModel.value.enabled,
|
||||
}
|
||||
const response: any = await fetch{{Module}}List(params)
|
||||
if (response && response.code === 0 && response.details) {
|
||||
tableData.value = response.details?.data || []
|
||||
pagination.total = response.details?.total || 0
|
||||
} else {
|
||||
tableData.value = []
|
||||
pagination.total = 0
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取列表失败:', error)
|
||||
Message.error('获取列表失败')
|
||||
tableData.value = []
|
||||
pagination.total = 0
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
pagination.current = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
const handleFormModelUpdate = (value: any) => {
|
||||
formModel.value = value
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
formModel.value = {
|
||||
keyword: '',
|
||||
enabled: undefined,
|
||||
}
|
||||
pagination.current = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
const handlePageChange = (current: number) => {
|
||||
pagination.current = current
|
||||
fetchData()
|
||||
}
|
||||
|
||||
const handleRefresh = () => {
|
||||
fetchData()
|
||||
Message.success('数据已刷新')
|
||||
}
|
||||
|
||||
const handleAdd = () => {
|
||||
currentRecord.value = null
|
||||
formDialogVisible.value = true
|
||||
}
|
||||
|
||||
const handleEdit = (record: {{Module}}Item) => {
|
||||
currentRecord.value = record
|
||||
formDialogVisible.value = true
|
||||
}
|
||||
|
||||
const handleDetail = (record: {{Module}}Item) => {
|
||||
currentRecord.value = record
|
||||
detailVisible.value = true
|
||||
}
|
||||
|
||||
const handleDetailEdit = () => {
|
||||
detailVisible.value = false
|
||||
formDialogVisible.value = true
|
||||
}
|
||||
|
||||
const handleDetailDelete = () => {
|
||||
detailVisible.value = false
|
||||
if (currentRecord.value) {
|
||||
handleDelete(currentRecord.value)
|
||||
}
|
||||
}
|
||||
|
||||
const handleFormSuccess = () => {
|
||||
fetchData()
|
||||
}
|
||||
|
||||
const handleDelete = (record: {{Module}}Item) => {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: `确认删除 "${record.name}" 吗?`,
|
||||
onOk: async () => {
|
||||
try {
|
||||
const res = await delete{{Module}}(record.id)
|
||||
if (res && res.code === 0) {
|
||||
Message.success('删除成功')
|
||||
fetchData()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除失败:', error)
|
||||
Message.error('删除失败')
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fetchData()
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: '{{ModuleName}}',
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.container {
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
22
.kilo/templates/search-form.ts
Normal file
22
.kilo/templates/search-form.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import type { FormItem } from '@/components/search-form/types'
|
||||
|
||||
export const searchFormConfig: FormItem[] = [
|
||||
{
|
||||
field: 'keyword',
|
||||
label: '关键词',
|
||||
type: 'input',
|
||||
placeholder: '请输入名称',
|
||||
span: 6,
|
||||
},
|
||||
{
|
||||
field: 'enabled',
|
||||
label: '启用状态',
|
||||
type: 'select',
|
||||
placeholder: '请选择启用状态',
|
||||
options: [
|
||||
{ label: '已启用', value: true },
|
||||
{ label: '已禁用', value: false },
|
||||
],
|
||||
span: 6,
|
||||
},
|
||||
]
|
||||
Reference in New Issue
Block a user