vue3-django 0.1.0
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 +92 -0
- package/dist/vue3-django.css +1 -0
- package/dist/vue3-django.js +4439 -0
- package/docs/iteration-01-preview.jpg +0 -0
- package/docs/iterations.md +37 -0
- package/docs/migration.md +133 -0
- package/docs/verification.md +25 -0
- package/package.json +53 -0
- package/src/components/form/Field.vue +113 -0
- package/src/components/form/Form.js +49 -0
- package/src/components/form/Form.vue +285 -0
- package/src/components/layout/Actions.vue +132 -0
- package/src/components/layout/Drawer.vue +87 -0
- package/src/components/layout/Layout.vue +193 -0
- package/src/components/layout/SideBar.vue +57 -0
- package/src/components/layout/ViewTabs.vue +136 -0
- package/src/components/mobile/Form.vue +16 -0
- package/src/components/model/Create.vue +20 -0
- package/src/components/model/Form.vue +136 -0
- package/src/components/model/Relations.vue +236 -0
- package/src/components/model/Search.vue +103 -0
- package/src/components/model/Select.vue +91 -0
- package/src/components/model/Table.vue +335 -0
- package/src/components/table/Widget.vue +195 -0
- package/src/components/widgets/ForeignKey.vue +58 -0
- package/src/components/widgets/Icon.vue +37 -0
- package/src/composables/context.js +16 -0
- package/src/composables/drawer.js +19 -0
- package/src/composables/tab.js +7 -0
- package/src/configs/axios.js +2 -0
- package/src/core/auth.js +68 -0
- package/src/core/http.js +52 -0
- package/src/core/menus.js +39 -0
- package/src/core/metadata.js +50 -0
- package/src/core/registry.js +127 -0
- package/src/core/relations.js +74 -0
- package/src/core/table.js +48 -0
- package/src/core/views.js +23 -0
- package/src/index.js +31 -0
- package/src/router/index.js +78 -0
- package/src/style.css +23 -0
- package/src/utils/app_model.js +2 -0
- package/src/utils/array_normalize.js +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# vue3-django
|
|
2
|
+
|
|
3
|
+
继承 `vue-django` 的理念:借鉴 Django Admin 的自动化思想,通过 DRF 元数据和少量业务配置实现前端极简定制。第一轮版本为 0.1.0,提供 Vue 3 + Element Plus + Vant 的模型 CRUD 基础链路。
|
|
4
|
+
|
|
5
|
+
## 运行
|
|
6
|
+
|
|
7
|
+
Node >= 22.12,执行:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install
|
|
11
|
+
npm run dev
|
|
12
|
+
npm test
|
|
13
|
+
npm run build
|
|
14
|
+
npm run build:demo
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
开发入口是本地演示,使用 Axios adapter 模拟 OPTIONS、分页、POST、PATCH、行操作与 400 错误。数据只在内存保存,刷新重置;不需要访问旧项目或生产后端。切换移动端查看 Vant 表单与列表。输入已有项目名称可验证后端字段错误。
|
|
18
|
+
|
|
19
|
+
## 接入业务
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
import { createApp } from 'vue'
|
|
23
|
+
import { createHttp, createRegistry, createDjango } from 'vue3-django'
|
|
24
|
+
import 'element-plus/dist/index.css'
|
|
25
|
+
import 'vant/lib/index.css'
|
|
26
|
+
import 'vue3-django/style.css'
|
|
27
|
+
import App from './App.vue'
|
|
28
|
+
|
|
29
|
+
const modules = import.meta.glob('/src/views/**/config.js')
|
|
30
|
+
const registry = createRegistry({
|
|
31
|
+
http: createHttp({ baseURL: '/api/' }),
|
|
32
|
+
apps: { crm: { models: { customer: { verbose_name: '客户' } } } },
|
|
33
|
+
loadViewsConfig: async (fullName) => {
|
|
34
|
+
const load = modules[`/src/views/${fullName.replace('.', '/')}/config.js`]
|
|
35
|
+
return load ? (await load()).default : {}
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
createApp(App).use(createDjango({ registry })).mount('#app')
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```vue
|
|
42
|
+
<script setup>
|
|
43
|
+
import { ModelTable, ModelForm } from 'vue3-django'
|
|
44
|
+
</script>
|
|
45
|
+
<template>
|
|
46
|
+
<ModelTable
|
|
47
|
+
app-model="crm.customer"
|
|
48
|
+
@edit="(row) => console.log(row)"
|
|
49
|
+
/>
|
|
50
|
+
<ModelForm
|
|
51
|
+
app-model="crm.customer"
|
|
52
|
+
:id="123"
|
|
53
|
+
@form-posted="console.log"
|
|
54
|
+
/>
|
|
55
|
+
</template>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`ModelForm` 不传 `id` 时新增,传入时编辑;`ModelTable` 发出 `create`/`edit`,由宿主决定路由或弹窗。两者传入 `mobile` 切换 Vant;表单支持 `v-model`、`defaults`、`items`,暴露 `load()`/`submit()`;列表暴露 `refresh()`。保存事件保留 `{ model, data, intent }`。
|
|
59
|
+
|
|
60
|
+
业务 `src/views/crm/customer/config.js` 保留旧式声明:
|
|
61
|
+
|
|
62
|
+
```js
|
|
63
|
+
export default {
|
|
64
|
+
list: {
|
|
65
|
+
items: ['id', 'name', 'is_active'],
|
|
66
|
+
options: {
|
|
67
|
+
remoteTable: {
|
|
68
|
+
rowActions: [
|
|
69
|
+
{ name: 'disable', label: '禁用', api: 'disable' },
|
|
70
|
+
{ name: 'inspect', label: '查看', do: ({ row, model }) => console.log(row, model) },
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
form: { items: ['name', { name: 'is_active', label: '启用' }] },
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
字段模板来自 OPTIONS,`items` 中的对象覆盖元数据。`create`/`update` 优先于 `form`。自定义字段使用 `#field-name="{ field, value, update }"`,或将 Vue 组件传给字段 `widget`;桌面列表支持 `#column-name="{ row, field }"`。复杂关系、JSON、上传组件应先通过插槽在业务侧提供,后续逐步收敛。
|
|
80
|
+
|
|
81
|
+
## DRF 约定
|
|
82
|
+
|
|
83
|
+
- 模型标识 `app.model`;接口相对于 `baseURL` 为 `app/model/`,可通过模型 `url` 覆盖。
|
|
84
|
+
- OPTIONS 读取 `actions.LIST` + `actions.POST`,编辑优先读取 `actions.PATCH`,回退 POST;仅标准 DRF OPTIONS 也可以运行。
|
|
85
|
+
- 查询保留 `search`、`page`、`page_size` 和业务 `baseQueries`,数组以逗号序列化。首轮分页支持 `count/results`,不分页支持数组;暂不支持 cursor/limit-offset。
|
|
86
|
+
- 新增 POST、编辑 PATCH,删除 DELETE;只读和未声明字段不提交。默认假设主键名 `id`,模型 `idField` 可定制列表行键,编辑页由宿主显式传入 `id`。
|
|
87
|
+
- choice 保留真实 value 类型;布尔默认值延续旧版 true,可用 OPTIONS `default` 或 `defaults` 覆盖。
|
|
88
|
+
- `400` 映射为字段错误和 `non_field_errors`,HTTP 错误保留 `code/msg`,网络错误 code=-1。
|
|
89
|
+
- Session 认证保留 `csrftoken`/`X-CSRFToken`,后端需先设置 CSRF cookie;也支持 `http.setAuthToken(token)`,传空值清除 Token。
|
|
90
|
+
- 跨域凭证、CORS 与 CSRF trusted origins 由业务环境配置;`createHttp` 可传 Axios 配置。框架不自动获取 CSRF cookie,不写认证信息到持久存储。
|
|
91
|
+
|
|
92
|
+
这是渐进迁移的第一轮基础,不是旧组件的完整替代。范围、验收和缺口见 [迭代计划](docs/iterations.md),业务迁移方式见 [迁移指南](docs/migration.md)。
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.vd-toolbar{display:flex;align-items:center;gap:12px;margin:16px 0}.vd-toolbar h2{flex:1;margin:0;font-size:20px}.vd-toolbar .el-input{max-width:360px}.vd-card{margin-bottom:16px}.el-pagination{margin-top:20px}.el-alert{margin-bottom:16px}.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-6281d15c]{white-space:pre-wrap;overflow-wrap:anywhere}.vd-table-boolean.is-true[data-v-6281d15c]{color:var(--el-color-success, green);font-weight:700}.vd-table-images[data-v-6281d15c]{display:flex;flex-wrap:wrap;gap:4px}.vd-table-video[data-v-6281d15c]{width:180px;max-width:100%}.vd-table-json[data-v-6281d15c]{display:grid;grid-template-columns:auto 1fr;gap:4px 12px;margin:0}.vd-table-json dd[data-v-6281d15c]{margin:0}.viewtabs[data-v-5d737ee7]{margin:0 24px}.viewtabs[data-v-5d737ee7] .el-tabs__item{font-size:.8rem}.tab-icon[data-v-5d737ee7]{width:1em;height:1em;margin-right:6px}.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}.django-layout[data-v-d75e30ad]{min-height:100vh;background:#f4f7fa;color:#253447}.layout-header[data-v-d75e30ad]{height:64px;padding:0 24px;display:flex;align-items:center;gap:16px;background:#fff;border-bottom:1px solid #e5eaf0}.layout-brand[data-v-d75e30ad]{font-size:20px;font-weight:700;color:inherit;text-decoration:none}.layout-account[data-v-d75e30ad]{margin-left:auto;display:flex;align-items:center;gap:16px}.layout-body[data-v-d75e30ad]{display:flex;min-height:calc(100vh - 65px)}.layout-sidebar[data-v-d75e30ad]{width:220px;flex-shrink:0;background:#fff;border-right:1px solid #e5eaf0}.layout-sidebar[data-v-d75e30ad] .el-menu{border-right:0}.layout-content[data-v-d75e30ad]{min-width:0;flex:1;padding-top:20px}.menu-toggle[data-v-d75e30ad],.menu-backdrop[data-v-d75e30ad]{display:none}.layout-content[data-v-d75e30ad] main{margin:24px auto}.layout-content[data-v-d75e30ad] .viewtabs{margin:0 20px}@media(max-width:768px){.menu-toggle[data-v-d75e30ad]{display:inline-flex}.layout-header[data-v-d75e30ad]{padding:0 12px}.layout-brand[data-v-d75e30ad]{font-size:16px}.layout-sidebar[data-v-d75e30ad]{display:none}.layout-sidebar.opened[data-v-d75e30ad]{display:block;position:fixed;top:65px;bottom:0;left:0;z-index:21;overflow:auto}.menu-backdrop[data-v-d75e30ad]{display:block;position:fixed;inset:65px 0 0;background:#0006;border:0;z-index:20}.layout-content[data-v-d75e30ad] .viewtabs{margin:0 8px}}
|