vue3-django 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -6
- package/dist/vue3-django.css +1 -1
- package/dist/vue3-django.js +3344 -2519
- package/docs/migration.md +70 -1
- package/docs/uploads.md +101 -0
- package/package.json +2 -1
- package/src/components/form/Field.vue +55 -7
- package/src/components/form/Form.vue +36 -21
- package/src/components/layout/Actions.vue +19 -7
- package/src/components/layout/Layout.vue +23 -20
- package/src/components/layout/ViewTabs.vue +14 -8
- package/src/components/media/FileUpload.vue +233 -0
- package/src/components/media/ImageUpload.vue +18 -0
- package/src/components/media/VideoCover.vue +74 -0
- package/src/components/model/Relations.vue +9 -6
- package/src/components/model/Search.vue +161 -41
- package/src/components/model/Select.vue +22 -3
- package/src/components/model/Table.vue +235 -219
- package/src/components/table/RemoteTable.vue +185 -0
- package/src/components/table/Table.vue +212 -0
- package/src/components/table/Widget.vue +18 -6
- package/src/composables/remote-table.js +80 -0
- package/src/core/search.js +86 -0
- package/src/core/upload.js +86 -0
- package/src/index.js +7 -0
- package/src/style.css +3 -6
- package/src/utils/filters.js +28 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# vue3-django
|
|
2
2
|
|
|
3
|
-
继承 `vue-django` 的理念:借鉴 Django Admin 的自动化思想,通过 DRF
|
|
3
|
+
继承 `vue-django` 的理念:借鉴 Django Admin 的自动化思想,通过 DRF 元数据和少量业务配置实现前端极简定制。当前版本为 0.1.0,使用 Vue 3 + Element Plus + Vant,渐进迁移模型 CRUD、路由、登录、布局与字段控件。
|
|
4
4
|
|
|
5
5
|
## 运行
|
|
6
6
|
|
|
@@ -16,10 +16,41 @@ npm run build:demo
|
|
|
16
16
|
|
|
17
17
|
开发入口是本地演示,使用 Axios adapter 模拟 OPTIONS、分页、POST、PATCH、行操作与 400 错误。数据只在内存保存,刷新重置;不需要访问旧项目或生产后端。切换移动端查看 Vant 表单与列表。输入已有项目名称可验证后端字段错误。
|
|
18
18
|
|
|
19
|
+
访问 [本地预览](http://127.0.0.1:5173/)。默认 mock 模式提供 demo/crm;真实 API 模式需先启动 Django(默认 `http://127.0.0.1:8000`),再运行:
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
VITE_REAL_API=true npm run dev -- --port 5173
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
真实模式登录入口为 `/#/auth/login/`,默认列表为 `/#/course/category/`。
|
|
26
|
+
Vite 将 `/api` 代理到 Django,包括用于读取元数据的 OPTIONS 请求。
|
|
27
|
+
如果端口已占用,请以终端输出的地址为准。
|
|
28
|
+
|
|
29
|
+
demo 的 `examples/demo/apps.js` 独立维护模型注册配置。真实模式已迁入原 dashboard 的
|
|
30
|
+
14 个 app、39 个 model,保留名称、图标、hidden、title_field 和动作配置。
|
|
31
|
+
这些配置不代表对应旧业务页面、动作和接口均已迁移或验收;无专用 config.js 的模型使用元数据默认视图。
|
|
32
|
+
demo 加载 Font Awesome 4 样式以显示原配置中的图标名称。
|
|
33
|
+
|
|
34
|
+
## 当前功能
|
|
35
|
+
|
|
36
|
+
- `createDjangoRouter`、`genModelRouters` 与 `createAuth`:hash 路由、登录守卫和旧版认证接口。
|
|
37
|
+
- `Layout`、`SideBar`、`ViewTabs`:菜单、独立编辑 path/tab、记录标题与图标。
|
|
38
|
+
- `Form`:通用字段、校验、提交与字段错误;登录表单和 `ModelForm` 复用该组件。
|
|
39
|
+
- `ModelTable`、`ModelForm`:元数据驱动列表与表单、新建抽屉、行双击编辑和行操作。
|
|
40
|
+
- `Table`、`RemoteTable`、`useRemoteTable`:通用展示、远程请求与分页/排序状态,可用于非模型接口。
|
|
41
|
+
- `ModelSearch`、`ModelSelect`、`ModelRelations`:搜索占位符、模型选择与关联视图。
|
|
42
|
+
- `Drawer`、`Actions`:动态内容、完成回调、按钮/更多菜单、确认与异步状态。
|
|
43
|
+
- `TableWidget`:choices、布尔、数字、日期、外键、图片、视频、JSON、HTML 和自定义渲染。
|
|
44
|
+
- `ImageUpload`、`FileUpload`、`createUploadService`:平台无关的签名直传、后端确认、进度/取消/重试;需接入后端上传接口。
|
|
45
|
+
|
|
46
|
+
上传控件接入及后端协议见 [媒体上传](docs/uploads.md)。
|
|
47
|
+
|
|
19
48
|
## 接入业务
|
|
20
49
|
|
|
21
50
|
```js
|
|
22
51
|
import { createApp } from 'vue'
|
|
52
|
+
import ElementPlus from 'element-plus'
|
|
53
|
+
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
|
23
54
|
import { createHttp, createRegistry, createDjango } from 'vue3-django'
|
|
24
55
|
import 'element-plus/dist/index.css'
|
|
25
56
|
import 'vant/lib/index.css'
|
|
@@ -35,7 +66,7 @@ const registry = createRegistry({
|
|
|
35
66
|
return load ? (await load()).default : {}
|
|
36
67
|
},
|
|
37
68
|
})
|
|
38
|
-
createApp(App).use(createDjango({ registry })).mount('#app')
|
|
69
|
+
createApp(App).use(ElementPlus, { locale: zhCn }).use(createDjango({ registry })).mount('#app')
|
|
39
70
|
```
|
|
40
71
|
|
|
41
72
|
```vue
|
|
@@ -55,7 +86,15 @@ import { ModelTable, ModelForm } from 'vue3-django'
|
|
|
55
86
|
</template>
|
|
56
87
|
```
|
|
57
88
|
|
|
58
|
-
`ModelForm` 不传 `id`
|
|
89
|
+
`ModelForm` 不传 `id` 时新增,传入时编辑;两者传入 `mobile` 切换移动界面。
|
|
90
|
+
表单支持 `v-model`、`defaults`、`items`,暴露 `load()`/`submit()`;列表暴露 `refresh()`。
|
|
91
|
+
保存事件 `form-posted` 保留 `{ model, data, intent }`。
|
|
92
|
+
|
|
93
|
+
`ModelTable` 的“新增”默认打开 Drawer,桌面宽度 66%,移动端全屏。
|
|
94
|
+
`createDefaults` 覆盖 `baseQueries` 的新建默认值,`createDrawerSize` 调整桌面宽度。
|
|
95
|
+
成功后关闭抽屉、发出 `created`(同 form-posted payload)并刷新列表;失败时保留表单。
|
|
96
|
+
`create` 表示点击新增,旧宿主若通过该事件自行导航或打开弹窗,应设置 `create-mode="event"`。
|
|
97
|
+
`edit` 仍由宿主处理;默认行双击也触发编辑,可用 `dblClickAction` 配置或空字符串禁用。
|
|
59
98
|
|
|
60
99
|
业务 `src/views/crm/customer/config.js` 保留旧式声明:
|
|
61
100
|
|
|
@@ -76,7 +115,35 @@ export default {
|
|
|
76
115
|
}
|
|
77
116
|
```
|
|
78
117
|
|
|
79
|
-
字段模板来自 OPTIONS,`items` 中的对象覆盖元数据。`create`/`update` 优先于 `form
|
|
118
|
+
字段模板来自 OPTIONS,`items` 中的对象覆盖元数据。`create`/`update` 优先于 `form`。
|
|
119
|
+
自定义字段使用 `#field-name="{ field, value, update }"`,或将 Vue 组件传给字段 `widget`;
|
|
120
|
+
桌面和移动列表均支持 `#column-name="{ row, field }"`,插槽优先于内置字段渲染。
|
|
121
|
+
|
|
122
|
+
列表 `formatter(row, fieldName, fieldValue)` 保留 0、false 和空字符串返回值。
|
|
123
|
+
组件 widget 接收整行 value/modelValue、field 和 context;旧函数 widget 使用 `(row, field)` 返回 HTML。
|
|
124
|
+
HTML 输出经过 DOMPurify 清理。`useFormWidget: true` 可在单元格使用表单字段控件,
|
|
125
|
+
变化发出 `field-change: { row, field, value }`,不会自动保存到 API。
|
|
126
|
+
|
|
127
|
+
`ModelRelations` 支持外键、多对多及通用关联配置、关联记录的新建抽屉,以及已有记录的添加/移出。
|
|
128
|
+
特殊上传控件及未覆盖的复杂业务仍由宿主插槽或自定义组件实现。
|
|
129
|
+
字段控件名称与关联配置示例见 [迁移指南](docs/migration.md)。
|
|
130
|
+
|
|
131
|
+
## 路由、布局与抽屉
|
|
132
|
+
|
|
133
|
+
宿主通过 `genModelRouters(apps, { list, create, edit })` 指定页面组件,生成列表、`add/` 和 `:id/` 路由;
|
|
134
|
+
通过 `createDjangoRouter({ routes, auth })` 启用登录守卫,默认 hash 路由。
|
|
135
|
+
公开页面设置 `meta.loginRequired: false`;`meta.layout: 'main'` 页面在 Layout 中独立展示。
|
|
136
|
+
demo 的编辑页每条记录使用独立 path/tab,标题优先采用记录 `__str__`,并支持模型 icon。
|
|
137
|
+
新建默认不打开 tab,已有 `add/` 页面路径继续兼容。
|
|
138
|
+
|
|
139
|
+
`genMenusFromApps` 生成菜单,`Layout` 接入菜单、用户、退出事件及可选标题栏 actions。
|
|
140
|
+
图标支持 emoji、Vue 组件及 Font Awesome 类名;宿主使用字体图标时需自行加载相应样式。
|
|
141
|
+
|
|
142
|
+
布局子页面可使用 `useDrawer().open({ component, context, onDone })` 打开抽屉,
|
|
143
|
+
内容组件 emit `done(result)` 后关闭并回调。字符串组件需要宿主配置 `loadDrawerView`,
|
|
144
|
+
可用 `createDrawerViewLoader(import.meta.glob('./views/**/*.vue'))` 创建加载器。
|
|
145
|
+
`Actions` 的函数 `do(context)` 执行业务操作,字符串/组件 `do` 打开抽屉;
|
|
146
|
+
嵌套数组项进入“更多”菜单。详细配置见 [迁移指南](docs/migration.md)。
|
|
80
147
|
|
|
81
148
|
## DRF 约定
|
|
82
149
|
|
|
@@ -87,6 +154,14 @@ export default {
|
|
|
87
154
|
- choice 保留真实 value 类型;布尔默认值延续旧版 true,可用 OPTIONS `default` 或 `defaults` 覆盖。
|
|
88
155
|
- `400` 映射为字段错误和 `non_field_errors`,HTTP 错误保留 `code/msg`,网络错误 code=-1。
|
|
89
156
|
- Session 认证保留 `csrftoken`/`X-CSRFToken`,后端需先设置 CSRF cookie;也支持 `http.setAuthToken(token)`,传空值清除 Token。
|
|
90
|
-
- 跨域凭证、CORS 与 CSRF trusted origins 由业务环境配置;`createHttp` 可传 Axios
|
|
157
|
+
- 跨域凭证、CORS 与 CSRF trusted origins 由业务环境配置;`createHttp` 可传 Axios 配置,不自动获取 CSRF cookie,也不持久保存 Token。
|
|
158
|
+
- 可选 `createAuth` 沿用 `auth/user/login/`、`auth/user/current/`、`auth/user/logout/`;JWT 使用 `token.access` 和 Bearer 请求头,并通过 `access_token` cookie 保留 Token,也支持 Session。demo 登录页可记忆用户名,但不保存密码。
|
|
159
|
+
|
|
160
|
+
## 尚未覆盖
|
|
161
|
+
|
|
162
|
+
这仍是渐进迁移版本,不是旧组件的完整替代。旧业务动作页面和批量操作尚未完整迁移;
|
|
163
|
+
基础图片/文件上传已实现,云存储签名及确认接口、大文件分片/断点续传、视频转码/VOD 尚未接通;
|
|
164
|
+
复杂分组列(subColumns/rows/headerWidget)尚未支持;Date2Now 当前显示完整本地时间,未实现旧版相对时间文案。
|
|
165
|
+
测试与构建通过不等于所有真实后端模型、权限和业务动作已经验收。
|
|
91
166
|
|
|
92
|
-
|
|
167
|
+
历史迭代范围见 [迭代计划](docs/iterations.md),当前接入约定与兼容差异见 [迁移指南](docs/migration.md)。
|
package/dist/vue3-django.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.vd-toolbar{display:flex;align-items:center;gap:
|
|
1
|
+
.vd-toolbar{display:flex;align-items:center;gap:8px;margin:0 0 8px}.vd-toolbar h2{flex:1;margin:0;font-size:16px}.vd-toolbar .el-input{max-width:360px}.vd-card,.el-alert{margin-bottom:16px}.vd-upload[data-v-777ec908]{display:flex;flex-wrap:wrap;gap:8px}.vd-upload-item[data-v-777ec908]{display:flex;align-items:center;flex-wrap:wrap;gap:6px;max-width:100%}.vd-upload-item img[data-v-777ec908]{width:72px;height:72px;object-fit:contain}.vd-upload-item a[data-v-777ec908]{overflow-wrap:anywhere}.vd-upload button[data-v-777ec908]{cursor:pointer;border:1px solid #dcdfe6;border-radius:4px;background:#fff;padding:5px 10px;color:#409eff}.vd-cover-placeholder[data-v-52989853]{display:inline-flex;align-items:center;justify-content:center;min-height:32px;color:#909399;font-size:12px}.model-icon[data-v-5a104f44]{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:1.25em;height:1.25em;margin-right:8px;vertical-align:middle}.vd-model-search[data-v-d6cb8ac9]{display:flex;flex-wrap:wrap;align-items:flex-start;gap:4px;margin-bottom:4px}.vd-search-field[data-v-d6cb8ac9]{flex:0 1 var(--search-field-width, 8rem);width:var(--search-field-width, 8rem);min-width:0;max-width:100%}.vd-search-field[data-v-d6cb8ac9] .el-input,.vd-search-field[data-v-d6cb8ac9] .el-select,.vd-search-field[data-v-d6cb8ac9] .el-input-number,.vd-search-field[data-v-d6cb8ac9] .el-date-editor{width:100%;min-width:0}.vd-search-range[data-v-d6cb8ac9]{display:flex;align-items:center;gap:4px}.foreignkey-link[data-v-72c5023a]{color:var(--el-color-primary, #409eff);text-decoration:none}.foreignkey-link[data-v-72c5023a]:hover{text-decoration:underline}.vd-table-text[data-v-8faa9aa1]{white-space:pre-wrap;overflow-wrap:anywhere}.vd-table-boolean.is-true[data-v-8faa9aa1]{color:var(--el-color-success, green);font-weight:700}.vd-table-images[data-v-8faa9aa1]{display:flex;flex-wrap:wrap;gap:4px}.vd-table-video[data-v-8faa9aa1]{width:180px;max-width:100%}.vd-table-json[data-v-8faa9aa1]{display:grid;grid-template-columns:auto 1fr;gap:4px 12px;margin:0}.vd-table-json dd[data-v-8faa9aa1]{margin:0}.vd-table-actions[data-v-f224bc95]{display:flex;align-items:center;justify-content:flex-end;gap:8px;margin-bottom:8px}.vd-table-pagination[data-v-5eef5cff]{padding:10px 0 6px}.vd-table-pagination[data-v-5eef5cff] .el-pagination{display:flex;flex-wrap:wrap;justify-content:flex-start;row-gap:8px}.vd-table-pagination.is-mobile[data-v-5eef5cff]{padding:12px 0}@media(max-width:600px){.vd-table-pagination[data-v-5eef5cff] .el-pagination__jump{margin-left:0}}.viewtabs[data-v-3064a066]{margin:0 8px}.viewtabs[data-v-3064a066] .el-tabs__header{margin-bottom:10px}.viewtabs[data-v-3064a066] .el-tabs__item{font-size:.8rem;height:36px;padding:0 16px}.tab-icon[data-v-3064a066]{width:1em;height:1em;margin-right:6px}.django-layout[data-v-43dec436]{min-height:100vh;background:#fff;color:#253447}.layout-header[data-v-43dec436]{height:50px;padding:0 16px;display:flex;align-items:center;gap:12px;background:#fff;border-bottom:1px solid #e5eaf0}.layout-brand[data-v-43dec436]{font-size:16px;font-weight:700;color:inherit;text-decoration:none}.layout-account[data-v-43dec436]{margin-left:auto;display:flex;align-items:center;gap:16px}.layout-body[data-v-43dec436]{display:flex;min-height:calc(100vh - 51px)}.layout-sidebar[data-v-43dec436]{width:190px;flex-shrink:0;background:#fff;border-right:1px solid #e5eaf0}.layout-sidebar[data-v-43dec436] .el-menu{border-right:0;--el-menu-item-height: 42px;--el-menu-sub-item-height: 38px}.layout-content[data-v-43dec436]{min-width:0;flex:1;padding-top:0}.menu-toggle[data-v-43dec436],.menu-backdrop[data-v-43dec436]{display:none}.layout-content[data-v-43dec436] .viewtabs{margin:0 8px}@media(max-width:768px){.menu-toggle[data-v-43dec436]{display:inline-flex}.layout-header[data-v-43dec436]{padding:0 12px}.layout-brand[data-v-43dec436]{font-size:16px}.layout-sidebar[data-v-43dec436]{display:none}.layout-sidebar.opened[data-v-43dec436]{display:block;position:fixed;top:51px;bottom:0;left:0;z-index:21;overflow:auto}.menu-backdrop[data-v-43dec436]{display:block;position:fixed;inset:51px 0 0;background:#0006;border:0;z-index:20}.layout-content[data-v-43dec436] .viewtabs{margin:0 8px}}
|