本文件为 AGENTS 在当前代码库中工作时提供指导。
后端:PHP8 + ThinkPHP8 + MySQL 前端:Vue3 + Element Plus + TypeScript + Vite + Pinia + Axios
- 回复使用中文
- 遇到有多种实现方案时,列出选项让我选择,而不是直接选一种
- 源代码文件:统一采用 UTF-8(无 BOM)编码;换行符统一使用 LF(\n);文件末尾保留一个空行,行尾禁止空白空格
- 重写继承的后台 CRUD 方法:将
app/admin/library/traits/Backend.php中的方法复制到目标控制器后进行改写,不要直接编辑 trait。
- 安装依赖:
composer install - 开发服务器:
php think run-> 启动http://localhost:8000(前端开发环境请求该地址)。 - 数据库迁移(通过
think-migration集成的Phinx):php think migrate:run命令可应用全部待执行的迁移。
- 安装依赖:
pnpm install - 开发服务:
pnpm dev-> 执行esno ./src/utils/dev.ts,该脚本会启动vite --force。开发服务器端口为1818(见web/.env)。 - 构建:
pnpm build->vite build,构建产物输出到web/dist;构建产物需要手动移动到public/以供生产环境使用。 - 代码检查:
pnpm lint/pnpm lint-fix(ESLint flat config)。格式化:pnpm format(Prettier)。类型检查:pnpm typecheck(vue-tsc --noEmit)。
- 采用多应用模式(
topthink/think-multi-app)。应用包括:admin(后台管理)、api(会员端 API + 安装器)、common(共享基类,不可通过 Web 访问,已列入deny_app_list)。 - 入口
public/index.php会区分前端请求(返回已编译的public/index.html)与 API 请求(通过server参数/头、index.phpURI 前缀或 OPTIONS 请求判定)。在常驻内存运行时(Workerman 模块)下不走本文件。
app/BaseController.php- ThinkPHP 框架基类。在构造方法中自动将controllerPath写入 Request(将.替换为/,如auth.Admin->auth/Admin),并调用initialize()。app/common/controller/Api.php- 继承BaseController。通过success()/error()/result()返回 JSON,这些方法均抛出HttpResponseException。统一响应结构:{code, msg, time, data};code == 1表示成功。initialize()中会检查数据库连接、执行ip_check()与时区设定,并加载控制器语言包。app/common/controller/Backend.php(继承Api)- 所有后台控制器的基类。初始化后台鉴权(app\admin\library\Auth),读取batoken请求头,并通过$noNeedLogin/$noNeedPermission数组强制登录与 RBAC 校验。权限校验的键为controllerPath/action。它use了Backendtrait(app/admin/library/traits/Backend.php),该 trait 提供了默认的index / add / edit / del / sortable / select方法,完全由$this->model及一组可配置属性驱动($preExcludeFields、$quickSearchField、$dataLimit、$dataLimitField、$defaultSortField、$orderGuarantee、$indexField、$withJoinTable、$modelValidate、$modelSceneValidate)。app/common/controller/Frontend.php(继承Api)- 会员端控制器基类;使用app\common\library\Auth与ba-user-token请求头。
一个典型的后台控制器(如 app/admin/controller/auth/Admin.php)只需在 initialize() 中设置 $model,并重写需要自定义逻辑的 CRUD 方法,其余均继承自基类。Backend::queryBuilder() 会将前端的 search / quickSearch / order / limit / initKey 参数转换为 ThinkPHP 的 where / order / paginate;getDataLimitAdminIds() 负责数据权限范围过滤(personal / allAuth / allAuthAndOthers / parent / 分组 id)。
- 全局(
app/middleware.php):Throttle(限流,配置文件位于config/throttle.php)。 - Admin 应用(
app/admin/middleware.php):AllowCrossDomain→AdminLog→LoadLangPack。 - API 应用(
app/api/middleware.php):AllowCrossDomain→LoadLangPack。 AllowCrossDomain(app/common/middleware/AllowCrossDomain.php):根据buildadmin.cors_request_domain配置动态设置Access-Control-Allow-Origin。AdminLog(app/common/middleware/AdminLog.php):当auto_write_admin_log开启时,自动记录所有 POST/DELETE 请求到AdminLogModel。- 添加应用时建议首先添加
AllowCrossDomain中间件,否则前端请求将报跨域。
- 系统有可视化CRUD功能,它是
app/admin/library/crud/Helper.php和app/admin/controller/crud/Crud.php根据后台 UI 中的图形化拖拽设计器提供的数据,生成:控制器 + 模型 + 验证器 + 视图 + 语言包文件,创建数据表,并写入admin_rule菜单/权限节点。 - 样板代码文件位于
app/admin/library/stubs/与app/admin/library/crud/stubs/。生成的控制器继承Backend,因此立即具备完整 CRUD 能力;生成完毕后会通过 WEB 终端自动对前端代码执行 prettier。 - CRUD 生成器生成的内容包括(
path/name是前端自定义的生成路径):写入到admin_rule表的菜单/权限节点、app/admin/controller/path/name.php(控制器)、app/admin/model/path/name.php(模型,还可以生成到app/common/model目录),app/admin/validate/path/name.php(验证器),src\lang\backend\{lang}\path/name.ts(前端语言包,默认有 en 和 zh-cn 两种语言支持),src\views\backend\path\name\index.vue(路由入口文件、CRUD表格组件),src\views\backend\test\popupForm.vue(表格表单组件)
- 辅助函数位于
app/common.php:__()(多语言)、get_sys_config()、full_url()、get_auth_token()、filter()/clean_xss()、hash_password()/verify_password()、action_in_arr()、get_route_remark()。 - 核心库类集位于
extend/ba/(通过"" => "extend/"以PSR-0自动加载):Auth、Captcha、ClickCaptcha、Filesystem、TableManager、Terminal、Tree、Version、Random、Depends。 - 模型位于
app/admin/model/与app/common/model/;验证器通过将类名中的\model\替换为\validate\自动发现。 - 迁移文件位于
database/migrations/(一个install迁移 + 各版本迁移)。 - 配置文件全部位于
config/目录。
/@/→src/(tsconfigpaths与 viteresolve.alias一致)。- 入口
src/main.ts:注册 pinia(带pinia-plugin-persistedstate)、router、element-plus、i18n、全局 icon。 - 路由用
hash模式(createWebHashHistory)。静态路由位于src/router/static.ts(自动加载./static/*.ts以获取adminBase与memberCenterBase基础路由)。菜单路由从后端拉取,并由src/utils/router.ts(addRouteAll/addRouteItem)在运行时注册,通过import.meta.glob('/src/views/{backend,frontend}/**/*.vue')匹配组件。权限节点被组装进authNodeMap,供auth()公共函数使用。
Pinia stores 全部位于 src/stores/,通过 pinia-plugin-persistedstate 持久化:
adminInfo— 管理员 token 与基本信息。userInfo— 会员 token 与基本信息。siteConfig— 站点全局配置(站点名、CDN、上传配置、备案号等),从后端加载。config— 布局/主题/语言配置。包含完整的布局参数(多种布局模式、菜单颜色/宽度、暗黑模式、顶栏配置等),通过getColorVal()根据isDark返回配色数组中的对应值。navTabs— 后台标签页管理。维护tabsView(已打开的标签)、tabsViewRoutes(菜单路由树)、childrenMenus(次级菜单)、authNode(权限节点 Map)。memberCenter— 会员中心的路由和权限节点。terminal— WEB 终端相关状态。
src/utils/axios.ts是自定义的 axios 封装。它会自动注入batoken(后台)/ba-user-token(会员)请求头,在收到code === 409时调用refreshToken并将进行中的请求排队重放,并根据code === 302(重定向)/code === 303(需登录 -> 清除 token 并跳转登录页)进行路由。重复请求会被自动取消。VITE_AXIOS_BASE_URL为后端地址(开发环境http://localhost:8000,生产环境getCurrentDomain)。当后台入口路径非默认时,axios 会将/admin/...重写为{path}.php/...。- 新增
API请求函数放src/api/,参考src\api\common.ts。
src\components\table 基于 Element Plus el-table 封装,需与 src\utils\baTable.ts 配合使用。
baTable.ts:表格管家类,它是表格数据管理器,其中封装好了一个表格应有的 属性 和 方法,预设了 查看前、查看后、编辑前 等钩子,在需要表格的组件中实例化后,provide给子组件index.vue:表格主体,遍历baTable.table.column渲染列,支持slot自定义渲染header/index.vue:表头工具栏,支持刷新、编辑、删除、公共搜索、快速搜索、列显示等按钮comSearch/index.vue:公共搜索组件,支持多字段组合条件搜索types\table.d.ts:表格类型定义(表格列可用属性定义、各种操作前后钩子、公共搜索可用操作符号、表格表单数据等)fieldRender/:单元格渲染器,一个文件等于一个渲染器,使用列配置的render属性指定渲染器(buttons,color,customRender,customTemplate,datetime,icon,image,images,switch,tag,tags,url,slot)
baTable 列配置(column 数组)关键属性:
| 属性 | 说明 |
|---|---|
prop |
字段名,支持 . 嵌套(如 user.nickname |
label |
列标题 |
render |
渲染器类型(image、tag、switch、slot 等) |
operator |
搜索操作符(eq、LIKE、RANGE、IN 等) |
formatter |
自定义格式化函数 |
showOverflowTooltip |
超出显示 tooltip |
我们封装了 src\components\baInput\index.vue 输入组件和 src\components\formItem\index.vue 表单项组件,都可以通过传递 type 属性渲染对应的输入框,如:<FormItem label="array" type="array" v-model="state.array" />,支持的所有 type 可以查阅:src\components\baInput\index.ts 中的 inputTypes
语言包位于 src/lang/——globs-{locale}.ts(全局根层)、common/{locale}/(公共语言包,总是加载)、backend/{locale}/(后台语言包,仅后台加) + backend/{locale}.ts、frontend/{locale}/(前台语言包,仅前台加载) + frontend/{locale}.ts。关键流程:
- 初始化(
lang/index.ts的loadLang()):加载 Element Plus 语言包 + 全局globs-{locale}.ts+ 所有common/{locale}/下的语言包文件(eager import),合并后创建 i18n 实例。同时将backend/{locale}/**和frontend/{locale}/**的 glob 句柄存入window.loadLangHandle,供后续按需加载。 - 按需加载(
router/index.ts的beforeEach):每次路由切换时,根据langAutoLoadMap(src/lang/autoload.ts)的静态映射 + 路由 path 推导的命名空间,调用loadAndMergeMessages()异步加载对应语言包文件并合并到 i18n 实例。
src/styles/ 使用 SCSS 管理样式,入口 index.scss。
var.scss:CSS 变量定义(--ba*前缀)element.scss:Element Plus 组件样式覆盖dark.scss:暗黑模式样式loading.scss:全局加载样式app.scss:应用基础样式
总是优先使用 element plus 的 --el 开头和框架自定义的 --ba 开头的 CSS 变量。
utils/common.ts 中的 auth() 函数根据当前路由 path 在 authNode Map 中查找权限节点;也可以提供路由的 name 对象进行鉴权。
src/utils/ 存放通用工具函数。
axios.ts:axios 封装common.ts:通用工具(fullURL资源地址拼接、auth鉴权等)router.ts:路由工具(handleAdminRoute动态路由注册、openMenu菜单打开、菜单处理、权限节点组装)layout.ts:布局工具(mainHeight计算内容区高度、setNavTabsWidth设置导航标签宽度)storage.ts:本地存储封装(localStorage和sessionStorage)validate.ts:表单验证规则random.ts:随机数生成horizontalScroll.ts:横向滚动辅助