48 changed files with 541 additions and 2 deletions
@ -0,0 +1,100 @@ |
|||||
|
import os |
||||
|
from jinja2 import Environment, FileSystemLoader |
||||
|
from db import get_columns |
||||
|
from utils import * |
||||
|
import yaml |
||||
|
|
||||
|
env = Environment(loader=FileSystemLoader("templates/vue")) |
||||
|
def render(template, out, ctx): |
||||
|
|
||||
|
tpl = env.get_template(template) |
||||
|
content = tpl.render(**ctx) |
||||
|
|
||||
|
os.makedirs(os.path.dirname(out), exist_ok=True) |
||||
|
|
||||
|
with open(out, "w", encoding="utf-8") as f: |
||||
|
f.write(content) |
||||
|
|
||||
|
|
||||
|
def build_fields(table): |
||||
|
|
||||
|
cols = get_columns(table) |
||||
|
|
||||
|
fields = [] |
||||
|
|
||||
|
for c in cols: |
||||
|
|
||||
|
field = {} |
||||
|
|
||||
|
field["name"] = to_camel(c["column_name"]) |
||||
|
field["comment"] = c["column_comment"] |
||||
|
field["type"] = c["data_type"] |
||||
|
|
||||
|
# ⭐ 在这里调用组件解析 |
||||
|
field["component"] = parse_component(c) |
||||
|
|
||||
|
fields.append(field) |
||||
|
|
||||
|
return fields |
||||
|
def generate(table): |
||||
|
|
||||
|
with open("./config.yml", "r", encoding="utf-8") as f: |
||||
|
cfg = yaml.safe_load(f) |
||||
|
cfg = resolve_config(cfg) |
||||
|
|
||||
|
API_DIR = cfg["frontend"]["root"]+"/"+cfg["frontend"]["api"] |
||||
|
VIEW_DIR = cfg["frontend"]["root"]+"/"+cfg["frontend"]["views"] |
||||
|
ROUTER_DIR = cfg["frontend"]["root"]+"/"+cfg["frontend"]["router"] |
||||
|
MOCK_DIR = cfg["frontend"]["root"]+"/"+cfg["frontend"]["mock"] |
||||
|
|
||||
|
|
||||
|
fields = get_columns(table) |
||||
|
entity = table.replace("health_","") |
||||
|
|
||||
|
ctx = { |
||||
|
"table":table, |
||||
|
"entity":entity, |
||||
|
"fields":build_fields(table) |
||||
|
} |
||||
|
|
||||
|
render( |
||||
|
"api.ts.j2", |
||||
|
f"{API_DIR}/{entity}.ts", |
||||
|
ctx |
||||
|
) |
||||
|
|
||||
|
render( |
||||
|
"index.vue.j2", |
||||
|
f"{VIEW_DIR}/{entity}/index.vue", |
||||
|
ctx |
||||
|
) |
||||
|
|
||||
|
render( |
||||
|
"data.ts.j2", |
||||
|
f"{VIEW_DIR}/{entity}/data.ts", |
||||
|
ctx |
||||
|
) |
||||
|
|
||||
|
render( |
||||
|
"form.ts.j2", |
||||
|
f"{VIEW_DIR}/{entity}/form.ts", |
||||
|
ctx |
||||
|
) |
||||
|
|
||||
|
render( |
||||
|
"router.ts.j2", |
||||
|
f"{ROUTER_DIR}/{entity}.ts", |
||||
|
ctx |
||||
|
) |
||||
|
|
||||
|
|
||||
|
render( |
||||
|
"mock.ts.j2", |
||||
|
f"{MOCK_DIR}/{entity}.ts", |
||||
|
ctx |
||||
|
) |
||||
|
|
||||
|
|
||||
|
if __name__ == "__main__": |
||||
|
|
||||
|
generate("health_user") |
||||
@ -0,0 +1,22 @@ |
|||||
|
/** |
||||
|
* 表格列配置 |
||||
|
*/ |
||||
|
|
||||
|
import type { VxeGridProps } from '#/adapter/vxe-table'; |
||||
|
|
||||
|
export const columns: VxeGridProps['columns'] = [ |
||||
|
|
||||
|
{% for f in fields %} |
||||
|
{ |
||||
|
// 列标题 |
||||
|
title: '{{f.comment}}', |
||||
|
|
||||
|
// 对应字段 |
||||
|
field: '{{f.name}}', |
||||
|
|
||||
|
// 宽度 |
||||
|
width: 150, |
||||
|
}, |
||||
|
{% endfor %} |
||||
|
|
||||
|
]; |
||||
@ -0,0 +1,24 @@ |
|||||
|
/** |
||||
|
* 表单 schema |
||||
|
* component 类型来自 parse_component() |
||||
|
*/ |
||||
|
|
||||
|
import type { VbenFormSchema } from '#/adapter/form'; |
||||
|
|
||||
|
export const formSchema: VbenFormSchema[] = [ |
||||
|
|
||||
|
{% for f in fields %} |
||||
|
{ |
||||
|
// 字段名 |
||||
|
fieldName: '{{f.name}}', |
||||
|
|
||||
|
// label |
||||
|
label: '{{f.comment}}', |
||||
|
|
||||
|
// 自动组件 |
||||
|
component: '{{f.component}}', |
||||
|
|
||||
|
}, |
||||
|
{% endfor %} |
||||
|
|
||||
|
]; |
||||
@ -0,0 +1,64 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
|
||||
|
/** |
||||
|
* 页面主入口 |
||||
|
*/ |
||||
|
|
||||
|
import { useVbenVxeGrid } from '#/adapter/vxe-table'; |
||||
|
import { columns } from './data'; |
||||
|
import { {{entity}}Api } from '#/api/{{entity}}'; |
||||
|
|
||||
|
|
||||
|
const [Grid, gridApi] = useVbenVxeGrid({ |
||||
|
|
||||
|
columns, |
||||
|
|
||||
|
proxyConfig: { |
||||
|
|
||||
|
ajax: { |
||||
|
|
||||
|
query: async ({ page }) => { |
||||
|
|
||||
|
const res = await {{entity}}Api.page({ |
||||
|
pageNum: page.currentPage, |
||||
|
pageSize: page.pageSize, |
||||
|
}) |
||||
|
const data = res.data |
||||
|
|
||||
|
const result = { |
||||
|
items: data.records, |
||||
|
total: data.total |
||||
|
} |
||||
|
|
||||
|
console.log('=== 返回给 VxeTable 的数据 ===', result) |
||||
|
|
||||
|
return result |
||||
|
|
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
}, |
||||
|
pagerConfig: { |
||||
|
enabled: true, |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
const [Grid, gridApi] = useVbenVxeGrid({ |
||||
|
gridOptions, |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
|
||||
|
<div> |
||||
|
|
||||
|
<Grid table-title="用户列表"> |
||||
|
<template #toolbar-tools> |
||||
|
<button @click="() => gridApi.reload()">刷新</button> |
||||
|
</template> |
||||
|
</Grid> |
||||
|
|
||||
|
</div> |
||||
|
|
||||
|
</template> |
||||
@ -0,0 +1,57 @@ |
|||||
|
/** |
||||
|
* 自动生成 Mock 数据 |
||||
|
*/ |
||||
|
|
||||
|
import { MockMethod } from 'vite-plugin-mock'; |
||||
|
|
||||
|
const list = [ |
||||
|
|
||||
|
{% for i in range(10) %} |
||||
|
{ |
||||
|
{% for f in fields %} |
||||
|
{{f.name}}: '{{f.name}}_{{i}}', |
||||
|
{% endfor %} |
||||
|
}, |
||||
|
{% endfor %} |
||||
|
|
||||
|
]; |
||||
|
|
||||
|
export default [ |
||||
|
|
||||
|
{ |
||||
|
url: '/api/{{table}}/page', |
||||
|
method: 'get', |
||||
|
response: () => { |
||||
|
return { |
||||
|
code: 0, |
||||
|
data: { |
||||
|
records: list, |
||||
|
total: list.length, |
||||
|
}, |
||||
|
}; |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
{ |
||||
|
url: '/api/{{table}}', |
||||
|
method: 'post', |
||||
|
response: () => { |
||||
|
return { |
||||
|
code: 0, |
||||
|
message: 'success', |
||||
|
}; |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
{ |
||||
|
url: '/api/{{table}}/:id', |
||||
|
method: 'delete', |
||||
|
response: () => { |
||||
|
return { |
||||
|
code: 0, |
||||
|
message: 'deleted', |
||||
|
}; |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
] as MockMethod[]; |
||||
@ -0,0 +1,31 @@ |
|||||
|
/** |
||||
|
* 自动生成路由 |
||||
|
*/ |
||||
|
|
||||
|
import type { RouteRecordRaw } from 'vue-router'; |
||||
|
|
||||
|
const routes: RouteRecordRaw[] = [ |
||||
|
|
||||
|
{ |
||||
|
path: '/{{entity}}', |
||||
|
name: '{{entityComment}}', |
||||
|
meta: { |
||||
|
icon: 'ic:baseline-view-in-ar', |
||||
|
order: 1000, |
||||
|
keepAlive: true, |
||||
|
title: '{{entityComment}}', |
||||
|
}, |
||||
|
children: [ |
||||
|
{ |
||||
|
meta: { |
||||
|
title: "{{entityComment}}列表", |
||||
|
}, |
||||
|
name: '{{entity}}_List', |
||||
|
path: '/{{entity}}', |
||||
|
component: () => import('#/views/{{entity}}/index.vue'), |
||||
|
}, |
||||
|
], |
||||
|
} |
||||
|
]; |
||||
|
|
||||
|
export default routes; |
||||
@ -0,0 +1,165 @@ |
|||||
|
# 根目录结构 |
||||
|
|
||||
|
~~~shell |
||||
|
vue-vben-admin/ |
||||
|
├── apps/ # 应用目录 - 存放各个可运行的应用 |
||||
|
├── docs/ # 项目文档 |
||||
|
├── internal/ # 内部工具和配置 |
||||
|
├── packages/ # 共享包目录 |
||||
|
├── playground/ # 测试和演示环境 |
||||
|
├── scripts/ # 脚本文件 |
||||
|
├── package.json # 根目录依赖配置 |
||||
|
├── pnpm-workspace.yaml # pnpm 工作区配置 |
||||
|
└── README.md # 项目说明文档 |
||||
|
~~~ |
||||
|
|
||||
|
# packages 共享包目录,存放可复用的代码库,如 UI 组件库、工具库等。 |
||||
|
|
||||
|
~~~shell |
||||
|
packages/ |
||||
|
├── @core/ # 核心功能模块(组件、hooks、utils) |
||||
|
├── constants/ # 全局常量定义 |
||||
|
├── effects/ # 副作用处理(如表单、表格) |
||||
|
├── icons/ # 图标资源 |
||||
|
├── locales/ # 国际化资源 |
||||
|
├── preferences/ # 偏好设置 |
||||
|
├── stores/ # 状态管理(Pinia) |
||||
|
├── styles/ # 共享样式 |
||||
|
├── types/ # TypeScript 类型定义 |
||||
|
└── utils/ # 工具函数 |
||||
|
~~~~ |
||||
|
|
||||
|
# apps/web-antd 应用目录详解 |
||||
|
|
||||
|
~~~shell |
||||
|
apps/web-antd/ |
||||
|
├── src/ |
||||
|
│ ├── api/ # API 接口层 - 所有后端接口请求 |
||||
|
│ │ ├── user.ts # 用户模块接口 |
||||
|
│ │ ├── dashboard.ts # 仪表盘接口 |
||||
|
│ │ └── system/ # 系统管理模块 |
||||
|
│ │ ├── menu.ts # 菜单管理 |
||||
|
│ │ ├── role.ts # 角色管理 |
||||
|
│ │ └── user.ts # 系统用户管理 |
||||
|
│ │ |
||||
|
│ ├── assets/ # 静态资源 |
||||
|
│ │ ├── images/ # 图片资源 |
||||
|
│ │ └── styles/ # 应用级样式 |
||||
|
│ │ |
||||
|
│ ├── components/ # 公共组件 |
||||
|
│ │ ├── BasicTable.vue # 表格组件 |
||||
|
│ │ ├── BasicForm.vue # 表单组件 |
||||
|
│ │ └── BasicUpload.vue # 上传组件 |
||||
|
│ │ |
||||
|
│ ├── hooks/ # 自定义组合式函数 |
||||
|
│ │ └── useTable.ts # 表格逻辑封装 |
||||
|
│ │ |
||||
|
│ ├── layouts/ # 布局组件 |
||||
|
│ │ ├── BasicLayout.vue # 基础布局 |
||||
|
│ │ ├── Header/ # 头部组件 |
||||
|
│ │ └── Sidebar/ # 侧边栏组件 |
||||
|
│ │ |
||||
|
│ ├── locales/ # 国际化语言包 |
||||
|
│ │ ├── zh-CN.ts # 中文 |
||||
|
│ │ └── en-US.ts # 英文 |
||||
|
│ │ |
||||
|
│ ├── router/ # 路由配置 |
||||
|
│ │ ├── index.ts # 路由入口 |
||||
|
│ │ ├── guard/ # 路由守卫 |
||||
|
│ │ ├── constant.ts # 常量路由 |
||||
|
│ │ └── modules/ # 按模块划分的路由 |
||||
|
│ │ └── [module].ts # 各模块路由 |
||||
|
│ │ |
||||
|
│ ├── store/ # Pinia 状态管理 |
||||
|
│ │ ├── index.ts # 状态入口 |
||||
|
│ │ └── modules/ # 模块化状态 |
||||
|
│ │ ├── app.ts # 应用状态 |
||||
|
│ │ ├── user.ts # 用户状态 |
||||
|
│ │ └── permission.ts # 权限状态 |
||||
|
│ │ |
||||
|
│ ├── utils/ # 工具函数 |
||||
|
│ │ └── http/axios.ts # HTTP 请求封装 |
||||
|
│ │ |
||||
|
│ ├── views/ # 页面组件(核心开发目录) |
||||
|
│ │ ├── dashboard/ # 仪表盘 |
||||
|
│ │ ├── system/ # 系统管理 |
||||
|
│ │ │ ├── user/ # 用户管理模块 |
||||
|
│ │ │ │ ├── index.vue # 列表页面 |
||||
|
│ │ │ │ ├── UserDrawer.vue # 表单抽屉 |
||||
|
│ │ │ │ └── user.data.ts # 配置数据 |
||||
|
│ │ │ └── role/ # 角色管理 |
||||
|
│ │ └── demo/ # 示例页面 |
||||
|
│ │ |
||||
|
│ ├── settings/ # 项目配置 |
||||
|
│ │ ├── projectSetting.ts # 项目基础配置 |
||||
|
│ │ ├── localeSetting.ts # 多语言配置 |
||||
|
│ │ └── designSetting.ts # 主题配置 |
||||
|
│ │ |
||||
|
│ ├── design/ # 全局样式设计 |
||||
|
│ │ ├── index.less # 样式入口 |
||||
|
│ │ ├── var/ # 变量定义 |
||||
|
│ │ └── theme/ # 主题相关 |
||||
|
│ │ |
||||
|
│ ├── directives/ # 自定义指令 |
||||
|
│ │ ├── permission.ts # 权限指令 |
||||
|
│ │ └── resize.ts # 尺寸监听 |
||||
|
│ │ |
||||
|
│ ├── types/ # 本地类型定义 |
||||
|
│ │ └── global.d.ts # 全局类型 |
||||
|
│ │ |
||||
|
│ ├── main.ts # 应用入口文件 |
||||
|
│ └── App.vue # 根组件 |
||||
|
│ |
||||
|
├── index.html # HTML 模板 |
||||
|
├── package.json # 应用依赖 |
||||
|
├── vite.config.ts # Vite 配置 |
||||
|
└── .env.* # 环境变量 |
||||
|
~~~ |
||||
|
|
||||
|
三、核心文件功能说明 |
||||
|
3.1 入口文件 main.ts |
||||
|
项目的启动入口,执行 bootstrap() 函数完成以下初始化: |
||||
|
创建 Vue 应用实例 |
||||
|
配置 Pinia 状态管理 |
||||
|
注册全局组件 |
||||
|
初始化国际化(i18n) |
||||
|
配置路由并挂载路由守卫 |
||||
|
注册全局指令 |
||||
|
配置错误处理 |
||||
|
|
||||
|
3.2 路由配置 router/ |
||||
|
modules/:按业务模块划分的路由文件 |
||||
|
guard/:路由守卫,处理权限验证、页面状态等 |
||||
|
路由配置直接决定侧边栏菜单的生成 |
||||
|
|
||||
|
3.3 API 层 api/ |
||||
|
按模块组织接口请求(user.ts、system/等) |
||||
|
使用封装的 defHttp 或 requestClient 发起请求 |
||||
|
包含完整的 TypeScript 类型定义 |
||||
|
|
||||
|
3.4 视图层 views/ |
||||
|
每个业务模块通常由三个文件组成: |
||||
|
index.vue:主页面(列表页) |
||||
|
[Module]Drawer.vue:新增/编辑弹窗 |
||||
|
[module].data.ts:配置数据(表格列、表单 Schema) |
||||
|
这种拆分方式使代码更简洁,维护性更好,Git 代码冲突概率低。 |
||||
|
|
||||
|
四、配置文件说明 |
||||
|
配置文件 作用 |
||||
|
vite.config.ts Vite 构建配置(端口、代理、插件) |
||||
|
.env.development 开发环境变量 |
||||
|
.env.production 生产环境变量 |
||||
|
package.json 依赖管理和脚本命令 |
||||
|
|
||||
|
五、核心设计理念 |
||||
|
分层架构:应用层(apps)、共享层(packages)、核心层(@core)清晰分离 |
||||
|
配置与逻辑分离:如 .data.ts 存放配置,index.vue 存放逻辑 |
||||
|
按模块组织:API、路由、视图、状态都按模块划分 |
||||
|
TypeScript 优先:全项目 TypeScript,类型定义完善 |
||||
|
|
||||
|
六、开发注意事项 |
||||
|
所有页面组件必须放在 views/ 目录下 |
||||
|
路由配置中父级必须使用 LAYOUT 组件 |
||||
|
路由 name 必须全局唯一 |
||||
|
使用路径别名 /@/ 指向 src/ 目录 |
||||
|
理解这些目录结构和设计理念,您就能更好地在 Vben Admin 中进行开发和代码生成。 |
||||
@ -0,0 +1,40 @@ |
|||||
|
import { requestClient } from '#/api/request'; |
||||
|
|
||||
|
export namespace UserApi { |
||||
|
|
||||
|
/** |
||||
|
* 分页查询 |
||||
|
*/ |
||||
|
export function page(params:any){ |
||||
|
return requestClient.get('/api/user/page',{params}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 详情 |
||||
|
*/ |
||||
|
export function detail(id:number){ |
||||
|
return requestClient.get('/api/user/'+id); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 新增 |
||||
|
*/ |
||||
|
export function add(data:any){ |
||||
|
return requestClient.post('/api/user',data); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 更新 |
||||
|
*/ |
||||
|
export function update(id:number,data:any){ |
||||
|
return requestClient.put('/api/user/'+id,data); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除 |
||||
|
*/ |
||||
|
export function remove(id:number){ |
||||
|
return requestClient.delete('/api/user/'+id); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
{ |
||||
|
"title": "用户模块", |
||||
|
"antd": "Ant Design Vue", |
||||
|
"vben": { |
||||
|
"title": "项目", |
||||
|
"about": "关于", |
||||
|
"document": "文档", |
||||
|
"antdv": "Ant Design Vue 版本", |
||||
|
"antdv-next": "Antdv Next 版本", |
||||
|
"naive-ui": "Naive UI 版本", |
||||
|
"element-plus": "Element Plus 版本", |
||||
|
"tdesign": "TDesign Vue 版本" |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue