create-yoya-ui 0.1.1 → 0.2.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 +3 -0
- package/bin/create-yoya-ui.js +4 -2
- package/package.json +1 -1
- package/templates/admin/README.md +77 -0
- package/templates/admin/index.html +12 -0
- package/templates/admin/node_modules/.vite/deps/_metadata.json +8 -0
- package/templates/admin/node_modules/.vite/deps/package.json +3 -0
- package/templates/admin/package.json +19 -0
- package/templates/admin/src/api/domain.api.d.ts +10 -0
- package/templates/admin/src/api/domain.api.js +31 -0
- package/templates/admin/src/api/fetch.api.d.ts +4 -0
- package/templates/admin/src/api/fetch.api.js +20 -0
- package/templates/admin/src/app/admin-shell.js +51 -0
- package/templates/admin/src/app/modules.js +37 -0
- package/templates/admin/src/app/router.js +20 -0
- package/templates/admin/src/features/dashboard/components/dashboard-chart.js +205 -0
- package/templates/admin/src/features/dashboard/pages/dashboard-overview-page.js +197 -0
- package/templates/admin/src/features/dicts/api/dict.mgr.d.ts +79 -0
- package/templates/admin/src/features/dicts/api/dict.mgr.js +160 -0
- package/templates/admin/src/features/dicts/api/dict.mock.js +81 -0
- package/templates/admin/src/features/dicts/api/dict.state.d.ts +23 -0
- package/templates/admin/src/features/dicts/api/dict.state.js +103 -0
- package/templates/admin/src/features/dicts/api/dict.views.d.ts +41 -0
- package/templates/admin/src/features/dicts/api/dict.views.js +23 -0
- package/templates/admin/src/features/dicts/components/dict-editor-dialog.js +218 -0
- package/templates/admin/src/features/dicts/components/dict-item-form-dialog.js +79 -0
- package/templates/admin/src/features/dicts/pages/dict-list-page.js +123 -0
- package/templates/admin/src/features/dicts/utils/options.js +5 -0
- package/templates/admin/src/features/members/api/member.mgr.d.ts +47 -0
- package/templates/admin/src/features/members/api/member.mgr.js +95 -0
- package/templates/admin/src/features/members/api/member.mock.js +74 -0
- package/templates/admin/src/features/members/api/member.req.d.ts +16 -0
- package/templates/admin/src/features/members/api/member.req.js +15 -0
- package/templates/admin/src/features/members/api/member.state.d.ts +23 -0
- package/templates/admin/src/features/members/api/member.state.js +97 -0
- package/templates/admin/src/features/members/api/member.views.d.ts +33 -0
- package/templates/admin/src/features/members/api/member.views.js +23 -0
- package/templates/admin/src/features/members/components/member-form-dialog.js +83 -0
- package/templates/admin/src/features/members/components/member-table.js +58 -0
- package/templates/admin/src/features/members/components/member-toolbar.js +58 -0
- package/templates/admin/src/features/members/pages/member-list-page.js +85 -0
- package/templates/admin/src/features/members/utils/options.d.ts +4 -0
- package/templates/admin/src/features/members/utils/options.js +6 -0
- package/templates/admin/src/features/permissions/api/permission.mgr.d.ts +45 -0
- package/templates/admin/src/features/permissions/api/permission.mgr.js +79 -0
- package/templates/admin/src/features/permissions/api/permission.mock.js +68 -0
- package/templates/admin/src/features/permissions/api/permission.state.d.ts +14 -0
- package/templates/admin/src/features/permissions/api/permission.state.js +46 -0
- package/templates/admin/src/features/permissions/api/permission.views.d.ts +27 -0
- package/templates/admin/src/features/permissions/api/permission.views.js +14 -0
- package/templates/admin/src/features/permissions/components/permission-form-dialog.js +98 -0
- package/templates/admin/src/features/permissions/pages/permission-list-page.js +98 -0
- package/templates/admin/src/features/roles/api/role.mgr.d.ts +57 -0
- package/templates/admin/src/features/roles/api/role.mgr.js +101 -0
- package/templates/admin/src/features/roles/api/role.mock.js +68 -0
- package/templates/admin/src/features/roles/api/role.state.d.ts +23 -0
- package/templates/admin/src/features/roles/api/role.state.js +97 -0
- package/templates/admin/src/features/roles/api/role.views.d.ts +43 -0
- package/templates/admin/src/features/roles/api/role.views.js +24 -0
- package/templates/admin/src/features/roles/components/role-form-dialog.js +86 -0
- package/templates/admin/src/features/roles/components/role-table.js +57 -0
- package/templates/admin/src/features/roles/components/role-toolbar.js +58 -0
- package/templates/admin/src/features/roles/pages/role-list-page.js +100 -0
- package/templates/admin/src/features/roles/utils/options.js +5 -0
- package/templates/admin/src/main.js +18 -0
- package/templates/admin/src/ui/app-navbar.js +67 -0
- package/templates/admin/src/ui/app-sidebar.js +27 -0
- package/templates/admin/src/ui/placeholder-page.js +17 -0
- package/templates/admin/src/ui/row-action-button.js +12 -0
- package/templates/admin/src/ui/sidebar-menu.js +14 -0
- package/templates/admin/tsconfig.json +18 -0
- package/templates/admin/vite.config.js +7 -0
- package/templates/basic/package.json +1 -1
- package/templates/ssr/package.json +1 -1
- package/templates/ssr/src/server.mjs +0 -1
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { vGauge, vRingStat, vTimeline, vTrendCard, vstack } from '@yoyaflow/yoya-ui';
|
|
2
|
+
import { DashboardChart } from '../components/dashboard-chart.js';
|
|
3
|
+
|
|
4
|
+
const weekLabels = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
|
|
5
|
+
|
|
6
|
+
export function DashboardOverviewPage() {
|
|
7
|
+
return {
|
|
8
|
+
render() {
|
|
9
|
+
return vstack({ gap: '16px' }, (stack) => {
|
|
10
|
+
stack.h2('数据概览');
|
|
11
|
+
|
|
12
|
+
stack.vCard((card) => {
|
|
13
|
+
card.vCardHeader('核心指标');
|
|
14
|
+
card.vCardBody((body) => {
|
|
15
|
+
body.vDigitalBoard((board) => {
|
|
16
|
+
board.columns(4);
|
|
17
|
+
board.vDigitalBoardItem((item) => {
|
|
18
|
+
item.label('服务总数');
|
|
19
|
+
item.value('128');
|
|
20
|
+
item.unit('个');
|
|
21
|
+
item.trend('+12 本月');
|
|
22
|
+
item.trendUp(true);
|
|
23
|
+
item.icon('🖥️');
|
|
24
|
+
});
|
|
25
|
+
board.vDigitalBoardItem((item) => {
|
|
26
|
+
item.label('运行中');
|
|
27
|
+
item.value('96');
|
|
28
|
+
item.unit('个');
|
|
29
|
+
item.trend('+8 本周');
|
|
30
|
+
item.trendUp(true);
|
|
31
|
+
item.tone('success');
|
|
32
|
+
item.icon('✅');
|
|
33
|
+
});
|
|
34
|
+
board.vDigitalBoardItem((item) => {
|
|
35
|
+
item.label('异常告警');
|
|
36
|
+
item.value('5');
|
|
37
|
+
item.unit('个');
|
|
38
|
+
item.trend('-2 今日');
|
|
39
|
+
item.trendUp(false);
|
|
40
|
+
item.tone('danger');
|
|
41
|
+
item.icon('⚠️');
|
|
42
|
+
});
|
|
43
|
+
board.vDigitalBoardItem((item) => {
|
|
44
|
+
item.label('平均响应');
|
|
45
|
+
item.value('243');
|
|
46
|
+
item.unit('ms');
|
|
47
|
+
item.trend('-18 今日');
|
|
48
|
+
item.trendUp(true);
|
|
49
|
+
item.tone('warning');
|
|
50
|
+
item.icon('⏱️');
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
stack.div((trends) => {
|
|
57
|
+
trends.style({
|
|
58
|
+
display: 'grid',
|
|
59
|
+
gap: '12px',
|
|
60
|
+
gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))'
|
|
61
|
+
});
|
|
62
|
+
trends.child(
|
|
63
|
+
vTrendCard({
|
|
64
|
+
data: [12, 15, 11, 18, 16, 21, 24],
|
|
65
|
+
delta: '+6.4%',
|
|
66
|
+
title: '今日请求',
|
|
67
|
+
tone: 'primary',
|
|
68
|
+
unit: 'k',
|
|
69
|
+
up: true,
|
|
70
|
+
value: '84.2'
|
|
71
|
+
})
|
|
72
|
+
);
|
|
73
|
+
trends.child(
|
|
74
|
+
vTrendCard({
|
|
75
|
+
data: [320, 290, 310, 270, 260, 248, 243],
|
|
76
|
+
delta: '-18 今日',
|
|
77
|
+
title: '平均响应',
|
|
78
|
+
tone: 'success',
|
|
79
|
+
unit: 'ms',
|
|
80
|
+
up: true,
|
|
81
|
+
value: '243'
|
|
82
|
+
})
|
|
83
|
+
);
|
|
84
|
+
trends.child(
|
|
85
|
+
vTrendCard({
|
|
86
|
+
data: [6, 7, 4, 3, 5, 2, 2],
|
|
87
|
+
delta: '-5 较昨日',
|
|
88
|
+
title: '故障次数',
|
|
89
|
+
tone: 'danger',
|
|
90
|
+
unit: '次',
|
|
91
|
+
up: false,
|
|
92
|
+
value: '2'
|
|
93
|
+
})
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
stack.div((charts) => {
|
|
98
|
+
charts.style({
|
|
99
|
+
display: 'grid',
|
|
100
|
+
gap: '12px',
|
|
101
|
+
gridTemplateColumns: 'repeat(auto-fit, minmax(320px, 1fr))'
|
|
102
|
+
});
|
|
103
|
+
charts.child(
|
|
104
|
+
DashboardChart({
|
|
105
|
+
labels: weekLabels,
|
|
106
|
+
series: [{ name: '请求量', data: [62, 71, 58, 84, 92, 76, 88] }],
|
|
107
|
+
title: '近 7 日请求量',
|
|
108
|
+
type: 'bar'
|
|
109
|
+
})
|
|
110
|
+
);
|
|
111
|
+
charts.child(
|
|
112
|
+
DashboardChart({
|
|
113
|
+
labels: weekLabels,
|
|
114
|
+
series: [
|
|
115
|
+
{ name: '错误数', data: [8, 12, 7, 15, 10, 6, 9], color: '#dc2626' },
|
|
116
|
+
{ name: '恢复数', data: [6, 10, 5, 12, 8, 5, 8], color: '#16a34a' }
|
|
117
|
+
],
|
|
118
|
+
title: '近 7 日错误与恢复',
|
|
119
|
+
type: 'line'
|
|
120
|
+
})
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
stack.div((bottom) => {
|
|
125
|
+
bottom.style({
|
|
126
|
+
display: 'grid',
|
|
127
|
+
gap: '12px',
|
|
128
|
+
gridTemplateColumns: 'repeat(auto-fit, minmax(320px, 1fr))'
|
|
129
|
+
});
|
|
130
|
+
bottom.vCard((card) => {
|
|
131
|
+
card.vCardHeader('资源使用');
|
|
132
|
+
card.vCardBody((body) => {
|
|
133
|
+
body.hstack((row) => {
|
|
134
|
+
row.style({ flexWrap: 'wrap', gap: '20px' });
|
|
135
|
+
row.child(vRingStat({ label: '成功率', percent: 99, tone: 'success' }));
|
|
136
|
+
row.child(
|
|
137
|
+
vRingStat({ label: '容量使用', percent: 68, tone: 'primary', value: '680 GB' })
|
|
138
|
+
);
|
|
139
|
+
row.div((box) => {
|
|
140
|
+
box.style({ textAlign: 'center' });
|
|
141
|
+
box.child(vGauge({ unit: '%', value: 72, tone: 'warning' }));
|
|
142
|
+
box.p((text) => {
|
|
143
|
+
text.style({ color: 'var(--yoya-color-text-muted, #64748b)', fontSize: '12px' });
|
|
144
|
+
text.text('CPU 使用率');
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
row.div((box) => {
|
|
148
|
+
box.style({ textAlign: 'center' });
|
|
149
|
+
box.child(vGauge({ unit: '%', value: 58, tone: 'primary' }));
|
|
150
|
+
box.p((text) => {
|
|
151
|
+
text.style({ color: 'var(--yoya-color-text-muted, #64748b)', fontSize: '12px' });
|
|
152
|
+
text.text('内存使用率');
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
bottom.vCard((card) => {
|
|
159
|
+
card.vCardHeader('最近动态');
|
|
160
|
+
card.vCardBody((body) => {
|
|
161
|
+
body.child(
|
|
162
|
+
vTimeline((timeline) => {
|
|
163
|
+
timeline.vTimelineItem((item) => {
|
|
164
|
+
item.status('success');
|
|
165
|
+
item.title('服务发布成功');
|
|
166
|
+
item.time('09:32 · 2026-09-01');
|
|
167
|
+
item.content((box) =>
|
|
168
|
+
box.p('api-gateway v2.4.1 已上线,金丝雀批次全部通过。')
|
|
169
|
+
);
|
|
170
|
+
});
|
|
171
|
+
timeline.vTimelineItem((item) => {
|
|
172
|
+
item.status('processing');
|
|
173
|
+
item.title('滚动发布中');
|
|
174
|
+
item.time('09:20 · 2026-09-01');
|
|
175
|
+
item.content('web-console 正在按 5% 批次滚动。');
|
|
176
|
+
});
|
|
177
|
+
timeline.vTimelineItem((item) => {
|
|
178
|
+
item.status('warning');
|
|
179
|
+
item.title('容量预警');
|
|
180
|
+
item.time('08:47 · 2026-09-01');
|
|
181
|
+
item.content('redis 内存使用率接近 80%。');
|
|
182
|
+
});
|
|
183
|
+
timeline.vTimelineItem((item) => {
|
|
184
|
+
item.status('danger');
|
|
185
|
+
item.title('告警已恢复');
|
|
186
|
+
item.time('08:15 · 2026-09-01');
|
|
187
|
+
item.content('数据库连接池告警自动恢复。');
|
|
188
|
+
});
|
|
189
|
+
})
|
|
190
|
+
);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/** 字典管理请求命令类型声明(对应 dict.mgr.js)。 */
|
|
2
|
+
import { RequestBase } from '@yoyaflow/yoya-ui';
|
|
3
|
+
import Dicts, { DictStatus } from './dict.views.js';
|
|
4
|
+
|
|
5
|
+
type DictTypeInstance = InstanceType<typeof Dicts.DictTypeItem>;
|
|
6
|
+
type DictItemInstance = InstanceType<typeof Dicts.DictItem>;
|
|
7
|
+
|
|
8
|
+
declare class QueryTypes extends RequestBase {
|
|
9
|
+
address(): string;
|
|
10
|
+
toItem(row: Record<string, unknown>): DictTypeInstance;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
declare class CreateType extends RequestBase {
|
|
14
|
+
constructor(init?: { name?: string; code?: string; status?: DictStatus; remark?: string });
|
|
15
|
+
method(): 'POST';
|
|
16
|
+
address(): string;
|
|
17
|
+
body(): { name: string; code: string; status: DictStatus; remark: string };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
declare class UpdateType extends RequestBase {
|
|
21
|
+
constructor(
|
|
22
|
+
id: number,
|
|
23
|
+
init?: { name?: string; code?: string; status?: DictStatus; remark?: string }
|
|
24
|
+
);
|
|
25
|
+
method(): 'PUT';
|
|
26
|
+
address(): string;
|
|
27
|
+
body(): { name: string; code: string; status: DictStatus; remark: string };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
declare class RemoveType extends RequestBase {
|
|
31
|
+
constructor(id: number);
|
|
32
|
+
method(): 'DELETE';
|
|
33
|
+
address(): string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
declare class QueryItems extends RequestBase {
|
|
37
|
+
constructor(typeId: number);
|
|
38
|
+
address(): string;
|
|
39
|
+
toItem(row: Record<string, unknown>): DictItemInstance;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
declare class CreateItem extends RequestBase {
|
|
43
|
+
constructor(
|
|
44
|
+
typeId: number,
|
|
45
|
+
init?: { label?: string; value?: string; sort?: number; status?: DictStatus }
|
|
46
|
+
);
|
|
47
|
+
method(): 'POST';
|
|
48
|
+
address(): string;
|
|
49
|
+
body(): { label: string; value: string; sort: number; status: DictStatus };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
declare class UpdateItem extends RequestBase {
|
|
53
|
+
constructor(
|
|
54
|
+
id: number,
|
|
55
|
+
init?: { label?: string; value?: string; sort?: number; status?: DictStatus }
|
|
56
|
+
);
|
|
57
|
+
method(): 'PUT';
|
|
58
|
+
address(): string;
|
|
59
|
+
body(): { label: string; value: string; sort: number; status: DictStatus };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
declare class RemoveItem extends RequestBase {
|
|
63
|
+
constructor(id: number);
|
|
64
|
+
method(): 'DELETE';
|
|
65
|
+
address(): string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
declare const DictMgr: {
|
|
69
|
+
QueryTypes: typeof QueryTypes;
|
|
70
|
+
CreateType: typeof CreateType;
|
|
71
|
+
UpdateType: typeof UpdateType;
|
|
72
|
+
RemoveType: typeof RemoveType;
|
|
73
|
+
QueryItems: typeof QueryItems;
|
|
74
|
+
CreateItem: typeof CreateItem;
|
|
75
|
+
UpdateItem: typeof UpdateItem;
|
|
76
|
+
RemoveItem: typeof RemoveItem;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export default DictMgr;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// 字典管理请求命令:字典类型与字典项。
|
|
2
|
+
import { RequestBase } from '@yoyaflow/yoya-ui';
|
|
3
|
+
import Dicts from './dict.views.js';
|
|
4
|
+
|
|
5
|
+
class QueryTypes extends RequestBase {
|
|
6
|
+
address() {
|
|
7
|
+
return '/dicts/types';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
toItem(row) {
|
|
11
|
+
return new Dicts.DictTypeItem(row);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
class CreateType extends RequestBase {
|
|
16
|
+
constructor({ name = '', code = '', status = 'active', remark = '' } = {}) {
|
|
17
|
+
super();
|
|
18
|
+
this.name = name;
|
|
19
|
+
this.code = code;
|
|
20
|
+
this.status = status;
|
|
21
|
+
this.remark = remark;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
method() {
|
|
25
|
+
return 'POST';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
address() {
|
|
29
|
+
return '/dicts/types';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
body() {
|
|
33
|
+
return { name: this.name, code: this.code, status: this.status, remark: this.remark };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
class UpdateType extends RequestBase {
|
|
38
|
+
constructor(id, { name = '', code = '', status = 'active', remark = '' } = {}) {
|
|
39
|
+
super();
|
|
40
|
+
this.id = id;
|
|
41
|
+
this.name = name;
|
|
42
|
+
this.code = code;
|
|
43
|
+
this.status = status;
|
|
44
|
+
this.remark = remark;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
method() {
|
|
48
|
+
return 'PUT';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
address() {
|
|
52
|
+
return `/dicts/types/${this.id}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
body() {
|
|
56
|
+
return { name: this.name, code: this.code, status: this.status, remark: this.remark };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
class RemoveType extends RequestBase {
|
|
61
|
+
constructor(id) {
|
|
62
|
+
super();
|
|
63
|
+
this.id = id;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
method() {
|
|
67
|
+
return 'DELETE';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
address() {
|
|
71
|
+
return `/dicts/types/${this.id}`;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
class QueryItems extends RequestBase {
|
|
76
|
+
constructor(typeId) {
|
|
77
|
+
super();
|
|
78
|
+
this.typeId = typeId;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
address() {
|
|
82
|
+
return `/dicts/types/${this.typeId}/items`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
toItem(row) {
|
|
86
|
+
return new Dicts.DictItem(row);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
class CreateItem extends RequestBase {
|
|
91
|
+
constructor(typeId, { label = '', value = '', sort = 0, status = 'active' } = {}) {
|
|
92
|
+
super();
|
|
93
|
+
this.typeId = typeId;
|
|
94
|
+
this.label = label;
|
|
95
|
+
this.value = value;
|
|
96
|
+
this.sort = sort;
|
|
97
|
+
this.status = status;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
method() {
|
|
101
|
+
return 'POST';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
address() {
|
|
105
|
+
return `/dicts/types/${this.typeId}/items`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
body() {
|
|
109
|
+
return { label: this.label, value: this.value, sort: this.sort, status: this.status };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
class UpdateItem extends RequestBase {
|
|
114
|
+
constructor(id, { label = '', value = '', sort = 0, status = 'active' } = {}) {
|
|
115
|
+
super();
|
|
116
|
+
this.id = id;
|
|
117
|
+
this.label = label;
|
|
118
|
+
this.value = value;
|
|
119
|
+
this.sort = sort;
|
|
120
|
+
this.status = status;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
method() {
|
|
124
|
+
return 'PUT';
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
address() {
|
|
128
|
+
return `/dicts/items/${this.id}`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
body() {
|
|
132
|
+
return { label: this.label, value: this.value, sort: this.sort, status: this.status };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
class RemoveItem extends RequestBase {
|
|
137
|
+
constructor(id) {
|
|
138
|
+
super();
|
|
139
|
+
this.id = id;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
method() {
|
|
143
|
+
return 'DELETE';
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
address() {
|
|
147
|
+
return `/dicts/items/${this.id}`;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export default {
|
|
152
|
+
QueryTypes,
|
|
153
|
+
CreateType,
|
|
154
|
+
UpdateType,
|
|
155
|
+
RemoveType,
|
|
156
|
+
QueryItems,
|
|
157
|
+
CreateItem,
|
|
158
|
+
UpdateItem,
|
|
159
|
+
RemoveItem
|
|
160
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// 字典演示用内存 mock(接入真实后端后删除本文件)。
|
|
2
|
+
import { mockRequest } from '../../../api/domain.api.js';
|
|
3
|
+
|
|
4
|
+
let types = [
|
|
5
|
+
{ id: 1, name: '性别', code: 'gender', status: 'active', remark: '性别字典' },
|
|
6
|
+
{ id: 2, name: '通用状态', code: 'common_status', status: 'active', remark: '通用启停状态' },
|
|
7
|
+
{ id: 3, name: '通知类型', code: 'notice_type', status: 'active', remark: '消息通知类型' }
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
let items = [
|
|
11
|
+
{ id: 11, typeId: 1, label: '男', value: 'male', sort: 1, status: 'active' },
|
|
12
|
+
{ id: 12, typeId: 1, label: '女', value: 'female', sort: 2, status: 'active' },
|
|
13
|
+
{ id: 21, typeId: 2, label: '启用', value: 'active', sort: 1, status: 'active' },
|
|
14
|
+
{ id: 22, typeId: 2, label: '禁用', value: 'disabled', sort: 2, status: 'active' },
|
|
15
|
+
{ id: 31, typeId: 3, label: '系统通知', value: 'system', sort: 1, status: 'active' },
|
|
16
|
+
{ id: 32, typeId: 3, label: '告警', value: 'alert', sort: 2, status: 'active' },
|
|
17
|
+
{ id: 33, typeId: 3, label: '私信', value: 'message', sort: 3, status: 'disabled' }
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
const delay = (ms = 200) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
21
|
+
|
|
22
|
+
mockRequest('GET', '/dicts/types', async () => {
|
|
23
|
+
await delay();
|
|
24
|
+
return { ok: true, code: '0', data: types.slice() };
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
mockRequest('POST', '/dicts/types', async (req) => {
|
|
28
|
+
await delay();
|
|
29
|
+
const type = { id: Math.max(0, ...types.map((item) => item.id)) + 1, ...req.body() };
|
|
30
|
+
types = [...types, type];
|
|
31
|
+
return { ok: true, code: '0', data: type };
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
mockRequest('PUT', /^\/dicts\/types\/\d+$/, async (req) => {
|
|
35
|
+
await delay();
|
|
36
|
+
const id = Number(req.address().split('/').pop());
|
|
37
|
+
const type = { ...types.find((item) => item.id === id), ...req.body(), id };
|
|
38
|
+
types = types.map((item) => (item.id === id ? type : item));
|
|
39
|
+
return { ok: true, code: '0', data: type };
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
mockRequest('DELETE', /^\/dicts\/types\/\d+$/, async (req) => {
|
|
43
|
+
await delay();
|
|
44
|
+
const id = Number(req.address().split('/').pop());
|
|
45
|
+
types = types.filter((item) => item.id !== id);
|
|
46
|
+
items = items.filter((item) => item.typeId !== id);
|
|
47
|
+
return { ok: true, code: '0', data: null };
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
mockRequest('GET', /^\/dicts\/types\/\d+\/items$/, async (req) => {
|
|
51
|
+
await delay();
|
|
52
|
+
const typeId = Number(req.address().split('/')[3]);
|
|
53
|
+
return {
|
|
54
|
+
ok: true,
|
|
55
|
+
code: '0',
|
|
56
|
+
data: items.filter((item) => item.typeId === typeId).sort((a, b) => a.sort - b.sort)
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
mockRequest('POST', /^\/dicts\/types\/\d+\/items$/, async (req) => {
|
|
61
|
+
await delay();
|
|
62
|
+
const typeId = Number(req.address().split('/')[3]);
|
|
63
|
+
const item = { id: Math.max(0, ...items.map((entry) => entry.id)) + 1, typeId, ...req.body() };
|
|
64
|
+
items = [...items, item];
|
|
65
|
+
return { ok: true, code: '0', data: item };
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
mockRequest('PUT', /^\/dicts\/items\/\d+$/, async (req) => {
|
|
69
|
+
await delay();
|
|
70
|
+
const id = Number(req.address().split('/').pop());
|
|
71
|
+
const item = { ...items.find((entry) => entry.id === id), ...req.body(), id };
|
|
72
|
+
items = items.map((entry) => (entry.id === id ? item : entry));
|
|
73
|
+
return { ok: true, code: '0', data: item };
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
mockRequest('DELETE', /^\/dicts\/items\/\d+$/, async (req) => {
|
|
77
|
+
await delay();
|
|
78
|
+
const id = Number(req.address().split('/').pop());
|
|
79
|
+
items = items.filter((entry) => entry.id !== id);
|
|
80
|
+
return { ok: true, code: '0', data: null };
|
|
81
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** 字典页状态类类型声明(对应 dict.state.js)。 */
|
|
2
|
+
import Dicts from './dict.views.js';
|
|
3
|
+
|
|
4
|
+
type DictTypeInstance = InstanceType<typeof Dicts.DictTypeItem>;
|
|
5
|
+
type DictItemInstance = InstanceType<typeof Dicts.DictItem>;
|
|
6
|
+
|
|
7
|
+
export default class DictsPageState {
|
|
8
|
+
constructor();
|
|
9
|
+
types(): DictTypeInstance[];
|
|
10
|
+
selectedTypeId(): number | null;
|
|
11
|
+
selectedType(): DictTypeInstance | null;
|
|
12
|
+
items(): DictItemInstance[];
|
|
13
|
+
subscribe(listener: () => void): () => void;
|
|
14
|
+
loadTypes(): Promise<unknown>;
|
|
15
|
+
selectType(id: number): void;
|
|
16
|
+
loadItems(): Promise<void>;
|
|
17
|
+
addType(payload: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
18
|
+
editType(id: number, payload: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
19
|
+
removeType(id: number): Promise<void>;
|
|
20
|
+
addItem(payload: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
21
|
+
editItem(id: number, payload: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
22
|
+
removeItem(id: number): Promise<void>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// 字典页状态类:字典类型 + 当前类型下的字典项。
|
|
2
|
+
import DictMgr from './dict.mgr.js';
|
|
3
|
+
|
|
4
|
+
export default class DictsPageState {
|
|
5
|
+
constructor() {
|
|
6
|
+
this._types = [];
|
|
7
|
+
this._selectedTypeId = null;
|
|
8
|
+
this._items = [];
|
|
9
|
+
this._listeners = new Set();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
types() {
|
|
13
|
+
return this._types;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
selectedTypeId() {
|
|
17
|
+
return this._selectedTypeId;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
selectedType() {
|
|
21
|
+
return this._types.find((type) => type.id === this._selectedTypeId) ?? null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
items() {
|
|
25
|
+
return this._items;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
subscribe(listener) {
|
|
29
|
+
this._listeners.add(listener);
|
|
30
|
+
return () => this._listeners.delete(listener);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async loadTypes() {
|
|
34
|
+
const result = await new DictMgr.QueryTypes().submit();
|
|
35
|
+
this._types = result.data;
|
|
36
|
+
if (!this._selectedTypeId && this._types.length > 0) {
|
|
37
|
+
this._selectedTypeId = this._types[0].id;
|
|
38
|
+
}
|
|
39
|
+
await this.loadItems();
|
|
40
|
+
this._emit();
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async selectType(id) {
|
|
45
|
+
this._selectedTypeId = id;
|
|
46
|
+
await this.loadItems();
|
|
47
|
+
this._emit();
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async loadItems() {
|
|
52
|
+
if (this._selectedTypeId === null) {
|
|
53
|
+
this._items = [];
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const result = await new DictMgr.QueryItems(this._selectedTypeId).submit();
|
|
57
|
+
this._items = result.data;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async addType(payload) {
|
|
61
|
+
await new DictMgr.CreateType(payload).submit();
|
|
62
|
+
await this.loadTypes();
|
|
63
|
+
return payload;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async editType(id, payload) {
|
|
67
|
+
await new DictMgr.UpdateType(id, payload).submit();
|
|
68
|
+
await this.loadTypes();
|
|
69
|
+
return payload;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async removeType(id) {
|
|
73
|
+
await new DictMgr.RemoveType(id).submit();
|
|
74
|
+
if (this._selectedTypeId === id) {
|
|
75
|
+
this._selectedTypeId = null;
|
|
76
|
+
}
|
|
77
|
+
await this.loadTypes();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async addItem(payload) {
|
|
81
|
+
await new DictMgr.CreateItem(this._selectedTypeId, payload).submit();
|
|
82
|
+
await this.loadItems();
|
|
83
|
+
this._emit();
|
|
84
|
+
return payload;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async editItem(id, payload) {
|
|
88
|
+
await new DictMgr.UpdateItem(id, payload).submit();
|
|
89
|
+
await this.loadItems();
|
|
90
|
+
this._emit();
|
|
91
|
+
return payload;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async removeItem(id) {
|
|
95
|
+
await new DictMgr.RemoveItem(id).submit();
|
|
96
|
+
await this.loadItems();
|
|
97
|
+
this._emit();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
_emit() {
|
|
101
|
+
this._listeners.forEach((listener) => listener());
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** 字典结果结构类型声明(对应 dict.views.js)。 */
|
|
2
|
+
export type DictStatus = 'active' | 'disabled';
|
|
3
|
+
|
|
4
|
+
export class DictTypeItem {
|
|
5
|
+
id: number;
|
|
6
|
+
name: string;
|
|
7
|
+
code: string;
|
|
8
|
+
status: DictStatus;
|
|
9
|
+
remark: string;
|
|
10
|
+
constructor(init?: {
|
|
11
|
+
id: number;
|
|
12
|
+
name?: string;
|
|
13
|
+
code?: string;
|
|
14
|
+
status?: DictStatus;
|
|
15
|
+
remark?: string;
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class DictItem {
|
|
20
|
+
id: number;
|
|
21
|
+
typeId: number | null;
|
|
22
|
+
label: string;
|
|
23
|
+
value: string;
|
|
24
|
+
sort: number;
|
|
25
|
+
status: DictStatus;
|
|
26
|
+
constructor(init?: {
|
|
27
|
+
id: number;
|
|
28
|
+
typeId?: number | null;
|
|
29
|
+
label?: string;
|
|
30
|
+
value?: string;
|
|
31
|
+
sort?: number;
|
|
32
|
+
status?: DictStatus;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
declare const Dicts: {
|
|
37
|
+
DictTypeItem: typeof DictTypeItem;
|
|
38
|
+
DictItem: typeof DictItem;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export default Dicts;
|