create-yoya-ui 0.4.1 → 0.6.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/package.json +1 -1
- package/templates/admin/RULES.md +22 -9
- package/templates/admin/package.json +1 -1
- package/templates/admin/src/api/domain.api.d.ts +1 -1
- package/templates/admin/src/api/domain.api.js +1 -1
- package/templates/admin/src/api/fetch.api.d.ts +1 -1
- package/templates/admin/src/features/dashboard/overview/pages/dashboard-overview-page.js +2 -2
- package/templates/admin/src/features/system/dicts/api/dict.mgr.d.ts +1 -1
- package/templates/admin/src/features/system/dicts/api/dict.mgr.js +1 -1
- package/templates/admin/src/features/system/dicts/api/dict.state.d.ts +14 -11
- package/templates/admin/src/features/system/dicts/api/dict.state.js +35 -70
- package/templates/admin/src/features/system/dicts/api/dict.views.d.ts +36 -23
- package/templates/admin/src/features/system/dicts/api/dict.views.js +36 -14
- package/templates/admin/src/features/system/dicts/components/dict-editor-dialog.js +53 -73
- package/templates/admin/src/features/system/dicts/components/dict-item-form-dialog.js +9 -6
- package/templates/admin/src/features/system/dicts/pages/dict-list-page.js +57 -59
- package/templates/admin/src/features/system/members/api/member.mgr.d.ts +1 -1
- package/templates/admin/src/features/system/members/api/member.mgr.js +1 -1
- package/templates/admin/src/features/system/members/api/member.req.d.ts +1 -1
- package/templates/admin/src/features/system/members/api/member.req.js +1 -1
- package/templates/admin/src/features/system/members/api/member.state.d.ts +10 -8
- package/templates/admin/src/features/system/members/api/member.state.js +24 -55
- package/templates/admin/src/features/system/members/api/member.views.d.ts +19 -5
- package/templates/admin/src/features/system/members/api/member.views.js +22 -8
- package/templates/admin/src/features/system/members/components/member-form-dialog.js +10 -6
- package/templates/admin/src/features/system/members/components/member-table.js +44 -54
- package/templates/admin/src/features/system/members/pages/member-list-page.js +19 -14
- package/templates/admin/src/features/system/permissions/api/permission.mgr.d.ts +1 -1
- package/templates/admin/src/features/system/permissions/api/permission.mgr.js +1 -1
- package/templates/admin/src/features/system/permissions/api/permission.state.d.ts +5 -3
- package/templates/admin/src/features/system/permissions/api/permission.state.js +4 -18
- package/templates/admin/src/features/system/permissions/api/permission.views.js +2 -1
- package/templates/admin/src/features/system/permissions/components/permission-form-dialog.js +1 -1
- package/templates/admin/src/features/system/permissions/pages/permission-list-page.js +9 -6
- package/templates/admin/src/features/system/roles/api/role.mgr.d.ts +1 -1
- package/templates/admin/src/features/system/roles/api/role.mgr.js +1 -1
- package/templates/admin/src/features/system/roles/api/role.state.d.ts +10 -8
- package/templates/admin/src/features/system/roles/api/role.state.js +22 -55
- package/templates/admin/src/features/system/roles/api/role.views.d.ts +24 -15
- package/templates/admin/src/features/system/roles/api/role.views.js +22 -9
- package/templates/admin/src/features/system/roles/components/role-form-dialog.js +11 -7
- package/templates/admin/src/features/system/roles/components/role-table.js +35 -47
- package/templates/admin/src/features/system/roles/pages/role-list-page.js +18 -14
- package/templates/admin/src/shared/state.rows.js +17 -0
- package/templates/admin/src/shell/api/shell.req.js +1 -1
- package/templates/admin/src/shell/api/shell.state.js +23 -55
- package/templates/admin/src/shell/components/admin-shell.js +4 -15
- package/templates/admin/src/shell/components/app-navbar.js +48 -56
- package/templates/admin/src/shell/components/app-sidebar.js +20 -31
- package/templates/admin/src/shell/components/sidebar-menu.js +1 -1
- package/templates/basic/package.json +1 -1
- package/templates/ssr/README.md +1 -1
- package/templates/ssr/package.json +1 -1
- package/templates/ssr/src/server.mjs +3 -0
|
@@ -1,16 +1,30 @@
|
|
|
1
|
-
//
|
|
1
|
+
// 领域结果结构(纯数据):字段按热度分——行内会展示、会原地变化的用句柄,
|
|
2
|
+
// key 与不参与原地刷新的字段保持普通值。类不单独 export,由默认导出命名空间提供。
|
|
3
|
+
import { ref } from '@yoyaflow/yoya-ui';
|
|
4
|
+
import { fieldValue } from '../../../../shared/state.rows.js';
|
|
5
|
+
|
|
2
6
|
class ListItem {
|
|
3
|
-
constructor(
|
|
4
|
-
this.id = id;
|
|
5
|
-
this.name = name;
|
|
6
|
-
this.email = email;
|
|
7
|
-
this.role = role;
|
|
8
|
-
this.status = status;
|
|
7
|
+
constructor(row = {}) {
|
|
8
|
+
this.id = row.id; // key:普通值,不能是句柄
|
|
9
|
+
this.name = ref(fieldValue(row.name) ?? ''); // 热字段:句柄 → 只刷那一格
|
|
10
|
+
this.email = ref(fieldValue(row.email) ?? '');
|
|
11
|
+
this.role = ref(fieldValue(row.role) ?? 'viewer');
|
|
12
|
+
this.status = ref(fieldValue(row.status) ?? 'active');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** 合并而非重建:实例身份不变,keyed 复用行节点,只刷句柄绑定的位置。 */
|
|
16
|
+
apply(row) {
|
|
17
|
+
this.name.value = fieldValue(row.name) ?? '';
|
|
18
|
+
this.email.value = fieldValue(row.email) ?? '';
|
|
19
|
+
this.role.value = fieldValue(row.role) ?? 'viewer';
|
|
20
|
+
this.status.value = fieldValue(row.status) ?? 'active';
|
|
21
|
+
return this;
|
|
9
22
|
}
|
|
10
23
|
}
|
|
11
24
|
|
|
25
|
+
// 详情结构不参与列表对账,按普通值持有即可。
|
|
12
26
|
class Detail {
|
|
13
|
-
constructor({ id, name, email, role, status, remark = '' }) {
|
|
27
|
+
constructor({ id, name, email, role, status, remark = '' } = {}) {
|
|
14
28
|
this.id = id;
|
|
15
29
|
this.name = name;
|
|
16
30
|
this.email = email;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { vDialog } from '@yoyaflow/yoya-ui';
|
|
2
|
+
import { fieldValue } from '../../../../shared/state.rows.js';
|
|
2
3
|
import { roleOptions, statusOptions } from '../utils/options.js';
|
|
3
4
|
|
|
4
5
|
const rowStyle = {
|
|
@@ -14,6 +15,11 @@ export function MemberFormDialog({ onSubmit }) {
|
|
|
14
15
|
let editingId = null;
|
|
15
16
|
|
|
16
17
|
function buildFormFields(form, member) {
|
|
18
|
+
const name = fieldValue(member?.name) ?? '';
|
|
19
|
+
const email = fieldValue(member?.email) ?? '';
|
|
20
|
+
const role = fieldValue(member?.role) ?? 'viewer';
|
|
21
|
+
const status = fieldValue(member?.status) ?? 'active';
|
|
22
|
+
|
|
17
23
|
form.styles({
|
|
18
24
|
border: '1px solid var(--yoya-color-border, #d8dee8)',
|
|
19
25
|
borderRadius: '8px',
|
|
@@ -25,19 +31,17 @@ export function MemberFormDialog({ onSubmit }) {
|
|
|
25
31
|
form.vFormItem((item) => {
|
|
26
32
|
item.styles(rowStyle);
|
|
27
33
|
item.label('姓名').name('name').required({ message: '请填写姓名' });
|
|
28
|
-
item.control((editor) => editor.vInput({ name: 'name', value:
|
|
34
|
+
item.control((editor) => editor.vInput({ name: 'name', value: name }));
|
|
29
35
|
});
|
|
30
36
|
form.vFormItem((item) => {
|
|
31
37
|
item.styles(rowStyle);
|
|
32
38
|
item.label('邮箱').name('email').required({ message: '请填写邮箱' });
|
|
33
|
-
item.control((editor) => editor.vInput({ name: 'email', value:
|
|
39
|
+
item.control((editor) => editor.vInput({ name: 'email', value: email }));
|
|
34
40
|
});
|
|
35
41
|
form.vFormItem((item) => {
|
|
36
42
|
item.styles(rowStyle);
|
|
37
43
|
item.label('角色').name('role');
|
|
38
|
-
item.control((editor) =>
|
|
39
|
-
editor.vSelect({ name: 'role', options: roleOptions, value: member?.role ?? 'viewer' })
|
|
40
|
-
);
|
|
44
|
+
item.control((editor) => editor.vSelect({ name: 'role', options: roleOptions, value: role }));
|
|
41
45
|
});
|
|
42
46
|
form.vFormItem((item) => {
|
|
43
47
|
item.styles(rowStyle);
|
|
@@ -46,7 +50,7 @@ export function MemberFormDialog({ onSubmit }) {
|
|
|
46
50
|
editor.vSelect({
|
|
47
51
|
name: 'status',
|
|
48
52
|
options: statusOptions,
|
|
49
|
-
value:
|
|
53
|
+
value: status
|
|
50
54
|
})
|
|
51
55
|
);
|
|
52
56
|
});
|
|
@@ -1,64 +1,54 @@
|
|
|
1
|
-
import { vTable } from '@yoyaflow/yoya-ui';
|
|
1
|
+
import { computed, vTable, vText, vTr } from '@yoyaflow/yoya-ui';
|
|
2
2
|
import { RowActionButton } from '../../../../shared/ui.buttons.js';
|
|
3
3
|
import { statusText } from '../utils/options.js';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* 成员表格:rows 是状态里的 ref 句柄,写入即按 key 对账——
|
|
7
|
+
* 能复用的行不重建,行内热字段只刷自己那一格,组件不需要对外暴露 refresh()。
|
|
8
|
+
*/
|
|
5
9
|
export function MemberTable({ rows, onEdit, onRemove }) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
rows().forEach((member) => {
|
|
15
|
-
tbody.vTr((row) => {
|
|
16
|
-
row.vTd(member.name);
|
|
17
|
-
row.vTd(member.email);
|
|
18
|
-
row.vTd(member.role);
|
|
19
|
-
row.vTd(statusText[member.status] ?? member.status);
|
|
20
|
-
row.vTd((cell) => {
|
|
21
|
-
cell.hstack({ gap: '8px' }, (actions) => {
|
|
22
|
-
actions.child(
|
|
23
|
-
RowActionButton('编辑', (btn) => {
|
|
24
|
-
btn.access('system:member:update');
|
|
25
|
-
btn.on('click', () => onEdit(member));
|
|
26
|
-
})
|
|
27
|
-
);
|
|
28
|
-
actions.child(
|
|
29
|
-
RowActionButton('删除', (btn) => {
|
|
30
|
-
btn.variant('danger');
|
|
31
|
-
btn.access('system:member:remove');
|
|
32
|
-
btn.on('click', () => onRemove(member));
|
|
33
|
-
})
|
|
34
|
-
);
|
|
35
|
-
});
|
|
36
|
-
});
|
|
10
|
+
return vTable((table) => {
|
|
11
|
+
table.vThead((head) => {
|
|
12
|
+
head.vTr((row) => {
|
|
13
|
+
row.vTh('姓名');
|
|
14
|
+
row.vTh('邮箱');
|
|
15
|
+
row.vTh('角色');
|
|
16
|
+
row.vTh('状态');
|
|
17
|
+
row.vTh('操作');
|
|
37
18
|
});
|
|
38
19
|
});
|
|
39
|
-
|
|
20
|
+
table.vTbody((body) => {
|
|
21
|
+
body.keyed(
|
|
22
|
+
rows,
|
|
23
|
+
(member) => member.id,
|
|
24
|
+
(member) => buildRow(member)
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
40
28
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
29
|
+
function buildRow(member) {
|
|
30
|
+
return vTr((row) => {
|
|
31
|
+
row.vTd(vText(member.name));
|
|
32
|
+
row.vTd(vText(member.email));
|
|
33
|
+
row.vTd(vText(member.role));
|
|
34
|
+
row.vTd(vText(computed(() => statusText[member.status.value] ?? member.status.value)));
|
|
35
|
+
row.vTd((cell) => {
|
|
36
|
+
cell.hstack({ gap: '8px' }, (actions) => {
|
|
37
|
+
actions.child(
|
|
38
|
+
RowActionButton('编辑', (btn) => {
|
|
39
|
+
btn.access('system:member:update');
|
|
40
|
+
btn.on('click', () => onEdit(member));
|
|
41
|
+
})
|
|
42
|
+
);
|
|
43
|
+
actions.child(
|
|
44
|
+
RowActionButton('删除', (btn) => {
|
|
45
|
+
btn.variant('danger');
|
|
46
|
+
btn.access('system:member:remove');
|
|
47
|
+
btn.on('click', () => onRemove(member));
|
|
48
|
+
})
|
|
49
|
+
);
|
|
56
50
|
});
|
|
57
51
|
});
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
renderRows();
|
|
61
|
-
return this;
|
|
62
|
-
}
|
|
63
|
-
};
|
|
52
|
+
});
|
|
53
|
+
}
|
|
64
54
|
}
|
|
@@ -4,13 +4,13 @@ import { MemberToolbar } from '../components/member-toolbar.js';
|
|
|
4
4
|
import { MemberTable } from '../components/member-table.js';
|
|
5
5
|
import { MemberFormDialog } from '../components/member-form-dialog.js';
|
|
6
6
|
|
|
7
|
-
//
|
|
7
|
+
// 页面只做编排:状态用 ref 持有视图字段,表格绑句柄后写入即刷新,不需要订阅接线。
|
|
8
8
|
export function MemberListPage() {
|
|
9
9
|
const state = new MembersPageState();
|
|
10
10
|
const dialog = MemberFormDialog({ onSubmit: saveMember });
|
|
11
11
|
const toolbar = MemberToolbar({ onSearch: applyFilters, onAdd: () => dialog.open(null) });
|
|
12
12
|
const table = MemberTable({
|
|
13
|
-
rows:
|
|
13
|
+
rows: state.items,
|
|
14
14
|
onEdit: (member) => dialog.open(member),
|
|
15
15
|
onRemove: removeMemberAction
|
|
16
16
|
});
|
|
@@ -20,22 +20,26 @@ export function MemberListPage() {
|
|
|
20
20
|
onChange({ page: nextPage, pageSize: nextSize }) {
|
|
21
21
|
state.setPage(nextPage);
|
|
22
22
|
state.setPageSize(nextSize);
|
|
23
|
-
|
|
23
|
+
load();
|
|
24
24
|
}
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
load();
|
|
28
|
+
|
|
29
|
+
// vPagination 是命令式组件(不是信号感知的):数据到位后由动作同步一次
|
|
30
|
+
async function load() {
|
|
31
|
+
await state.load();
|
|
29
32
|
syncPagination();
|
|
30
|
-
}
|
|
31
|
-
state.load();
|
|
33
|
+
}
|
|
32
34
|
|
|
33
35
|
function syncPagination() {
|
|
36
|
+
const total = state.total.value;
|
|
37
|
+
const pageSize = state.pageSize.value;
|
|
34
38
|
pagination.update({
|
|
35
|
-
page: state.page
|
|
36
|
-
pageSize
|
|
37
|
-
total
|
|
38
|
-
totalPages: Math.max(1, Math.ceil(
|
|
39
|
+
page: state.page.value,
|
|
40
|
+
pageSize,
|
|
41
|
+
total,
|
|
42
|
+
totalPages: Math.max(1, Math.ceil(total / pageSize))
|
|
39
43
|
});
|
|
40
44
|
}
|
|
41
45
|
|
|
@@ -43,12 +47,12 @@ export function MemberListPage() {
|
|
|
43
47
|
state.setKeyword(values.keyword ?? '');
|
|
44
48
|
state.setStatus(values.status ?? '');
|
|
45
49
|
state.setPage(1);
|
|
46
|
-
|
|
50
|
+
load();
|
|
47
51
|
}
|
|
48
52
|
|
|
49
53
|
async function removeMemberAction(member) {
|
|
50
54
|
await state.remove(member.id);
|
|
51
|
-
toast.success(`已删除 ${member.name}`);
|
|
55
|
+
toast.success(`已删除 ${member.name.value}`);
|
|
52
56
|
}
|
|
53
57
|
|
|
54
58
|
async function saveMember(editingId, payload) {
|
|
@@ -78,8 +82,9 @@ export function MemberListPage() {
|
|
|
78
82
|
stack.child(dialog);
|
|
79
83
|
});
|
|
80
84
|
},
|
|
85
|
+
// 页面也是组件:父级 / 路由可以调用实例方法刷新
|
|
81
86
|
refresh() {
|
|
82
|
-
return
|
|
87
|
+
return load();
|
|
83
88
|
}
|
|
84
89
|
};
|
|
85
90
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** 权限管理请求命令类型声明(对应 permission.mgr.js)。 */
|
|
2
|
-
import { RequestBase } from '@yoyaflow/yoya-ui';
|
|
2
|
+
import { RequestBase } from '@yoyaflow/yoya-ui/api';
|
|
3
3
|
import Permissions, { PermissionType } from './permission.views.js';
|
|
4
4
|
|
|
5
5
|
type PermissionNodeInstance = InstanceType<typeof Permissions.PermissionNode>;
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
/** 权限页状态类类型声明(对应 permission.state.js)。 */
|
|
2
|
+
import type { SignalHandle } from '@yoyaflow/yoya-ui';
|
|
3
|
+
import type { Result } from '@yoyaflow/yoya-ui/api';
|
|
2
4
|
import Permissions from './permission.views.js';
|
|
3
5
|
|
|
4
6
|
type PermissionNodeInstance = InstanceType<typeof Permissions.PermissionNode>;
|
|
5
7
|
|
|
6
8
|
export default class PermissionsPageState {
|
|
7
9
|
constructor();
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
load(): Promise<
|
|
10
|
+
/** 视图字段:权限树整体重建,页面读值后喂给 vTree */
|
|
11
|
+
tree: SignalHandle<PermissionNodeInstance[]>;
|
|
12
|
+
load(): Promise<Result<PermissionNodeInstance[]>>;
|
|
11
13
|
add(parentId: number | null, payload: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
12
14
|
edit(id: number, payload: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
13
15
|
remove(id: number): Promise<void>;
|
|
@@ -1,25 +1,15 @@
|
|
|
1
|
-
//
|
|
1
|
+
// 权限页状态类:权限树用 ref 持有,动作写句柄即完成通知。
|
|
2
|
+
import { ref } from '@yoyaflow/yoya-ui';
|
|
2
3
|
import PermissionMgr from './permission.mgr.js';
|
|
3
4
|
|
|
4
5
|
export default class PermissionsPageState {
|
|
5
6
|
constructor() {
|
|
6
|
-
this.
|
|
7
|
-
this._listeners = new Set();
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
tree() {
|
|
11
|
-
return this._tree;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
subscribe(listener) {
|
|
15
|
-
this._listeners.add(listener);
|
|
16
|
-
return () => this._listeners.delete(listener);
|
|
7
|
+
this.tree = ref([]);
|
|
17
8
|
}
|
|
18
9
|
|
|
19
10
|
async load() {
|
|
20
11
|
const result = await PermissionMgr.QueryTree().submit();
|
|
21
|
-
this.
|
|
22
|
-
this._emit();
|
|
12
|
+
this.tree.value = result.data;
|
|
23
13
|
return result;
|
|
24
14
|
}
|
|
25
15
|
|
|
@@ -39,8 +29,4 @@ export default class PermissionsPageState {
|
|
|
39
29
|
await PermissionMgr.Remove({ id }).submit();
|
|
40
30
|
await this.load();
|
|
41
31
|
}
|
|
42
|
-
|
|
43
|
-
_emit() {
|
|
44
|
-
this._listeners.forEach((listener) => listener());
|
|
45
|
-
}
|
|
46
32
|
}
|
|
@@ -8,10 +8,13 @@ export function PermissionListPage() {
|
|
|
8
8
|
const dialog = PermissionFormDialog({ onSubmit: savePermission });
|
|
9
9
|
const tree = vTree({ ariaLabel: '权限树' });
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
load();
|
|
12
|
+
|
|
13
|
+
// vTree 是命令式组件(整树重建,不做行级对账):数据到位后由动作把 ref 值喂给它
|
|
14
|
+
async function load() {
|
|
15
|
+
await state.load();
|
|
16
|
+
tree.nodes(buildTreeNodes(state.tree.value));
|
|
17
|
+
}
|
|
15
18
|
|
|
16
19
|
function buildTreeNodes(nodes) {
|
|
17
20
|
return nodes.map((node) => ({
|
|
@@ -75,7 +78,7 @@ export function PermissionListPage() {
|
|
|
75
78
|
btn.variant('primary');
|
|
76
79
|
btn.on('click', () => dialog.open({}));
|
|
77
80
|
});
|
|
78
|
-
toolbar.vButton('刷新', (btn) => btn.on('click', () =>
|
|
81
|
+
toolbar.vButton('刷新', (btn) => btn.on('click', () => load()));
|
|
79
82
|
});
|
|
80
83
|
content.child(tree);
|
|
81
84
|
});
|
|
@@ -85,7 +88,7 @@ export function PermissionListPage() {
|
|
|
85
88
|
});
|
|
86
89
|
},
|
|
87
90
|
refresh() {
|
|
88
|
-
return
|
|
91
|
+
return load();
|
|
89
92
|
}
|
|
90
93
|
};
|
|
91
94
|
}
|
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
/** 角色页状态类类型声明(对应 role.state.js)。 */
|
|
2
|
+
import type { SignalHandle } from '@yoyaflow/yoya-ui';
|
|
3
|
+
import type { Result } from '@yoyaflow/yoya-ui/api';
|
|
2
4
|
import Roles from './role.views.js';
|
|
3
5
|
|
|
4
6
|
type RoleListItem = InstanceType<typeof Roles.ListItem>;
|
|
5
7
|
|
|
6
8
|
export default class RolesPageState {
|
|
7
9
|
constructor();
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
page
|
|
12
|
-
pageSize
|
|
13
|
-
|
|
10
|
+
/** 视图字段:页面与组件直接绑句柄,写入即更新 */
|
|
11
|
+
items: SignalHandle<RoleListItem[]>;
|
|
12
|
+
total: SignalHandle<number>;
|
|
13
|
+
page: SignalHandle<number>;
|
|
14
|
+
pageSize: SignalHandle<number>;
|
|
15
|
+
keyword: SignalHandle<string>;
|
|
16
|
+
status: SignalHandle<string>;
|
|
14
17
|
setKeyword(value: string): void;
|
|
15
18
|
setStatus(value: string): void;
|
|
16
19
|
setPage(value: number): void;
|
|
17
20
|
setPageSize(value: number): void;
|
|
18
|
-
|
|
19
|
-
load(): Promise<unknown>;
|
|
21
|
+
load(): Promise<Result<RoleListItem[]>>;
|
|
20
22
|
add(payload: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
21
23
|
edit(id: number, patch: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
22
24
|
remove(id: number): Promise<void>;
|
|
@@ -1,78 +1,49 @@
|
|
|
1
|
-
//
|
|
1
|
+
// 角色页状态类:视图字段用 ref 持有,动作写句柄即完成通知。
|
|
2
|
+
import { ref } from '@yoyaflow/yoya-ui';
|
|
3
|
+
import { mergeRowsByKey } from '../../../../shared/state.rows.js';
|
|
2
4
|
import RoleMgr from './role.mgr.js';
|
|
3
5
|
|
|
4
6
|
export default class RolesPageState {
|
|
5
7
|
constructor() {
|
|
6
|
-
this.
|
|
7
|
-
this.
|
|
8
|
-
this.
|
|
9
|
-
this.
|
|
10
|
-
this.
|
|
11
|
-
this.
|
|
12
|
-
this._listeners = new Set();
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
items() {
|
|
16
|
-
return this._items;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
keyword() {
|
|
20
|
-
return this._keyword;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
status() {
|
|
24
|
-
return this._status;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
page() {
|
|
28
|
-
return this._page;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
pageSize() {
|
|
32
|
-
return this._pageSize;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
total() {
|
|
36
|
-
return this._total;
|
|
8
|
+
this.items = ref([]);
|
|
9
|
+
this.total = ref(0);
|
|
10
|
+
this.page = ref(1);
|
|
11
|
+
this.pageSize = ref(10);
|
|
12
|
+
this.keyword = ref('');
|
|
13
|
+
this.status = ref('');
|
|
37
14
|
}
|
|
38
15
|
|
|
39
16
|
setKeyword(value) {
|
|
40
|
-
this.
|
|
17
|
+
this.keyword.value = value;
|
|
41
18
|
}
|
|
42
19
|
|
|
43
20
|
setStatus(value) {
|
|
44
|
-
this.
|
|
21
|
+
this.status.value = value;
|
|
45
22
|
}
|
|
46
23
|
|
|
47
24
|
setPage(value) {
|
|
48
|
-
this.
|
|
25
|
+
this.page.value = Math.max(1, Number(value) || 1);
|
|
49
26
|
}
|
|
50
27
|
|
|
51
28
|
setPageSize(value) {
|
|
52
|
-
this.
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
subscribe(listener) {
|
|
56
|
-
this._listeners.add(listener);
|
|
57
|
-
return () => this._listeners.delete(listener);
|
|
29
|
+
this.pageSize.value = Math.max(1, Number(value) || 10);
|
|
58
30
|
}
|
|
59
31
|
|
|
60
32
|
async load() {
|
|
61
33
|
const result = await RoleMgr.Query({
|
|
62
|
-
page: this.
|
|
63
|
-
pageSize: this.
|
|
64
|
-
keyword: this.
|
|
65
|
-
status: this.
|
|
34
|
+
page: this.page.value,
|
|
35
|
+
pageSize: this.pageSize.value,
|
|
36
|
+
keyword: this.keyword.value,
|
|
37
|
+
status: this.status.value
|
|
66
38
|
}).submit();
|
|
67
|
-
this.
|
|
68
|
-
this.
|
|
69
|
-
this._emit();
|
|
39
|
+
this.items.value = mergeRowsByKey(this.items.peek(), result.data);
|
|
40
|
+
this.total.value = result.total;
|
|
70
41
|
return result;
|
|
71
42
|
}
|
|
72
43
|
|
|
73
44
|
async add(payload) {
|
|
74
45
|
await RoleMgr.Create(payload).submit();
|
|
75
|
-
this.
|
|
46
|
+
this.page.value = 1;
|
|
76
47
|
await this.load();
|
|
77
48
|
return payload;
|
|
78
49
|
}
|
|
@@ -85,13 +56,9 @@ export default class RolesPageState {
|
|
|
85
56
|
|
|
86
57
|
async remove(id) {
|
|
87
58
|
await RoleMgr.Remove({ id }).submit();
|
|
88
|
-
if (this.
|
|
89
|
-
this.
|
|
59
|
+
if (this.items.peek().length === 1 && this.page.value > 1) {
|
|
60
|
+
this.page.value -= 1;
|
|
90
61
|
}
|
|
91
62
|
await this.load();
|
|
92
63
|
}
|
|
93
|
-
|
|
94
|
-
_emit() {
|
|
95
|
-
this._listeners.forEach((listener) => listener());
|
|
96
|
-
}
|
|
97
64
|
}
|
|
@@ -1,21 +1,30 @@
|
|
|
1
1
|
/** 角色结果结构类型声明(对应 role.views.js)。 */
|
|
2
|
+
import type { SignalHandle } from '@yoyaflow/yoya-ui';
|
|
3
|
+
|
|
4
|
+
/** 角色状态:与 utils/options.js 的 statusOptions 对齐 */
|
|
2
5
|
export type RoleStatus = 'active' | 'disabled';
|
|
3
6
|
|
|
7
|
+
export type RoleListRow = {
|
|
8
|
+
id: number;
|
|
9
|
+
name?: string;
|
|
10
|
+
code?: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
status?: RoleStatus;
|
|
13
|
+
sort?: number;
|
|
14
|
+
};
|
|
15
|
+
|
|
4
16
|
export class ListItem {
|
|
17
|
+
/** key:普通值,不能是句柄 */
|
|
5
18
|
id: number;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
19
|
+
/** 行内热字段:句柄写入即刷新对应单元格 */
|
|
20
|
+
name: SignalHandle<string>;
|
|
21
|
+
code: SignalHandle<string>;
|
|
22
|
+
description: SignalHandle<string>;
|
|
23
|
+
status: SignalHandle<string>;
|
|
24
|
+
/** 冷字段:表格不展示,随行结构变化刷新 */
|
|
10
25
|
sort: number;
|
|
11
|
-
constructor(
|
|
12
|
-
|
|
13
|
-
name: string;
|
|
14
|
-
code: string;
|
|
15
|
-
description?: string;
|
|
16
|
-
status?: RoleStatus;
|
|
17
|
-
sort?: number;
|
|
18
|
-
});
|
|
26
|
+
constructor(row?: RoleListRow);
|
|
27
|
+
apply(row: RoleListRow): this;
|
|
19
28
|
}
|
|
20
29
|
|
|
21
30
|
export class Detail {
|
|
@@ -23,14 +32,14 @@ export class Detail {
|
|
|
23
32
|
name: string;
|
|
24
33
|
code: string;
|
|
25
34
|
description: string;
|
|
26
|
-
status:
|
|
35
|
+
status: string;
|
|
27
36
|
sort: number;
|
|
28
|
-
constructor(init
|
|
37
|
+
constructor(init: {
|
|
29
38
|
id: number;
|
|
30
39
|
name: string;
|
|
31
40
|
code: string;
|
|
32
41
|
description?: string;
|
|
33
|
-
status?:
|
|
42
|
+
status?: string;
|
|
34
43
|
sort?: number;
|
|
35
44
|
});
|
|
36
45
|
}
|