vue3-django 0.1.4 → 0.1.5
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 +73 -8
- package/dist/vue3-django.css +1 -1
- package/dist/vue3-django.js +3020 -2718
- package/docs/migration.md +2 -2
- package/package.json +7 -3
- package/src/app/index.js +1 -1
- package/src/components/generic/ForeignKey.vue +92 -0
- package/src/components/layout/Actions.vue +65 -15
- package/src/components/layout/BatchActions.vue +89 -0
- package/src/components/layout/Layout.vue +92 -19
- package/src/components/layout/ViewTabs.vue +0 -5
- package/src/components/model/Create.vue +0 -2
- package/src/components/model/Form.vue +2 -8
- package/src/components/model/Relations.vue +1 -5
- package/src/components/model/Table.vue +81 -28
- package/src/components/table/RemoteTable.vue +48 -34
- package/src/components/table/Table.vue +52 -4
- package/src/components/table/Widget.vue +23 -3
- package/src/core/content-types.js +26 -0
- package/src/core/table.js +34 -4
- package/src/index.js +3 -1
- package/src/style.css +0 -1
- package/src/views/model/edit.vue +1 -3
- package/src/views/model/list.vue +1 -2
- package/src/components/mobile/Form.vue +0 -16
package/README.md
CHANGED
|
@@ -9,12 +9,14 @@ Node >= 22.12,执行:
|
|
|
9
9
|
```sh
|
|
10
10
|
npm install
|
|
11
11
|
npm run dev
|
|
12
|
+
npm run dev:mobile
|
|
12
13
|
npm test
|
|
13
14
|
npm run build
|
|
14
15
|
npm run build:demo
|
|
16
|
+
npm run build:mobile
|
|
15
17
|
```
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
开发入口是 PC 模型界面的本地演示,使用 Axios adapter 模拟 OPTIONS、分页、POST、PATCH、行操作与 400 错误。数据只在内存保存,刷新重置;不需要访问旧项目或生产后端。输入已有项目名称可验证后端字段错误。
|
|
18
20
|
|
|
19
21
|
访问 [本地预览](http://127.0.0.1:5173/)。默认 mock 模式提供 demo/crm;真实 API 模式需先启动 Django(默认 `http://127.0.0.1:8000`),再运行:
|
|
20
22
|
|
|
@@ -26,7 +28,16 @@ VITE_REAL_API=true npm run dev -- --port 5173
|
|
|
26
28
|
Vite 将 `/api` 代理到 Django,包括用于读取元数据的 OPTIONS 请求。
|
|
27
29
|
如果端口已占用,请以终端输出的地址为准。
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
## 两个独立示例项目
|
|
32
|
+
|
|
33
|
+
- `examples/dashboard`:PC 管理后台,使用默认布局、登录、sidebar、tabs 和模型列表/表单;可切换真实 Django API。
|
|
34
|
+
- `examples/mobile`:独立 Vant 应用,使用通用 Form、RemoteTable,自有路由和内存任务数据;不复用 dashboard 页面,不连接真实 API。
|
|
35
|
+
|
|
36
|
+
先在仓库根目录安装依赖。`npm run dev:dashboard` / `npm run dev:mobile` 分别启动两个示例;也可进入对应目录运行 `npm run dev`。两者各自提供 package.json、index.html、main.js 与 Vite 配置,共享仓库依赖和库源码。
|
|
37
|
+
`npm run build:dashboard` / `npm run build:mobile` 分别输出 dist-dashboard / dist-mobile。`npm run dev` 和 `npm run build:demo` 保留为 dashboard 的兼容命令。
|
|
38
|
+
移动示例仅用于展示通用控件能力,不通过 mobile 开关切换管理后台。
|
|
39
|
+
|
|
40
|
+
dashboard 的 `examples/dashboard/apps.js` 独立维护模型注册配置。真实模式已迁入原 dashboard 的
|
|
30
41
|
14 个 app、39 个 model,保留名称、图标、hidden、title_field 和动作配置。
|
|
31
42
|
这些配置不代表对应旧业务页面、动作和接口均已迁移或验收;无专用 config.js 的模型使用元数据默认视图。
|
|
32
43
|
demo 加载 Font Awesome 4 样式以显示原配置中的图标名称。
|
|
@@ -56,12 +67,13 @@ application.mount('#app')
|
|
|
56
67
|
首页默认进入 `/home/` 显示 Welcome,在默认布局与 tabs 内呈现;兼容 `/welcome/` 地址。可用 `homePath` 指定其他入口,通过 `components.home` 替换首页组件。
|
|
57
68
|
业务视图、关联视图与抽屉视图共享宿主提供的 viewModules,仍优先业务页面、缺失才回退模板。
|
|
58
69
|
|
|
59
|
-
可覆盖 `http`、`registry`、`auth`、`authOptions`、`router`、`history`、`locale`、`loginPath`、`menus`、`layoutProps`,
|
|
70
|
+
可覆盖 `http`、`registry`、`auth`、`authOptions`、`router`、`history`、`locale`、`size`、`loginPath`、`menus`、`layoutProps`,
|
|
60
71
|
通过 `components: { app, layout, login, home }` 替换默认组件,`modelViews` 覆盖模型页面;`routes` 添加布局内业务路由。
|
|
61
72
|
返回的 `app`、`router`、`registry`、`auth` 可继续使用,挂载前可调用 `application.app.use(...)` 安装其他插件。
|
|
62
73
|
`context` 可注入上传服务等已有扩展,不需要放弃底层 createDjango/createRegistry API。
|
|
63
74
|
|
|
64
75
|
默认排版提供 `--vd-body-margin`、`--vd-page-padding`、`--vd-text-color`、`--vd-background` CSS 变量。
|
|
76
|
+
右上角帐号区采用名称下拉菜单,退出登录需确认;修改密码默认指向 `/auth/change_password/`,未注册该页面时菜单项禁用。可通过 `layoutProps.changePasswordPath` 修改目标路径,单独使用 Layout 时也可用 `account` 插槽覆盖帐号区。
|
|
65
77
|
字体图标样式不自动引入:使用旧 Font Awesome 图标名称的宿主需自行安装并加载对应 CSS。
|
|
66
78
|
|
|
67
79
|
## 当前功能
|
|
@@ -72,9 +84,15 @@ application.mount('#app')
|
|
|
72
84
|
- `Form`:通用字段、校验、提交与字段错误;登录表单和 `ModelForm` 复用该组件。
|
|
73
85
|
- `ModelTable`、`ModelForm`:元数据驱动列表与表单、新建抽屉、行双击编辑和行操作。
|
|
74
86
|
- `Table`、`RemoteTable`、`useRemoteTable`:通用展示、远程请求与分页/排序状态,可用于非模型接口。
|
|
87
|
+
|
|
88
|
+
模型表根据 OPTIONS 的 `actions.SEARCH.ordering_fields` 自动启用远程排序。字段配置 `sortable: false` 可禁用,`sortable: true` 仅排序当前页,`sortable: 'custom'` 向后端发送 ordering(降序加 `-`)。改变排序回到第一页并保留搜索;清除排序时不再发送交互排序参数,恢复后端或固定查询的默认顺序。
|
|
89
|
+
|
|
75
90
|
- `ModelSearch`、`ModelSelect`、`ModelRelations`:搜索占位符、模型选择与关联视图。
|
|
76
91
|
- `Drawer`、`Actions`:动态内容、完成回调、按钮/更多菜单、确认与异步状态。
|
|
77
92
|
- `TableWidget`:choices、布尔、数字、日期、外键、图片、视频、JSON、HTML 和自定义渲染。
|
|
93
|
+
|
|
94
|
+
表格 datetime 默认沿用旧 Date2Now:近期显示“刚刚 / 几分钟前 / 几小时前 / 1天前”,较早记录显示简短日期,悬停显示完整本地时间。`date2now`、`timestamp` 控件使用相同规则;无时区 datetime 按旧项目的北京时间解析,显式时区保留。纯 date 保留 `YYYY-MM-DD`;需要其他格式时使用字段 formatter 覆盖。
|
|
95
|
+
|
|
78
96
|
- `ImageUpload`、`FileUpload`、`createUploadService`:平台无关的签名直传、后端确认、进度/取消/重试;需接入后端上传接口。
|
|
79
97
|
|
|
80
98
|
上传控件接入及后端协议见 [媒体上传](docs/uploads.md)。
|
|
@@ -120,7 +138,7 @@ import { ModelTable, ModelForm } from 'vue3-django'
|
|
|
120
138
|
</template>
|
|
121
139
|
```
|
|
122
140
|
|
|
123
|
-
`ModelForm` 不传 `id`
|
|
141
|
+
`ModelForm` 不传 `id` 时新增,传入时编辑。ModelTable、ModelForm 及默认模型页固定使用 PC 控件,不提供 mobile 模式;通用 Table、RemoteTable、Form 仍保留移动能力。
|
|
124
142
|
表单支持 `v-model`、`defaults`、`items`,暴露 `load()`/`submit()`;列表暴露 `refresh()`。
|
|
125
143
|
保存事件 `form-posted` 保留 `{ model, data, intent }`。
|
|
126
144
|
|
|
@@ -128,15 +146,17 @@ import { ModelTable, ModelForm } from 'vue3-django'
|
|
|
128
146
|
`genModelRouters(apps, { modules: import.meta.glob('./views/**/*.vue') })` 通过 `import_or_use_template` 动态加载页面:优先使用宿主的 `views/<app>/<model>/list.vue`、`edit.vue`,不存在时使用内置模板;新增也复用 edit 模板。已有页面的加载错误不会回退。仍可传 `{ list, create, edit }` 覆盖页面组件。
|
|
129
147
|
默认 App 只渲染 RouterView,布局由父路由的内置 Root 提供,模型页作为子路由渲染。demo 直接使用内置组件,无需自己的 App.vue、Root.vue、Login.vue;需要定制时通过 components 覆盖。
|
|
130
148
|
列表页读取 `config.list` 并传递 `baseQueries` 等配置,处理编辑跳转及 revisions 刷新。
|
|
131
|
-
`list.mode: 'grid'`
|
|
149
|
+
`list.mode: 'grid'` 配合 `gridComponent` 接入宿主专用网格组件;未提供时仍使用 PC 表格(并非旧 ModelGrid 的全部功能)。
|
|
132
150
|
编辑页在 ModelForm 的 `bottom` 插槽挂载 `panels`(兼容旧拼写 `pannels`)折叠面板及 relations;面板的 component 接收 parent 模型上下文。
|
|
133
|
-
新增页在保存成功后替换为编辑路径,关联区只在已有主键时显示。内置页面需要安装 vue-router,页面 props 支持 appModel、id、mode
|
|
151
|
+
新增页在保存成功后替换为编辑路径,关联区只在已有主键时显示。内置页面需要安装 vue-router,页面 props 支持 appModel、id、mode。
|
|
134
152
|
|
|
135
|
-
`ModelTable` 的“新增”默认打开 Drawer
|
|
153
|
+
`ModelTable` 的“新增”默认打开 Drawer,宽度 66%。
|
|
136
154
|
`createDefaults` 覆盖 `baseQueries` 的新建默认值,`createDrawerSize` 调整桌面宽度。
|
|
137
155
|
成功后关闭抽屉、发出 `created`(同 form-posted payload)并刷新列表;失败时保留表单。
|
|
138
156
|
`create` 表示点击新增,旧宿主若通过该事件自行导航或打开弹窗,应设置 `create-mode="event"`。
|
|
139
157
|
`edit` 仍由宿主处理;默认行双击也触发编辑,可用 `dblClickAction` 配置或空字符串禁用。
|
|
158
|
+
表格表头与行操作默认优先使用图标按钮,Tooltip 和 aria-label 保留操作名称;未配置 icon 的动作显示文字,更多菜单内仍保留文字。`actionIconOnly: false` 可恢复图标加文字,单个动作可设置 `iconOnly: false`。
|
|
159
|
+
桌面行操作采用紧凑按钮组,更多菜单仅显示箭头,沿用旧版 visibility 规则,仅在记录行悬停时显示;触屏保持常显。`hoverShow: false` 可关闭悬停隐藏,便于键盘操作。`actionsColumnWidth` 可指定操作列宽度(默认根据动作数量与文字估算)。模型表支持在 `list.options.remoteTable.table` 中配置这些选项。
|
|
140
160
|
|
|
141
161
|
业务 `src/views/crm/customer/config.js` 保留旧式声明:
|
|
142
162
|
|
|
@@ -199,9 +219,54 @@ demo 的编辑页每条记录使用独立 path/tab,标题优先采用记录 `_
|
|
|
199
219
|
- 跨域凭证、CORS 与 CSRF trusted origins 由业务环境配置;`createHttp` 可传 Axios 配置,不自动获取 CSRF cookie,也不持久保存 Token。
|
|
200
220
|
- 可选 `createAuth` 沿用 `auth/user/login/`、`auth/user/current/`、`auth/user/logout/`;JWT 使用 `token.access` 和 Bearer 请求头,并通过 `access_token` cookie 保留 Token,也支持 Session。demo 登录页可记忆用户名,但不保存密码。
|
|
201
221
|
|
|
222
|
+
## 通用外键展示
|
|
223
|
+
|
|
224
|
+
普通 ForeignKey 指向固定模型;GenericForeignKey 使用记录的 content type 动态确定模型。
|
|
225
|
+
|
|
226
|
+
```js
|
|
227
|
+
import { GenericForeignKey } from 'vue3-django'
|
|
228
|
+
|
|
229
|
+
const ownerColumn = {
|
|
230
|
+
name: 'owner_type',
|
|
231
|
+
label: '属主',
|
|
232
|
+
widget: GenericForeignKey,
|
|
233
|
+
contentTypeIdField: 'owner_type',
|
|
234
|
+
objectIdField: 'owner_id',
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
也可使用字符串 `widget: 'GenericForeignKey'`。默认字段为 `content_type`、`object_id`;兼容独立使用的平铺 context、表格 context.row 和整行 value。
|
|
239
|
+
需注册 `contenttypes.contenttype`,后端提供 `contenttypes/contenttype/all/` 接口(results 中包含 id、app_label、model)。映射在同一 registry 中缓存,支持分页,加载失败通过 error 事件反馈。
|
|
240
|
+
已注册模型且存在编辑路由时显示“模型名称:ID”链接,点击由 RouterLink 打开对应模型 tab;未知类型、缺少路由时显示文本,不生成无效链接。`labelField` 可指定未知类型的回退文本字段,`showLink: false` 可关闭跳转。
|
|
241
|
+
|
|
242
|
+
## 模型批量操作
|
|
243
|
+
|
|
244
|
+
在模型 `config.js` 的 `list.batchActions` 中配置,或向 ModelTable 传入 `batch-actions`;配置后自动显示勾选列。
|
|
245
|
+
|
|
246
|
+
```js
|
|
247
|
+
export default {
|
|
248
|
+
list: {
|
|
249
|
+
batchActions: [
|
|
250
|
+
{
|
|
251
|
+
name: 'disable',
|
|
252
|
+
label: '禁用',
|
|
253
|
+
icon: 'ban',
|
|
254
|
+
api: 'batch_disable',
|
|
255
|
+
context: { enable_flag: false },
|
|
256
|
+
notice: '确定禁用这些记录吗?',
|
|
257
|
+
},
|
|
258
|
+
],
|
|
259
|
+
},
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
沿用 vue-django 的 `select`(选中)、`all`(全部)、`exclude`(其余)范围。默认 POST 到模型列表的动作接口,请求体包含 `batch_action_ids`、`scope` 和 action.context,查询参数携带当前搜索与固定条件;后端负责按范围处理数据。
|
|
264
|
+
未选中时禁用,默认弹出确认(`confirm: false` 可关闭);接口动作默认检查 `api || name` 权限,执行成功刷新列表并清除选择。
|
|
265
|
+
自定义 `do({ selection, count, scope, model, parent, queries, table, confirmResult })` 和抽屉组件动作复用现有 Actions;函数确认的返回值通过 confirmResult 传递。
|
|
266
|
+
|
|
202
267
|
## 尚未覆盖
|
|
203
268
|
|
|
204
|
-
|
|
269
|
+
这仍是渐进迁移版本,不是旧组件的完整替代。旧业务动作页面及批量操作的旧式 dialog 配置尚未完整迁移;
|
|
205
270
|
基础图片/文件上传已实现,云存储签名及确认接口、大文件分片/断点续传、视频转码/VOD 尚未接通;
|
|
206
271
|
复杂分组列(subColumns/rows/headerWidget)尚未支持;Date2Now 当前显示完整本地时间,未实现旧版相对时间文案。
|
|
207
272
|
测试与构建通过不等于所有真实后端模型、权限和业务动作已经验收。
|
package/dist/vue3-django.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.vd-toolbar{display:flex;align-items:center;gap:8px;margin:0 0 8px}.vd-toolbar h2{flex:1;margin:0
|
|
1
|
+
.vd-toolbar{display:flex;align-items:center;gap:8px;margin:0 0 8px}.vd-toolbar h2{flex:1;margin:0}.vd-toolbar .el-input{max-width:360px}.vd-card,.el-alert{margin-bottom:16px}body:has(.vd-app){margin:var(--vd-body-margin, 0)}.vd-app[v-cloak]{display:none}.vd-app{color:var(--vd-text-color, #253447);background:var(--vd-background, white);font-family:system-ui,-apple-system,sans-serif}.vd-app .vd-model-page{width:100%;box-sizing:border-box;padding:var(--vd-page-padding, 0 4px 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}.viewtabs[data-v-5703e450]{margin:0 8px}.viewtabs[data-v-5703e450] .el-tabs__header{margin-bottom:10px}.tab-icon[data-v-5703e450]{width:1em;height:1em;margin-right:6px}.vd-actions[data-v-2e52c126]{display:inline-flex;align-items:center;white-space:nowrap;vertical-align:middle}.vd-actions.is-compact[data-v-2e52c126] .el-button+.el-button{margin-left:0}.vd-actions.is-compact[data-v-2e52c126] .el-dropdown{margin-left:4px}.vd-actions-more[data-v-2e52c126]{border:0;background:transparent}.vd-actions[data-v-2e52c126] .vd-action-icon .model-icon{margin-right:0}.django-layout[data-v-3bbd5166]{--vd-header-height: var(--el-component-size);display:flex;flex-direction:column;min-height:100vh;background:#fff;color:#253447}.layout-header[data-v-3bbd5166]{min-height:var(--vd-header-height);padding:0 16px;display:flex;align-items:center;gap:12px;background:#fff;border-bottom:1px solid #e5eaf0}.layout-brand[data-v-3bbd5166]{font-weight:700;color:inherit;text-decoration:none}.layout-account[data-v-3bbd5166]{margin-left:auto;display:flex;align-items:center;gap:12px;min-width:0}.account-trigger[data-v-3bbd5166]{display:inline-flex;align-items:center;gap:10px;max-width:min(240px,40vw)}.account-trigger[data-v-3bbd5166]:focus-visible{outline:2px solid var(--el-color-primary, #409eff);outline-offset:-2px}.account-name[data-v-3bbd5166]{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.account-arrow[data-v-3bbd5166]{flex-shrink:0;color:var(--el-text-color-placeholder, #a8abb2)}.layout-body[data-v-3bbd5166]{display:flex;flex:1}.layout-sidebar[data-v-3bbd5166]{width:190px;flex-shrink:0;background:#fff;border-right:1px solid #e5eaf0}.layout-sidebar[data-v-3bbd5166] .el-menu{border-right:0}.layout-content[data-v-3bbd5166]{min-width:0;flex:1;padding-top:0}.menu-toggle[data-v-3bbd5166],.menu-backdrop[data-v-3bbd5166]{display:none}.layout-content[data-v-3bbd5166] .viewtabs{margin:0 8px}@media(max-width:768px){.menu-toggle[data-v-3bbd5166]{display:inline-flex}.layout-header[data-v-3bbd5166]{padding:0 12px}.layout-sidebar[data-v-3bbd5166]{display:none}.layout-sidebar.opened[data-v-3bbd5166]{display:block;position:fixed;top:calc(var(--vd-header-height) + 1px);bottom:0;left:0;z-index:21;overflow:auto}.menu-backdrop[data-v-3bbd5166]{display:block;position:fixed;inset:calc(var(--vd-header-height) + 1px) 0 0;background:#0006;border:0;z-index:20}.layout-content[data-v-3bbd5166] .viewtabs{margin:0 8px}}.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}.vd-login[data-v-a32e9ae3]{max-width:400px;margin:80px auto;padding:24px}h1[data-v-a32e9ae3]{font-size:24px}.vd-home[data-v-e81cec03]{padding:var(--vd-home-padding, 24px)}h1[data-v-e81cec03]{margin:0 0 12px;font-size:24px;font-weight:500}p[data-v-e81cec03]{margin:0;color:var(--el-text-color-secondary, #909399)}.vd-batch-actions[data-v-bf15a101]{display:flex;align-items:center;gap:6px;margin:4px 0}.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}.foreignkey-link[data-v-75351597]{color:var(--el-color-primary, #409eff);text-decoration:none}.foreignkey-link[data-v-75351597]:hover{text-decoration:underline}.vd-table-text[data-v-286ae7d1]{white-space:pre-wrap;overflow-wrap:anywhere}.vd-table-boolean.is-true[data-v-286ae7d1]{color:var(--el-color-success, green);font-weight:700}.vd-table-images[data-v-286ae7d1]{display:flex;flex-wrap:wrap;gap:4px}.vd-table-video[data-v-286ae7d1]{width:180px;max-width:100%}.vd-table-json[data-v-286ae7d1]{display:grid;grid-template-columns:auto 1fr;gap:4px 12px;margin:0}.vd-table-json dd[data-v-286ae7d1]{margin:0}@media(hover:hover)and (pointer:fine){[data-v-415a1a29] .vd-row-actions.hover-show{visibility:hidden}[data-v-415a1a29] .el-table__row:hover .vd-row-actions.hover-show{visibility:visible;cursor:pointer}}.vd-table-actions[data-v-415a1a29]{display:flex;align-items:center;justify-content:flex-end;gap:8px;margin-bottom:8px}.vd-table-pagination[data-v-560aef57]{padding:10px 0 6px}.vd-table-pagination[data-v-560aef57] .el-pagination{display:flex;flex-wrap:wrap;justify-content:flex-start;row-gap:8px}.vd-table-pagination.is-mobile[data-v-560aef57]{padding:12px 0}@media(max-width:600px){.vd-table-pagination[data-v-560aef57] .el-pagination__jump{margin-left:0}}
|