@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 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
- > 注意:若使用本库内置的 Vite 应用演示工程,安装时已自动调用 `setupModal(app._context)`,接入方无需手动处理命令式弹窗上下文。
51
+ > 若未注册 `vxe-table`,表格组件会报 `[Vue warn]: Failed to resolve component: vxe-grid`。开发环境下本库 `install` 阶段会在控制台给出明确提示。
39
52
 
40
- ## 组件总览
53
+ ## 组件能力矩阵
41
54
 
42
- ### 表单 Form
55
+ ### 场景组件
43
56
 
44
57
  | 组件 / API | 说明 |
45
58
  |---|---|
46
- | `Form` | Schema 驱动表单 |
47
- | `SearchForm` | 搜索表单(含高级搜索、FilterBar) |
48
- | `SearchFormSimple` | 简易单行搜索 |
49
- | `DialogForm` | 弹窗表单场景 |
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
- | `Pagination` | 分页 |
63
-
64
- ### 列表页 PageList
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
- ### 弹窗 Modal
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
- | `openModal` / `openDrawer` | 命令式打开弹窗/抽屉 |
81
- | `confirm` / `alert` | 命令式确认/提示 |
82
- | `PageModals` | 页面级弹窗管理器 |
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
- ### 标签页 Tabs
94
+ ### Hooks / 工具函数
85
95
 
86
- | 组件 / API | 说明 |
96
+ | API | 说明 |
87
97
  |---|---|
88
- | `Tabs` | 基础标签页 |
89
- | `TabsView` / `PageTabs` | 页签视图 |
90
- | `PageListTabs` | 标签页 + 列表组合 |
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 loader: PageShellProps['loader'] = async ({ query, currentPage, pageSize }) => {
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 :search-schemas="searchSchemas" :loader="loader" :toolbar-actions="toolbarActions">
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>