@wg-song/bare 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/AGENTS.md +21 -1
- package/README.md +271 -56
- package/examples/role-management/RoleDetail.vue +41 -0
- package/examples/role-management/index.vue +115 -0
- package/examples/user-management/index.vue +87 -0
- package/index.cjs +8 -4
- package/index.css +1 -1
- package/index.mjs +254 -250
- package/index.umd.js +8 -4
- package/package.json +13 -12
- package/types.d.ts +52 -1
package/AGENTS.md
CHANGED
|
@@ -13,12 +13,23 @@
|
|
|
13
13
|
接入项目的 `main.ts` 通常已包含:
|
|
14
14
|
|
|
15
15
|
```ts
|
|
16
|
+
import { createApp } from 'vue'
|
|
17
|
+
import VXETable from 'vxe-table'
|
|
18
|
+
import VXEPcUI from 'vxe-pc-ui'
|
|
16
19
|
import BareComponents from '@wg-song/bare'
|
|
20
|
+
import 'vxe-table/lib/style.css'
|
|
21
|
+
import 'vxe-pc-ui/lib/style.css'
|
|
17
22
|
import '@wg-song/bare/index.css'
|
|
18
23
|
|
|
24
|
+
const app = createApp(App)
|
|
25
|
+
app.use(VXEPcUI)
|
|
26
|
+
app.use(VXETable)
|
|
19
27
|
app.use(BareComponents)
|
|
28
|
+
app.mount('#app')
|
|
20
29
|
```
|
|
21
30
|
|
|
31
|
+
**顺序要求**:必须先 `app.use(VXETable)` / `app.use(VXEPcUI)`,再 `app.use(BareComponents)`。若未注册 `vxe-table`,表格组件会报 `Failed to resolve component: vxe-grid`。
|
|
32
|
+
|
|
22
33
|
因此业务页面中直接使用组件即可,无需再次安装或注册。
|
|
23
34
|
|
|
24
35
|
## 3. 组件选择速查表
|
|
@@ -165,7 +176,16 @@ function handleDetail(row: any) {
|
|
|
165
176
|
}
|
|
166
177
|
```
|
|
167
178
|
|
|
168
|
-
## 5.
|
|
179
|
+
## 5. 首次接入检查清单
|
|
180
|
+
|
|
181
|
+
在业务页面引用组件前,请确认:
|
|
182
|
+
|
|
183
|
+
1. `main.ts` 已 `app.use(VXETable)` 并引入 `vxe-table/lib/style.css`。
|
|
184
|
+
2. 若使用 vxe-table 高级 UI 能力(tooltip、loading、筛选面板),已 `app.use(VXEPcUI)` 并引入 `vxe-pc-ui/lib/style.css`。
|
|
185
|
+
3. 已 `import '@wg-song/bare/index.css'`。
|
|
186
|
+
4. `package.json` 中 `@wg-song/bare` 的 peer dependencies 已满足(vue、ant-design-vue、vxe-table、@ant-design/icons-vue、vue-router)。
|
|
187
|
+
|
|
188
|
+
## 6. 类型导入清单
|
|
169
189
|
|
|
170
190
|
```ts
|
|
171
191
|
import type {
|
package/README.md
CHANGED
|
@@ -8,95 +8,103 @@
|
|
|
8
8
|
pnpm add @wg-song/bare
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Peer dependencies
|
|
11
|
+
Peer dependencies(必需,由接入应用提供):
|
|
12
12
|
|
|
13
13
|
```json
|
|
14
14
|
{
|
|
15
15
|
"vue": "^3.0.0",
|
|
16
16
|
"ant-design-vue": "^4.0.0",
|
|
17
17
|
"vxe-table": "^4.0.0",
|
|
18
|
-
"vxe-pc-ui": "^4.0.0",
|
|
19
18
|
"@ant-design/icons-vue": "^7.0.0",
|
|
20
|
-
"vue-router": "^4.0.0"
|
|
19
|
+
"vue-router": "^4.0.0 || ^5.0.0"
|
|
21
20
|
}
|
|
22
21
|
```
|
|
23
22
|
|
|
23
|
+
推荐一并安装(以获得完整 vxe-table 体验):
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pnpm add vxe-pc-ui
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
> `vxe-pc-ui` 是 `vxe-table` 的配套 UI 包,提供 tooltip、loading、筛选面板等能力。本库表格组件默认使用 `showOverflow: 'title'` 避免强依赖,不强制要求 `vxe-pc-ui`;但建议接入方一并安装并注册,否则部分 vxe-table 高级交互可能表现不一致。
|
|
30
|
+
|
|
24
31
|
## 快速开始
|
|
25
32
|
|
|
26
|
-
在 `main.ts`
|
|
33
|
+
在 `main.ts` 中**先注册 vxe-table / vxe-pc-ui**,再安装本库:
|
|
27
34
|
|
|
28
35
|
```ts
|
|
29
36
|
import { createApp } from 'vue'
|
|
37
|
+
import VXETable from 'vxe-table'
|
|
38
|
+
import VXEPcUI from 'vxe-pc-ui'
|
|
30
39
|
import BareComponents from '@wg-song/bare'
|
|
40
|
+
import 'vxe-table/lib/style.css'
|
|
41
|
+
import 'vxe-pc-ui/lib/style.css'
|
|
31
42
|
import '@wg-song/bare/index.css'
|
|
32
43
|
|
|
33
44
|
const app = createApp(App)
|
|
45
|
+
app.use(VXEPcUI)
|
|
46
|
+
app.use(VXETable)
|
|
34
47
|
app.use(BareComponents)
|
|
35
48
|
app.mount('#app')
|
|
36
49
|
```
|
|
37
50
|
|
|
38
|
-
>
|
|
51
|
+
> 若未注册 `vxe-table`,表格组件会报 `[Vue warn]: Failed to resolve component: vxe-grid`。开发环境下本库 `install` 阶段会在控制台给出明确提示。
|
|
39
52
|
|
|
40
|
-
##
|
|
53
|
+
## 组件能力矩阵
|
|
41
54
|
|
|
42
|
-
###
|
|
55
|
+
### 场景组件
|
|
43
56
|
|
|
44
57
|
| 组件 / API | 说明 |
|
|
45
58
|
|---|---|
|
|
46
|
-
| `
|
|
47
|
-
| `
|
|
48
|
-
| `
|
|
49
|
-
| `
|
|
50
|
-
| `useForm` | 表单核心 hook |
|
|
51
|
-
| `useSearchForm` | 搜索表单 hook |
|
|
52
|
-
|
|
53
|
-
### 表格 Table
|
|
54
|
-
|
|
55
|
-
| 组件 / API | 说明 |
|
|
56
|
-
|---|---|
|
|
57
|
-
| `Table` | 基础 vxe-table 封装 |
|
|
59
|
+
| `PageList` | 完整列表页(搜索 + 工具栏 + 表格 + 分页 + 详情弹窗) |
|
|
60
|
+
| `PageShell` | 列表页外壳(搜索 + 工具栏 + 表格插槽 + 分页) |
|
|
61
|
+
| `PageEditList` | 可编辑列表页 |
|
|
62
|
+
| `PageChildList` | 子列表页 |
|
|
58
63
|
| `TableRead` | 只读表格(固定操作列等) |
|
|
59
64
|
| `TableEdit` | 行内编辑表格 |
|
|
60
65
|
| `TableChild` | 父子表格 |
|
|
61
66
|
| `TableGroup` / `TableGroupPage` | 分组表格 |
|
|
62
|
-
| `
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
| 组件 / API | 说明 |
|
|
67
|
-
|---|---|
|
|
68
|
-
| `PageList` | 完整列表页 |
|
|
69
|
-
| `PageShell` | 列表页外壳(搜索 + 工具栏 + 表格插槽 + 分页) |
|
|
70
|
-
| `PageEditList` | 可编辑列表页 |
|
|
71
|
-
| `PageChildList` | 子列表页 |
|
|
72
|
-
| `usePageList` | 列表页逻辑 hook |
|
|
73
|
-
| `definePageList` / `defineLoader` | 列表页配置定义 |
|
|
67
|
+
| `DialogForm` | 弹窗表单场景 |
|
|
68
|
+
| `PageModals` / `PageModalSlot` | 页面级弹窗管理器 |
|
|
69
|
+
| `PageListTabs` | 标签页 + 列表组合 |
|
|
74
70
|
|
|
75
|
-
###
|
|
71
|
+
### 基础组件
|
|
76
72
|
|
|
77
73
|
| 组件 / API | 说明 |
|
|
78
74
|
|---|---|
|
|
75
|
+
| `Form` | Schema 驱动表单 |
|
|
76
|
+
| `SearchForm` | 搜索表单(含高级搜索、FilterBar) |
|
|
77
|
+
| `SearchFormSimple` | 简易单行搜索 |
|
|
78
|
+
| `Table` | 基础 vxe-table 封装 |
|
|
79
|
+
| `Pagination` | 分页 |
|
|
79
80
|
| `Modal` / `Drawer` | 声明式弹窗/抽屉 |
|
|
80
|
-
| `
|
|
81
|
-
| `
|
|
82
|
-
| `
|
|
81
|
+
| `Button` | 按钮 |
|
|
82
|
+
| `Switch` | 开关 |
|
|
83
|
+
| `Tag` | 标签 |
|
|
84
|
+
| `Link` | 链接 |
|
|
85
|
+
| `Icon` | 图标 |
|
|
86
|
+
| `Col` | 布局列 |
|
|
87
|
+
| `Dropdown` / `DropdownMenuItem` / `DropdownSplitButton` | 下拉菜单 |
|
|
88
|
+
| `Dialog` | 对话框 |
|
|
89
|
+
| `Popover` / `Tooltip` / `Popconfirm` | 反馈类浮层 |
|
|
90
|
+
| `Tabs` / `TabsView` / `PageTabs` | 标签页 |
|
|
91
|
+
| `SplitPane` / `SplitPaneLayout` | 分割面板 |
|
|
92
|
+
| `ToolbarActionBar` / `ToolbarActionButtonMenu` | 表格工具栏操作 |
|
|
83
93
|
|
|
84
|
-
###
|
|
94
|
+
### Hooks / 工具函数
|
|
85
95
|
|
|
86
|
-
|
|
|
96
|
+
| API | 说明 |
|
|
87
97
|
|---|---|
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
90
|
-
| `
|
|
98
|
+
| `useForm` / `useSearchForm` | 表单核心 hook |
|
|
99
|
+
| `usePageList` | 列表页逻辑 hook |
|
|
100
|
+
| `definePageList` / `defineLoader` | 列表页配置定义 |
|
|
101
|
+
| `usePageModals` / `usePageModalContext` / `definePageModals` | 页面弹窗管理 |
|
|
91
102
|
| `useTabState` / `useTabbedList` | 标签页状态 hook |
|
|
103
|
+
| `openModal` / `openDrawer` / `confirm` / `alert` / `setupModal` / `useModalContext` | 命令式弹窗/反馈 |
|
|
104
|
+
| `localColDriver` / `remoteColDriver` / `storeColDriver` / `setupColPref` | 列偏好存储 |
|
|
105
|
+
| `mapToOptions` / `formatDictLabel` / `formatAmountValue` / `formatDateValue` / `formatCellDisplay` | 表格格式化工具 |
|
|
92
106
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
| 组件 / API | 说明 |
|
|
96
|
-
|---|---|
|
|
97
|
-
| `SplitPane` / `SplitPaneLayout` | 分割面板 |
|
|
98
|
-
| `ToolbarActionBar` / `ToolbarActionButtonMenu` | 表格工具栏操作 |
|
|
99
|
-
| `Button`、`Input`、`Select`、`Tag`、`Link` 等 | 字段级原子组件(同样从 `@wg-song/bare` 导入) |
|
|
107
|
+
> 当前版本**未提供**独立的 `Input`、`Select`、`Card` 组件;表单输入请使用 `Form` / `SearchForm` 的 `FormField` 配置,或继续使用 Ant Design Vue 原生组件。
|
|
100
108
|
|
|
101
109
|
## 核心使用模式
|
|
102
110
|
|
|
@@ -160,26 +168,30 @@ const data = [
|
|
|
160
168
|
```vue
|
|
161
169
|
<script setup lang="ts">
|
|
162
170
|
import { PageShell } from '@wg-song/bare'
|
|
163
|
-
import type { FormField, PageShellProps } from '@wg-song/bare'
|
|
171
|
+
import type { FormField, PageShellProps, ActionItem } from '@wg-song/bare'
|
|
164
172
|
|
|
165
173
|
const searchSchemas: FormField[] = [
|
|
166
174
|
{ field: 'keyword', label: '关键词', component: 'input' },
|
|
167
175
|
]
|
|
168
176
|
|
|
169
|
-
const
|
|
170
|
-
// 调用接口
|
|
171
|
-
return { list: [], total: 0 }
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const toolbarActions = [
|
|
177
|
+
const toolbarActions: ActionItem[] = [
|
|
175
178
|
{ label: '新增', onClick: () => {} },
|
|
176
179
|
]
|
|
180
|
+
|
|
181
|
+
const loader: PageShellProps['loader'] = async ({ params, page, pageSize }) => {
|
|
182
|
+
// 调用业务 API,params 为搜索表单值
|
|
183
|
+
return { list: [], total: 0 }
|
|
184
|
+
}
|
|
177
185
|
</script>
|
|
178
186
|
|
|
179
187
|
<template>
|
|
180
|
-
<PageShell
|
|
188
|
+
<PageShell
|
|
189
|
+
:search-schemas="searchSchemas"
|
|
190
|
+
:loader="loader"
|
|
191
|
+
:toolbar-actions="toolbarActions"
|
|
192
|
+
>
|
|
181
193
|
<template #default="{ tableData, loading }">
|
|
182
|
-
<!--
|
|
194
|
+
<!-- 渲染表格或其他内容 -->
|
|
183
195
|
</template>
|
|
184
196
|
</PageShell>
|
|
185
197
|
</template>
|
|
@@ -202,6 +214,173 @@ function onOpen() {
|
|
|
202
214
|
}
|
|
203
215
|
```
|
|
204
216
|
|
|
217
|
+
## 完整 CRUD 示例:角色管理
|
|
218
|
+
|
|
219
|
+
```vue
|
|
220
|
+
<!-- views/role/index.vue -->
|
|
221
|
+
<script setup lang="ts">
|
|
222
|
+
import { ref } from 'vue'
|
|
223
|
+
import { ref } from 'vue'
|
|
224
|
+
import { PageList, confirm } from '@wg-song/bare'
|
|
225
|
+
import type {
|
|
226
|
+
FormField,
|
|
227
|
+
TableColumn,
|
|
228
|
+
ActionItem,
|
|
229
|
+
PageListProps,
|
|
230
|
+
PageListToolbarContext,
|
|
231
|
+
} from '@wg-song/bare'
|
|
232
|
+
import RoleDetail from './RoleDetail.vue'
|
|
233
|
+
|
|
234
|
+
const pageListRef = ref<InstanceType<typeof PageList> | null>(null)
|
|
235
|
+
|
|
236
|
+
const searchSchemas: FormField[] = [
|
|
237
|
+
{ field: 'roleName', label: '角色名称', component: 'input' },
|
|
238
|
+
{ field: 'status', label: '状态', component: 'select', options: [
|
|
239
|
+
{ label: '启用', value: 1 },
|
|
240
|
+
{ label: '禁用', value: 0 },
|
|
241
|
+
]},
|
|
242
|
+
]
|
|
243
|
+
|
|
244
|
+
const columns: TableColumn[] = [
|
|
245
|
+
{ field: 'roleName', title: '角色名称', component: 'link' },
|
|
246
|
+
{ field: 'roleCode', title: '角色编码' },
|
|
247
|
+
{ field: 'status', title: '状态', component: 'tag', tagColor: ({ value }) => value === 1 ? 'success' : 'default' },
|
|
248
|
+
{ field: 'updateTime', title: '更新时间' },
|
|
249
|
+
]
|
|
250
|
+
|
|
251
|
+
function toolbarActions(ctx: PageListToolbarContext): ActionItem[] {
|
|
252
|
+
return [
|
|
253
|
+
{ label: '新增', type: 'primary', onClick: () => pageListRef.value?.openDetail(null) },
|
|
254
|
+
{
|
|
255
|
+
label: '批量删除',
|
|
256
|
+
danger: true,
|
|
257
|
+
disabled: ctx.selectedCount === 0,
|
|
258
|
+
onClick: async () => {
|
|
259
|
+
const rows = ctx.getSelectedRows()
|
|
260
|
+
const ok = await confirm(`确认删除选中的 ${rows.length} 项?`)
|
|
261
|
+
if (!ok) return
|
|
262
|
+
// 业务侧调用删除 API,例如 await deleteRole(rows.map((r) => r.id))
|
|
263
|
+
pageListRef.value?.reload()
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
]
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function rowActions(row: any): ActionItem[] {
|
|
270
|
+
return [
|
|
271
|
+
{ label: '编辑', onClick: () => pageListRef.value?.openDetail(row) },
|
|
272
|
+
{ label: '删除', danger: true, onClick: async () => {
|
|
273
|
+
const ok = await confirm('确认删除该角色?')
|
|
274
|
+
if (!ok) return
|
|
275
|
+
// 业务侧调用删除 API,例如 await deleteRole([row.id])
|
|
276
|
+
pageListRef.value?.reload()
|
|
277
|
+
}},
|
|
278
|
+
]
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const page: PageListProps['page'] = {
|
|
282
|
+
tableId: 'role-management',
|
|
283
|
+
search: searchSchemas,
|
|
284
|
+
columns,
|
|
285
|
+
loader: async ({ params, page, pageSize }) => {
|
|
286
|
+
// 业务侧调用分页 API,例如 const res = await getRolePage({ ...params, currentPage: page, pageSize })
|
|
287
|
+
return { list: [], total: 0 }
|
|
288
|
+
},
|
|
289
|
+
toolbarActions,
|
|
290
|
+
rowActions,
|
|
291
|
+
detailComponent: RoleDetail,
|
|
292
|
+
detailTitle: '角色详情',
|
|
293
|
+
detailWidth: 600,
|
|
294
|
+
}
|
|
295
|
+
</script>
|
|
296
|
+
|
|
297
|
+
<template>
|
|
298
|
+
<PageList ref="pageListRef" :page="page" />
|
|
299
|
+
</template>
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
```vue
|
|
303
|
+
<!-- views/role/RoleDetail.vue -->
|
|
304
|
+
<script setup lang="ts">
|
|
305
|
+
import { computed } from 'vue'
|
|
306
|
+
import { Form, usePageModalContext } from '@wg-song/bare'
|
|
307
|
+
import type { FormField } from '@wg-song/bare'
|
|
308
|
+
const { row, close, confirmLoading } = usePageModalContext<any>()
|
|
309
|
+
|
|
310
|
+
/** row 存在表示编辑,不存在表示新增 */
|
|
311
|
+
const isEdit = computed(() => !!row.value)
|
|
312
|
+
|
|
313
|
+
const schemas: FormField[] = [
|
|
314
|
+
{ field: 'roleName', label: '角色名称', component: 'input', required: true },
|
|
315
|
+
{ field: 'roleCode', label: '角色编码', component: 'input', required: true },
|
|
316
|
+
{ field: 'status', label: '状态', component: 'select', options: [
|
|
317
|
+
{ label: '启用', value: 1 },
|
|
318
|
+
{ label: '禁用', value: 0 },
|
|
319
|
+
]},
|
|
320
|
+
{ field: 'remark', label: '备注', component: 'textarea' },
|
|
321
|
+
]
|
|
322
|
+
|
|
323
|
+
async function submit() {
|
|
324
|
+
confirmLoading.value = true
|
|
325
|
+
try {
|
|
326
|
+
// 业务侧调用保存 API,例如 await saveRole(row.value)
|
|
327
|
+
close({ success: true })
|
|
328
|
+
} finally {
|
|
329
|
+
confirmLoading.value = false
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
defineExpose({ submit })
|
|
334
|
+
</script>
|
|
335
|
+
|
|
336
|
+
<template>
|
|
337
|
+
<Form
|
|
338
|
+
:model="row"
|
|
339
|
+
:schemas="schemas"
|
|
340
|
+
:disabled="!isEdit && false"
|
|
341
|
+
/>
|
|
342
|
+
</template>
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
> 关键约定:
|
|
346
|
+
> - `detailComponent` 通过 `usePageModalContext()` 获取 `row`;`row` 为 `null/undefined` 表示新增,否则为编辑。
|
|
347
|
+
> - 详情组件必须 `defineExpose({ submit })`,PageList 在点击「确定」时调用该方法。
|
|
348
|
+
> - `submit` 中通过 `confirmLoading.value = true/false` 控制确认按钮加载态;调用 `close(result)` 关闭弹窗。
|
|
349
|
+
|
|
350
|
+
## 核心 API 速查
|
|
351
|
+
|
|
352
|
+
### `PageList` 实例方法(通过 `ref` 获取)
|
|
353
|
+
|
|
354
|
+
| 方法 | 说明 |
|
|
355
|
+
|---|---|
|
|
356
|
+
| `openDetail(row?)` | 打开详情弹窗;`row` 为空表示新增 |
|
|
357
|
+
| `reload()` | 重新加载当前页数据 |
|
|
358
|
+
| `load()` | 同 `reload` |
|
|
359
|
+
| `reset()` | 重置搜索条件 |
|
|
360
|
+
| `gotoPage(page)` | 跳转到指定页 |
|
|
361
|
+
| `getSelectedRows()` | 获取当前已选行 |
|
|
362
|
+
| `setFieldsValue(values)` | 设置搜索表单字段值 |
|
|
363
|
+
| `clearCheckboxSelection()` | 清空复选框选择 |
|
|
364
|
+
|
|
365
|
+
### `usePageModalContext()`
|
|
366
|
+
|
|
367
|
+
| 属性 / 方法 | 类型 | 说明 |
|
|
368
|
+
|---|---|---|
|
|
369
|
+
| `row` | `Ref<D \| undefined>` | 当前行数据;为空表示新增 |
|
|
370
|
+
| `close` | `(result?: any) => void` | 关闭并触发 ok |
|
|
371
|
+
| `cancel` | `() => void` | 关闭并触发 cancel |
|
|
372
|
+
| `confirmLoading` | `Ref<boolean>` | 确认按钮加载状态 |
|
|
373
|
+
|
|
374
|
+
### `ActionItem` 常用字段
|
|
375
|
+
|
|
376
|
+
| 字段 | 说明 |
|
|
377
|
+
|---|---|
|
|
378
|
+
| `label` | 按钮文字 |
|
|
379
|
+
| `type` | Ant Design Vue Button `type`:`primary` / `default` / `dashed` / `link` / `text` |
|
|
380
|
+
| `danger` | 是否危险样式 |
|
|
381
|
+
| `disabled` | 是否禁用 |
|
|
382
|
+
| `onClick` | 点击回调 |
|
|
383
|
+
|
|
205
384
|
## 全局配置
|
|
206
385
|
|
|
207
386
|
通过 `app.use(BareComponents, { globalConfig })` 传入:
|
|
@@ -220,15 +399,50 @@ app.use(BareComponents, {
|
|
|
220
399
|
|
|
221
400
|
```ts
|
|
222
401
|
import type {
|
|
402
|
+
// 表单
|
|
223
403
|
FormField,
|
|
224
404
|
FormProps,
|
|
405
|
+
SearchFormProps,
|
|
406
|
+
DialogFormProps,
|
|
407
|
+
|
|
408
|
+
// 表格
|
|
225
409
|
TableColumn,
|
|
226
410
|
TableProps,
|
|
411
|
+
TableReadProps,
|
|
412
|
+
TableEditColumn,
|
|
413
|
+
TableChildProps,
|
|
414
|
+
TableGroupProps,
|
|
415
|
+
|
|
416
|
+
// 列表页
|
|
227
417
|
PageShellProps,
|
|
418
|
+
PageListProps,
|
|
419
|
+
PageListConfig,
|
|
420
|
+
PageListRowActions,
|
|
421
|
+
PageListToolbarContext,
|
|
228
422
|
ActionItem,
|
|
423
|
+
|
|
424
|
+
// 弹窗
|
|
425
|
+
ModalContext,
|
|
426
|
+
ModalProps,
|
|
427
|
+
DrawerProps,
|
|
428
|
+
PageModalContext,
|
|
429
|
+
PageModalConfig,
|
|
430
|
+
|
|
431
|
+
// 工具
|
|
432
|
+
OptionItem,
|
|
433
|
+
ColProps,
|
|
229
434
|
} from '@wg-song/bare'
|
|
230
435
|
```
|
|
231
436
|
|
|
437
|
+
## 包内示例
|
|
438
|
+
|
|
439
|
+
本包附带可直接运行的业务场景示例,统一使用 `from '@wg-song/bare'` 导入:
|
|
440
|
+
|
|
441
|
+
- `examples/role-management/` — 角色管理完整 CRUD
|
|
442
|
+
- `examples/user-management/` — 用户管理 PageShell + TableRead
|
|
443
|
+
|
|
444
|
+
复制到业务项目的 `views/` 目录即可参考使用。
|
|
445
|
+
|
|
232
446
|
## 构建产物
|
|
233
447
|
|
|
234
448
|
`pnpm build:bare` 输出到 `dist/packages/`:
|
|
@@ -239,6 +453,7 @@ import type {
|
|
|
239
453
|
- `index.css` — 样式
|
|
240
454
|
- `types.d.ts` — TypeScript 类型声明
|
|
241
455
|
- `README.md` / `AGENTS.md` — 使用文档与 AI 指南
|
|
456
|
+
- `examples/` — 业务场景示例
|
|
242
457
|
- `package.json`
|
|
243
458
|
|
|
244
459
|
本地联调可在其他项目中:
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
import { Form, usePageModalContext } from '@wg-song/bare'
|
|
4
|
+
import type { FormField } from '@wg-song/bare'
|
|
5
|
+
|
|
6
|
+
const { row, close, confirmLoading } = usePageModalContext<any>()
|
|
7
|
+
|
|
8
|
+
const isEdit = computed(() => !!row.value)
|
|
9
|
+
|
|
10
|
+
const schemas: FormField[] = [
|
|
11
|
+
{ field: 'roleName', label: '角色名称', component: 'input', required: true },
|
|
12
|
+
{ field: 'roleCode', label: '角色编码', component: 'input', required: true },
|
|
13
|
+
{
|
|
14
|
+
field: 'status',
|
|
15
|
+
label: '状态',
|
|
16
|
+
component: 'select',
|
|
17
|
+
options: [
|
|
18
|
+
{ label: '启用', value: 1 },
|
|
19
|
+
{ label: '禁用', value: 0 },
|
|
20
|
+
],
|
|
21
|
+
},
|
|
22
|
+
{ field: 'remark', label: '备注', component: 'textarea' },
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
async function submit() {
|
|
26
|
+
confirmLoading.value = true
|
|
27
|
+
try {
|
|
28
|
+
// 业务侧调用保存 API,例如 await saveRole(row.value)
|
|
29
|
+
await new Promise((resolve) => setTimeout(resolve, 500))
|
|
30
|
+
close({ success: true })
|
|
31
|
+
} finally {
|
|
32
|
+
confirmLoading.value = false
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
defineExpose({ submit })
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<template>
|
|
40
|
+
<Form :model="row" :schemas="schemas" />
|
|
41
|
+
</template>
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
import { PageList, confirm } from '@wg-song/bare'
|
|
4
|
+
import type {
|
|
5
|
+
FormField,
|
|
6
|
+
TableColumn,
|
|
7
|
+
ActionItem,
|
|
8
|
+
PageListProps,
|
|
9
|
+
PageListToolbarContext,
|
|
10
|
+
} from '@wg-song/bare'
|
|
11
|
+
import RoleDetail from './RoleDetail.vue'
|
|
12
|
+
|
|
13
|
+
const pageListRef = ref<InstanceType<typeof PageList> | null>(null)
|
|
14
|
+
|
|
15
|
+
const searchSchemas: FormField[] = [
|
|
16
|
+
{ field: 'roleName', label: '角色名称', component: 'input' },
|
|
17
|
+
{
|
|
18
|
+
field: 'status',
|
|
19
|
+
label: '状态',
|
|
20
|
+
component: 'select',
|
|
21
|
+
options: [
|
|
22
|
+
{ label: '启用', value: 1 },
|
|
23
|
+
{ label: '禁用', value: 0 },
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
const columns: TableColumn[] = [
|
|
29
|
+
{ field: 'roleName', title: '角色名称', component: 'link' },
|
|
30
|
+
{ field: 'roleCode', title: '角色编码' },
|
|
31
|
+
{
|
|
32
|
+
field: 'status',
|
|
33
|
+
title: '状态',
|
|
34
|
+
component: 'tag',
|
|
35
|
+
tagColor: ({ value }) => (value === 1 ? 'success' : 'default'),
|
|
36
|
+
},
|
|
37
|
+
{ field: 'updateTime', title: '更新时间' },
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
// 模拟后端分页数据
|
|
41
|
+
const allRows = Array.from({ length: 35 }).map((_, i) => ({
|
|
42
|
+
id: i + 1,
|
|
43
|
+
roleName: `角色 ${i + 1}`,
|
|
44
|
+
roleCode: `ROLE_${String(i + 1).padStart(3, '0')}`,
|
|
45
|
+
status: i % 3 === 0 ? 0 : 1,
|
|
46
|
+
updateTime: '2024-01-01 12:00:00',
|
|
47
|
+
}))
|
|
48
|
+
|
|
49
|
+
function toolbarActions(ctx: PageListToolbarContext): ActionItem[] {
|
|
50
|
+
return [
|
|
51
|
+
{
|
|
52
|
+
label: '新增',
|
|
53
|
+
type: 'primary',
|
|
54
|
+
onClick: () => pageListRef.value?.openDetail(null),
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
label: '批量删除',
|
|
58
|
+
danger: true,
|
|
59
|
+
disabled: ctx.selectedCount === 0,
|
|
60
|
+
onClick: async () => {
|
|
61
|
+
const rows = ctx.getSelectedRows()
|
|
62
|
+
const ok = await confirm(`确认删除选中的 ${rows.length} 项?`)
|
|
63
|
+
if (!ok) return
|
|
64
|
+
// 业务侧调用删除 API 后刷新
|
|
65
|
+
pageListRef.value?.reload()
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function rowActions(row: any): ActionItem[] {
|
|
72
|
+
return [
|
|
73
|
+
{ label: '编辑', onClick: () => pageListRef.value?.openDetail(row) },
|
|
74
|
+
{
|
|
75
|
+
label: '删除',
|
|
76
|
+
danger: true,
|
|
77
|
+
onClick: async () => {
|
|
78
|
+
const ok = await confirm('确认删除该角色?')
|
|
79
|
+
if (!ok) return
|
|
80
|
+
// 业务侧调用删除 API 后刷新
|
|
81
|
+
pageListRef.value?.reload()
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const page: PageListProps['page'] = {
|
|
88
|
+
tableId: 'role-management',
|
|
89
|
+
search: searchSchemas,
|
|
90
|
+
columns,
|
|
91
|
+
loader: async ({ params, page, pageSize }) => {
|
|
92
|
+
// 模拟 API 延迟
|
|
93
|
+
await new Promise((resolve) => setTimeout(resolve, 300))
|
|
94
|
+
const filtered = allRows.filter((r) => {
|
|
95
|
+
if (params.roleName && !r.roleName.includes(String(params.roleName))) return false
|
|
96
|
+
if (params.status != null && r.status !== Number(params.status)) return false
|
|
97
|
+
return true
|
|
98
|
+
})
|
|
99
|
+
const start = (page - 1) * pageSize
|
|
100
|
+
return {
|
|
101
|
+
list: filtered.slice(start, start + pageSize),
|
|
102
|
+
total: filtered.length,
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
toolbarActions,
|
|
106
|
+
rowActions,
|
|
107
|
+
detailComponent: RoleDetail,
|
|
108
|
+
detailTitle: '角色详情',
|
|
109
|
+
detailWidth: 600,
|
|
110
|
+
}
|
|
111
|
+
</script>
|
|
112
|
+
|
|
113
|
+
<template>
|
|
114
|
+
<PageList ref="pageListRef" :page="page" />
|
|
115
|
+
</template>
|