v0.3.0: 单 sources 入口 + 路径后缀覆盖约定

破坏性重构。框架对外只暴露一个 `sources` glob 入口,消费侧任何 .vue
都按文件路径后缀匹配同后缀覆盖:

- shrimp/components/<name>.vue 同后缀覆盖框架内置(login/layout/topbar/ShrimpTable/...)
- shrimp/components/fields/Field<X>.vue 注册类型 <x>(首字母小写)
- shrimp/module/<key>.vue 业务模块

变更:
- 删除 customFields / moduleSources / overrideField / registerField 旧入口
- 删除 fieldOverrides Map,字段表纯靠 glob 扫描注册
- 框架内 .vue 对可覆盖 peer 改用 defineAsyncComponent(resolveVue(...))
  (login → ShrimpLangSwitch / layout → sideM/navMenu/topbar/userbar/logo /
   ShrimpTable → ShrimpEditDrawer/ShrimpFilterDrawer/...)
- views/ 全部并入 components/,App.vue 上提到 src/ 根
- LangSwitch → ShrimpLangSwitch,PowerTree → ShrimpPowerTree
- module/FieldRelativeTime.vue → components/fields/FieldRelativeTime.vue
- module/user.vue 改用 #column-updatedAt slot 替代旧的 overrideField

字段类型约定从 'field' + Name 改为 lowercase-first(FieldString → 'string'),
内置和消费侧统一。
This commit is contained in:
2026-05-25 15:05:16 +08:00
parent ac57de97cb
commit 2b33bca681
20 changed files with 165 additions and 112 deletions
+52 -11
View File
@@ -7,8 +7,7 @@ Shrimp 框架前端 —— 一种**经典解决方案**(不是框架本身),消
## 安装
```bash
# 推荐 HTTP
npm i git+http://git.zeromvc.com/wc24/shrimp-admin.git#v0.1.0
npm i git+http://git.zeromvc.com/wc24/shrimp-admin.git#v0.3.0
```
shrimp-admin 是**源码 ship**(.ts + .vue 直接发),由消费侧 Vite 编译。
@@ -20,6 +19,8 @@ shrimp-admin 是**源码 ship**(.ts + .vue 直接发),由消费侧 Vite 编译
import { createApp } from 'vue'
import { createShrimpAdmin, App } from 'shrimp-admin'
import 'shrimp-admin/style'
import zhCn from './locales/zh-cn.json'
import en from './locales/en.json'
const app = createApp(App)
app.use(createShrimpAdmin({
@@ -27,22 +28,62 @@ app.use(createShrimpAdmin({
API_URL: '/admin-api',
TIMEOUT: 10000,
},
moduleSources: import.meta.glob('./module/**/*.vue'),
customFields: import.meta.glob('./module/fields/Field*.vue'),
locales: { 'zh-cn': zhCn, en },
sources: import.meta.glob('./shrimp/**/*.vue'),
}))
app.mount('#app')
```
## 唯一约定: `sources` 路径后缀覆盖
消费侧把所有要交给框架的 .vue 通过单个 `sources` 入口注入(`import.meta.glob` 必须由消费侧自己调用,vite 静态扫描相对调用文件)。框架按**文件路径后缀**匹配,同后缀消费侧优先,内置兜底。
```
项目/
└── src/shrimp/ 消费侧目录,可任意命名
├── components/
│ ├── login.vue 覆盖框架内置 login 页
│ ├── topbar.vue 覆盖顶栏
│ └── fields/
│ └── FieldRelativeTime.vue 注册字段类型 relativeTime
└── module/
├── product.vue 业务模块,routed by moduleKey
└── user/
└── orders.vue 子模块,parent=user
```
### 框架内置 slot(可覆盖,suffix 列表)
```
components/login.vue components/404.vue components/layout.vue
components/logo.vue components/navMenu.vue components/sideM.vue
components/topbar.vue components/userbar.vue
components/ShrimpLangSwitch.vue components/ShrimpPowerTree.vue
components/ShrimpTable.vue components/ShrimpEditDrawer.vue
components/ShrimpFilterDrawer.vue components/ShrimpFilterTrigger.vue
components/ShrimpSearchInput.vue
components/fields/Field{String,Boolean,Date,Json,Number,Password,Relation,Select,Tab,Textarea,RelativeTime}.vue
```
### 字段类型注册
`components/fields/Field<X>.vue` 自动注册类型 `<x>`(首字母小写):
| 文件 | type 注册键 |
| ------------------------ | ------------------ |
| `FieldString.vue` | `string` |
| `FieldRelativeTime.vue` | `relativeTime` |
| 你自己的 `FieldMyType.vue` | `myType` |
后端 moduleInfo 里 `field.type === 'myType'` 时就用你这个组件。
### 业务模块
`components/X.vue` 命中 `moduleKey === 'X'` 的路由,自动作为内容页。子表用嵌套目录(`module/<parent>/<sub>.vue`)。
## 消费的契约
参见 shrimp-server README `## 约定` 章节。本包消费以下条件契约:
- **ShrimpTable**: list 响应 `{list,total,page,pageSize,totalPages}` / 字段 type 枚举 / populate `${key}_label`
- **ShrimpServer**: HTTP 路由 / JWT Bearer / IUserAccessor 返回的 powers
- **默认 Field*.vue**: 字段 type 必须是内置枚举之一,或通过 `customFields` 注册
## 扩展点
- `customFields` — 业务字段组件(`Field<Name>.vue`)
- `moduleSources` — 业务模块组件(`module/<key>.vue` / `module/<parent>/<sub>.vue`)
- `vueAdmin.registerField(type, comp)` / `overrideField(moduleKey, fieldKey, comp)` — 运行时也可