14 changed files with 112 additions and 897 deletions
@ -1,241 +0,0 @@ |
|||
/** |
|||
* 表格列配置 |
|||
*/ |
|||
|
|||
import type {VxeGridProps} from '#/adapter/vxe-table'; |
|||
|
|||
export const columns: VxeGridProps['columns'] = [ |
|||
|
|||
|
|||
{ |
|||
// 列标题
|
|||
title: '主键ID', |
|||
// 对应字段
|
|||
field: 'id', |
|||
// 宽度
|
|||
width: 150, |
|||
|
|||
}, |
|||
|
|||
{ |
|||
// 列标题
|
|||
title: '账号', |
|||
|
|||
// 对应字段
|
|||
field: 'userName', |
|||
|
|||
// 宽度
|
|||
width: 150 |
|||
}, |
|||
|
|||
{ |
|||
// 列标题
|
|||
title: '手机号', |
|||
|
|||
// 对应字段
|
|||
field: 'userPhone', |
|||
|
|||
// 宽度
|
|||
width: 150 |
|||
}, |
|||
|
|||
{ |
|||
// 列标题
|
|||
title: '姓名', |
|||
|
|||
// 对应字段
|
|||
field: 'userRealName', |
|||
|
|||
// 宽度
|
|||
width: 150 |
|||
}, |
|||
|
|||
{ |
|||
// 列标题
|
|||
title: '性别:0默认保密,1男2女', |
|||
|
|||
// 对应字段
|
|||
field: 'userGender', |
|||
|
|||
// 宽度
|
|||
width: 150 |
|||
}, |
|||
|
|||
{ |
|||
// 列标题
|
|||
title: '密码', |
|||
|
|||
// 对应字段
|
|||
field: 'userPassword', |
|||
|
|||
// 宽度
|
|||
width: 150 |
|||
}, |
|||
|
|||
{ |
|||
// 列标题
|
|||
title: '生日', |
|||
|
|||
// 对应字段
|
|||
field: 'userBirthday', |
|||
|
|||
// 宽度
|
|||
width: 150 |
|||
}, |
|||
|
|||
{ |
|||
// 列标题
|
|||
title: '头像', |
|||
|
|||
// 对应字段
|
|||
field: 'userFace', |
|||
|
|||
// 宽度
|
|||
width: 150 |
|||
}, |
|||
|
|||
{ |
|||
// 列标题
|
|||
title: '邮箱', |
|||
|
|||
// 对应字段
|
|||
field: 'userEmail', |
|||
|
|||
// 宽度
|
|||
width: 150 |
|||
}, |
|||
|
|||
{ |
|||
// 列标题
|
|||
title: '创建时间', |
|||
|
|||
// 对应字段
|
|||
field: 'createdAt', |
|||
|
|||
// 宽度
|
|||
width: 150 |
|||
}, |
|||
|
|||
{ |
|||
// 列标题
|
|||
title: '更新时间', |
|||
|
|||
// 对应字段
|
|||
field: 'updatedAt', |
|||
|
|||
// 宽度
|
|||
width: 150 |
|||
}, |
|||
|
|||
{ |
|||
// 列标题
|
|||
title: '创建人', |
|||
|
|||
// 对应字段
|
|||
field: 'createdBy', |
|||
|
|||
// 宽度
|
|||
width: 150 |
|||
}, |
|||
|
|||
{ |
|||
// 列标题
|
|||
title: '更新人', |
|||
|
|||
// 对应字段
|
|||
field: 'updatedBy', |
|||
|
|||
// 宽度
|
|||
width: 150 |
|||
}, |
|||
|
|||
{ |
|||
// 列标题
|
|||
title: '删除 0 默认 1删除', |
|||
|
|||
// 对应字段
|
|||
field: 'deletedFlag', |
|||
|
|||
// 宽度
|
|||
width: 150 |
|||
}, |
|||
|
|||
{ |
|||
// 列标题
|
|||
title: '地址', |
|||
|
|||
// 对应字段
|
|||
field: 'userAddress', |
|||
|
|||
// 宽度
|
|||
width: 150 |
|||
}, |
|||
|
|||
{ |
|||
// 列标题
|
|||
title: '用户ID冗余字段', |
|||
|
|||
// 对应字段
|
|||
field: 'userId', |
|||
|
|||
// 宽度
|
|||
width: 150 |
|||
}, |
|||
|
|||
{ |
|||
// 列标题
|
|||
title: '系统型号', |
|||
|
|||
// 对应字段
|
|||
field: 'userSys', |
|||
|
|||
// 宽度
|
|||
width: 150 |
|||
}, |
|||
|
|||
{ |
|||
// 列标题
|
|||
title: '手机型号', |
|||
|
|||
// 对应字段
|
|||
field: 'userPhoneModel', |
|||
|
|||
// 宽度
|
|||
width: 150 |
|||
}, |
|||
|
|||
{ |
|||
// 列标题
|
|||
title: '用户状态默认启用0,注销1', |
|||
|
|||
// 对应字段
|
|||
field: 'userStatus', |
|||
|
|||
// 宽度
|
|||
width: 150 |
|||
}, |
|||
|
|||
{ |
|||
// 列标题
|
|||
title: '手机唯一标识', |
|||
|
|||
// 对应字段
|
|||
field: 'userCid', |
|||
|
|||
// 宽度
|
|||
width: 150 |
|||
}, |
|||
|
|||
{ |
|||
// 列标题
|
|||
title: '手机IP', |
|||
|
|||
// 对应字段
|
|||
field: 'userIp', |
|||
|
|||
// 宽度
|
|||
width: 150 |
|||
}, |
|||
|
|||
|
|||
]; |
|||
@ -1,258 +0,0 @@ |
|||
/** |
|||
* 表单 schema |
|||
* component 类型来自 parse_component() |
|||
*/ |
|||
|
|||
import type {VbenFormSchema} from '#/adapter/form'; |
|||
|
|||
export const formSchema: VbenFormSchema[] = [ |
|||
|
|||
{ |
|||
// 字段名
|
|||
fieldName: 'id', |
|||
|
|||
// label
|
|||
label: '主键ID', |
|||
// 自动组件
|
|||
component: 'InputNumber' |
|||
}, |
|||
|
|||
{ |
|||
// 字段名
|
|||
fieldName: 'userName', |
|||
// label
|
|||
label: '账号', |
|||
// 自动组件
|
|||
component: 'Input' |
|||
}, |
|||
|
|||
{ |
|||
// 字段名
|
|||
fieldName: 'userPhone', |
|||
|
|||
// label
|
|||
label: '手机号', |
|||
|
|||
// 自动组件
|
|||
component: 'Input' |
|||
|
|||
}, |
|||
|
|||
{ |
|||
// 字段名
|
|||
fieldName: 'userRealName', |
|||
|
|||
// label
|
|||
label: '姓名', |
|||
|
|||
// 自动组件
|
|||
component: 'Input' |
|||
|
|||
}, |
|||
|
|||
{ |
|||
// 字段名
|
|||
fieldName: 'userGender', |
|||
|
|||
// label
|
|||
label: '性别:0默认保密,1男2女', |
|||
|
|||
// 自动组件
|
|||
component: 'InputNumber' |
|||
|
|||
}, |
|||
|
|||
{ |
|||
// 字段名
|
|||
fieldName: 'userPassword', |
|||
|
|||
// label
|
|||
label: '密码', |
|||
|
|||
// 自动组件
|
|||
component: 'Input' |
|||
|
|||
}, |
|||
|
|||
{ |
|||
// 字段名
|
|||
fieldName: 'userBirthday', |
|||
|
|||
// label
|
|||
label: '生日', |
|||
|
|||
// 自动组件
|
|||
component: 'DatePicker' |
|||
|
|||
}, |
|||
|
|||
{ |
|||
// 字段名
|
|||
fieldName: 'userFace', |
|||
|
|||
// label
|
|||
label: '头像', |
|||
|
|||
// 自动组件
|
|||
component: 'Input' |
|||
|
|||
}, |
|||
|
|||
{ |
|||
// 字段名
|
|||
fieldName: 'userEmail', |
|||
|
|||
// label
|
|||
label: '邮箱', |
|||
|
|||
// 自动组件
|
|||
component: 'Input' |
|||
|
|||
}, |
|||
|
|||
{ |
|||
// 字段名
|
|||
fieldName: 'createdAt', |
|||
|
|||
// label
|
|||
label: '创建时间', |
|||
|
|||
// 自动组件
|
|||
component: 'DatePicker' |
|||
|
|||
}, |
|||
|
|||
{ |
|||
// 字段名
|
|||
fieldName: 'updatedAt', |
|||
|
|||
// label
|
|||
label: '更新时间', |
|||
|
|||
// 自动组件
|
|||
component: 'DatePicker' |
|||
|
|||
}, |
|||
|
|||
{ |
|||
// 字段名
|
|||
fieldName: 'createdBy', |
|||
|
|||
// label
|
|||
label: '创建人', |
|||
|
|||
// 自动组件
|
|||
component: 'InputNumber' |
|||
|
|||
}, |
|||
|
|||
{ |
|||
// 字段名
|
|||
fieldName: 'updatedBy', |
|||
|
|||
// label
|
|||
label: '更新人', |
|||
|
|||
// 自动组件
|
|||
component: 'InputNumber' |
|||
|
|||
}, |
|||
|
|||
{ |
|||
// 字段名
|
|||
fieldName: 'deletedFlag', |
|||
|
|||
// label
|
|||
label: '删除 0 默认 1删除', |
|||
|
|||
// 自动组件
|
|||
component: 'InputNumber' |
|||
|
|||
}, |
|||
|
|||
{ |
|||
// 字段名
|
|||
fieldName: 'userAddress', |
|||
|
|||
// label
|
|||
label: '地址', |
|||
|
|||
// 自动组件
|
|||
component: 'Input' |
|||
|
|||
}, |
|||
|
|||
{ |
|||
// 字段名
|
|||
fieldName: 'userId', |
|||
|
|||
// label
|
|||
label: '用户ID冗余字段', |
|||
|
|||
// 自动组件
|
|||
component: 'InputNumber' |
|||
|
|||
}, |
|||
|
|||
{ |
|||
// 字段名
|
|||
fieldName: 'userSys', |
|||
|
|||
// label
|
|||
label: '系统型号', |
|||
|
|||
// 自动组件
|
|||
component: 'Input' |
|||
|
|||
}, |
|||
|
|||
{ |
|||
// 字段名
|
|||
fieldName: 'userPhoneModel', |
|||
|
|||
// label
|
|||
label: '手机型号', |
|||
|
|||
// 自动组件
|
|||
component: 'Input' |
|||
|
|||
}, |
|||
|
|||
{ |
|||
// 字段名
|
|||
fieldName: 'userStatus', |
|||
|
|||
// label
|
|||
label: '用户状态默认启用0,注销1', |
|||
|
|||
// 自动组件
|
|||
component: 'InputNumber' |
|||
|
|||
}, |
|||
|
|||
{ |
|||
// 字段名
|
|||
fieldName: 'userCid', |
|||
|
|||
// label
|
|||
label: '手机唯一标识', |
|||
|
|||
// 自动组件
|
|||
component: 'Input' |
|||
|
|||
}, |
|||
|
|||
{ |
|||
// 字段名
|
|||
fieldName: 'userIp', |
|||
|
|||
// label
|
|||
label: '手机IP', |
|||
|
|||
// 自动组件
|
|||
component: 'Input' |
|||
|
|||
}, |
|||
|
|||
|
|||
]; |
|||
@ -1,332 +0,0 @@ |
|||
<script setup lang="ts"> |
|||
/** |
|||
* 用户管理页面 |
|||
*/ |
|||
import {ref, reactive, onMounted, watch} from 'vue'; |
|||
import {useVbenVxeGrid} from '#/adapter/vxe-table'; |
|||
import {useVbenModal} from '@vben/common-ui'; |
|||
import {useVbenForm} from '#/adapter/form'; |
|||
import dayjs from 'dayjs'; |
|||
import {columns} from './data'; |
|||
import {formSchema} from './form'; |
|||
import {userApi} from '#/api/user'; |
|||
|
|||
// ========== 状态变量 ========== |
|||
const currentRow = ref(null); |
|||
const isEdit = ref(false); |
|||
const modalTitle = ref('新增'); |
|||
// ========== 动态生成的查询表单 Schema ========== |
|||
const querySchema = ref([]); |
|||
// ========== 枚举数据配置 ========== |
|||
const enumData = reactive({ |
|||
loading: true, |
|||
list: [], // 存储接口返回的原始枚举数据 |
|||
}); |
|||
//todo 枚举数据在查询里面不展示的配置 |
|||
const hiddenColumns = ref(['createdAt', 'updatedBy', 'updatedAt', 'userPassword', 'userId', 'userSys', 'deletedFlag', "userFace"]) |
|||
|
|||
// ========== 初始化枚举数据和查询表单 ========== |
|||
async function initEnumData() { |
|||
try { |
|||
enumData.loading = true; |
|||
|
|||
// 调用枚举列表接口 |
|||
const res = await userApi.enumList({}); |
|||
const enums = res.result || res; |
|||
|
|||
// 保存原始枚举数据 |
|||
enumData.list = enums; |
|||
|
|||
// 根据 queryFields 和枚举数据动态生成查询表单 schema` |
|||
querySchema.value = formSchema.filter(formItem => { |
|||
// 2. 排除隐藏的字段 |
|||
if (hiddenColumns.value.includes(formItem.fieldName)) { |
|||
console.log('跳过隐藏字段:', formItem.fieldName) |
|||
return false; |
|||
} |
|||
return true; |
|||
}).map(formSchemaTmp => { |
|||
// 在枚举列表中查找匹配的字段 |
|||
const matchedEnums = enums.filter(item => item.fieldName === formSchemaTmp.fieldName); |
|||
|
|||
// 合并相同 fieldName 的所有 options |
|||
const allOptions = matchedEnums.reduce((acc, item) => { |
|||
if (item.options && Array.isArray(item.options)) { |
|||
return [...acc, ...item.options]; |
|||
} |
|||
return acc; |
|||
}, []); |
|||
// 判断是否有匹配的枚举选项 |
|||
if (allOptions.length > 0) { |
|||
return { |
|||
component: 'Select', |
|||
fieldName: formSchemaTmp.fieldName, |
|||
label: formSchemaTmp.label, |
|||
componentProps: { |
|||
placeholder: `请选择`, |
|||
allowClear: true, |
|||
options: allOptions.map(opt => ({ |
|||
label: opt.label, |
|||
value: String(opt.value), // 确保 value 是字符串 |
|||
})), |
|||
}, |
|||
}; |
|||
} else { |
|||
// ❌ 未匹配到枚举 → 使用 Input 组件 |
|||
return { |
|||
component: 'Input', |
|||
fieldName: formSchemaTmp.fieldName, |
|||
label: formSchemaTmp.label, |
|||
componentProps: { |
|||
placeholder: `请输入${formSchemaTmp.label}`, |
|||
allowClear: true, |
|||
}, |
|||
}; |
|||
} |
|||
}); |
|||
} catch (error) { |
|||
} finally { |
|||
enumData.loading = false; |
|||
} |
|||
} |
|||
|
|||
// 页面加载时自动执行初始化 |
|||
onMounted(() => { |
|||
initEnumData(); |
|||
}); |
|||
|
|||
|
|||
// ========== 查询表单配置 (初始为空 schema) ========== |
|||
const [QueryForm, queryFormApi] = useVbenForm({ |
|||
schema: [], // 初始化为空数组 |
|||
layout: 'inline', |
|||
showDefaultActions: false, |
|||
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-5', |
|||
}) |
|||
// ========== 监听 querySchema 变化并更新表单 ========== |
|||
watch( |
|||
querySchema, |
|||
(newSchema) => { |
|||
if (newSchema && newSchema.length > 0) { |
|||
console.log('🔄 更新查询表单 schema:', newSchema); |
|||
// ✅ 使用 setState 方法更新 schema |
|||
queryFormApi.setState({ |
|||
schema: newSchema, |
|||
}); |
|||
} |
|||
}, |
|||
{immediate: false} |
|||
); |
|||
|
|||
// ========== 表格配置 ========== |
|||
const gridOptions = { |
|||
columns: [ |
|||
...columns, |
|||
{ |
|||
field: 'action', |
|||
title: '操作', |
|||
width: 120, |
|||
fixed: 'right', |
|||
slots: {default: 'action'}, |
|||
}, |
|||
], |
|||
proxyConfig: { |
|||
ajax: { |
|||
query: async ({page}) => { |
|||
try { |
|||
// 获取查询表单的值 |
|||
const queryValues = await queryFormApi.getValues(); |
|||
|
|||
console.log('=== 查询参数 ===', { |
|||
pageNum: page?.currentPage || 1, |
|||
pageSize: page?.pageSize || 10, |
|||
...queryValues, |
|||
}) |
|||
|
|||
const res = await userApi.page({ |
|||
pageNum: page?.currentPage || 1, |
|||
pageSize: page?.pageSize || 10, |
|||
...queryValues, // 携带查询条件 |
|||
}) |
|||
|
|||
const data = res.error?.result || res.result || res |
|||
|
|||
return { |
|||
items: data.records || [], |
|||
total: data.total || 0 |
|||
} |
|||
|
|||
} catch (error) { |
|||
console.error('查询用户列表失败:', error) |
|||
throw error |
|||
} |
|||
} |
|||
}, |
|||
response: { |
|||
result: 'items', |
|||
total: 'total' |
|||
} |
|||
}, |
|||
pagerConfig: { |
|||
enabled: true, |
|||
pageSize: 10, |
|||
}, |
|||
} |
|||
|
|||
const [Grid, gridApi] = useVbenVxeGrid({gridOptions}) |
|||
|
|||
// ========== 弹窗配置 ========== |
|||
const [Modal, modalApi] = useVbenModal({ |
|||
centered: true, |
|||
closable: true, |
|||
maskClosable: false, |
|||
draggable: true, |
|||
width: 800, |
|||
onCancel() { |
|||
modalApi.close(); |
|||
}, |
|||
onConfirm: async () => { |
|||
try { |
|||
const values = await formApi.validateAndSubmitForm(); |
|||
if (!values) return; |
|||
|
|||
// 处理日期格式,将 Day.js 对象转换为字符串 |
|||
const submitValues = { |
|||
...values, |
|||
userBirthday: values.userBirthday ? dayjs(values.userBirthday).format('YYYY-MM-DD') : null, |
|||
}; |
|||
|
|||
if (isEdit.value && currentRow.value?.id) { |
|||
await userApi.save({...submitValues, id: currentRow.value.id}); |
|||
} else { |
|||
await userApi.save(submitValues); |
|||
} |
|||
|
|||
modalApi.close(); |
|||
gridApi.reload(); |
|||
} catch (error) { |
|||
console.error('保存失败:', error); |
|||
} |
|||
}, |
|||
onOpenChange(isOpen: boolean) { |
|||
if (!isOpen && !isEdit.value) { |
|||
formApi.resetForm(); |
|||
} |
|||
}, |
|||
}) |
|||
|
|||
// ========== 表单配置 ========== |
|||
const [Form, formApi] = useVbenForm({ |
|||
schema: formSchema, |
|||
showDefaultActions: false, |
|||
wrapperClass: 'grid-cols-1 md:grid-cols-2', |
|||
commonConfig: { |
|||
componentProps: { |
|||
class: 'w-full', |
|||
autocomplete: 'off', |
|||
}, |
|||
}, |
|||
}) |
|||
|
|||
// ========== 打开新增弹窗 ========== |
|||
function handleAdd() { |
|||
isEdit.value = false; |
|||
modalTitle.value = '新增用户'; |
|||
formApi.resetForm(); |
|||
modalApi.open(); |
|||
} |
|||
|
|||
// ========== 打开编辑弹窗 ========== |
|||
function handleEdit(row: any) { |
|||
isEdit.value = true; |
|||
currentRow.value = row; |
|||
modalTitle.value = '编辑用户'; |
|||
|
|||
// 设置表单值,过滤掉不必要的字段,并处理日期格式 |
|||
const formValues = { |
|||
id: row.id, |
|||
userName: row.userName, |
|||
userPhone: row.userPhone, |
|||
userRealName: row.userRealName, |
|||
userGender: row.userGender, |
|||
userPassword: row.userPassword, |
|||
userBirthday: row.userBirthday, |
|||
userFace: row.userFace, |
|||
userEmail: row.userEmail, |
|||
userAddress: row.userAddress, |
|||
userId: row.userId, |
|||
userSys: row.userSys, |
|||
userPhoneModel: row.userPhoneModel, |
|||
userStatus: row.userStatus, |
|||
userCid: row.userCid, |
|||
userIp: row.userIp, |
|||
}; |
|||
|
|||
formApi.setValues(formValues); |
|||
modalApi.open(); |
|||
} |
|||
|
|||
// ========== 删除确认 ========== |
|||
function handleDelete(row: any) { |
|||
if (!row.id) return; |
|||
window.confirm(`确定要删除 "${row.userName}" 吗?`) && |
|||
userApi.remove(row.id).then(() => { |
|||
gridApi.reload(); |
|||
}); |
|||
} |
|||
|
|||
// ========== 查询功能 ========== |
|||
function handleSearch() { |
|||
console.log('=== 点击查询按钮 ===') |
|||
// 先重置到第一页,然后执行查询 |
|||
gridApi.query(); |
|||
} |
|||
|
|||
// ========== 重置查询 ========== |
|||
function handleReset() { |
|||
queryFormApi.resetForm(); |
|||
gridApi.reload(); |
|||
} |
|||
|
|||
</script> |
|||
|
|||
<template> |
|||
<div style="height: 100vh; padding: 16px; box-sizing: border-box;"> |
|||
<!-- 查询表单 --> |
|||
<div class="bg-card mb-4 p-4 rounded shadow"> |
|||
<h3 class="text-lg font-semibold mb-3">查询条件</h3> |
|||
<QueryForm/> |
|||
<div class="mt-3 flex gap-2"> |
|||
<button @click="handleSearch" |
|||
class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"> |
|||
🔍 查询 |
|||
</button> |
|||
<button @click="handleReset" |
|||
class="bg-gray-500 text-white px-4 py-2 rounded hover:bg-gray-600"> |
|||
🔄 重置 |
|||
</button> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- 数据表格 --> |
|||
<Grid> |
|||
<template #toolbar-tools> |
|||
<button @click="handleAdd" class="mr-2">➕ 新增</button> |
|||
<button @click="() => gridApi.reload()">🔄 刷新</button> |
|||
</template> |
|||
|
|||
<template #action="{ row }"> |
|||
<div class="flex gap-2"> |
|||
<button @click="() => handleEdit(row)" class="text-blue-500 hover:text-blue-700">✏️ 编辑 |
|||
</button> |
|||
<button @click="() => handleDelete(row)" class="text-red-500 hover:text-red-700">🗑️ 删除 |
|||
</button> |
|||
</div> |
|||
</template> |
|||
</Grid> |
|||
|
|||
<Modal :title="modalTitle"> |
|||
<Form/> |
|||
</Modal> |
|||
</div> |
|||
</template> |
|||
Loading…
Reference in new issue