@mu-cabin/opms-permission 0.1.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 +86 -0
- package/dist/index.cjs +529 -0
- package/dist/index.d.mts +171 -0
- package/dist/index.d.ts +171 -0
- package/dist/index.mjs +490 -0
- package/package.json +29 -0
package/dist/index.mjs
ADDED
@@ -0,0 +1,490 @@
|
|
1
|
+
// src/utils/storage.ts
|
2
|
+
var Storage = class {
|
3
|
+
setSystemId(systemId) {
|
4
|
+
this.systemId = systemId;
|
5
|
+
}
|
6
|
+
prefixKey(key) {
|
7
|
+
return this.systemId !== void 0 ? `systemId-${this.systemId}-${key}` : key;
|
8
|
+
}
|
9
|
+
/**
|
10
|
+
* Set an item in localStorage, with optional expiration in minutes
|
11
|
+
*/
|
12
|
+
setItem(key, value, expireMinutes) {
|
13
|
+
const data = {
|
14
|
+
value,
|
15
|
+
expire: expireMinutes ? Date.now() + expireMinutes * 60 * 1e3 : null
|
16
|
+
};
|
17
|
+
localStorage.setItem(this.prefixKey(key), JSON.stringify(data));
|
18
|
+
}
|
19
|
+
/**
|
20
|
+
* Get an item from localStorage, returns undefined if expired or not found
|
21
|
+
*/
|
22
|
+
getItem(key) {
|
23
|
+
const raw = localStorage.getItem(this.prefixKey(key));
|
24
|
+
if (!raw) return void 0;
|
25
|
+
try {
|
26
|
+
const data = JSON.parse(raw);
|
27
|
+
if (data.expire && Date.now() > data.expire) {
|
28
|
+
localStorage.removeItem(this.prefixKey(key));
|
29
|
+
return void 0;
|
30
|
+
}
|
31
|
+
return data.value;
|
32
|
+
} catch {
|
33
|
+
return void 0;
|
34
|
+
}
|
35
|
+
}
|
36
|
+
/**
|
37
|
+
* Remove an item from localStorage
|
38
|
+
*/
|
39
|
+
removeItem(key) {
|
40
|
+
localStorage.removeItem(this.prefixKey(key));
|
41
|
+
}
|
42
|
+
};
|
43
|
+
var storage = new Storage();
|
44
|
+
|
45
|
+
// src/utils/dataHandler.ts
|
46
|
+
function getCompletePath(item) {
|
47
|
+
const menuUrl = item.openIndicator === "INNER_COMPONENT" ? item.componentPath : item.defaultUrl;
|
48
|
+
return menuUrl ?? "";
|
49
|
+
}
|
50
|
+
function getMenuName(item) {
|
51
|
+
return item.resourceType !== "FOLDER" ? item.resourceName || "" : (item.showName ?? item.resourceName) || "";
|
52
|
+
}
|
53
|
+
function createResourceMap(items) {
|
54
|
+
const resourceMap = {};
|
55
|
+
const widgetMap = {};
|
56
|
+
function addToMap(items2) {
|
57
|
+
items2.forEach((item) => {
|
58
|
+
const newItem = { ...item };
|
59
|
+
if (newItem.defaultResourceType === "WIDGET") {
|
60
|
+
if (newItem.componentPath) widgetMap[newItem.componentPath] = newItem;
|
61
|
+
} else {
|
62
|
+
const path = getCompletePath(newItem);
|
63
|
+
if (path) {
|
64
|
+
resourceMap[path] = newItem;
|
65
|
+
}
|
66
|
+
}
|
67
|
+
if (newItem.children) {
|
68
|
+
addToMap(newItem.children);
|
69
|
+
}
|
70
|
+
});
|
71
|
+
}
|
72
|
+
addToMap(items);
|
73
|
+
return { resourceMap, widgetMap };
|
74
|
+
}
|
75
|
+
function isMenuItem(item) {
|
76
|
+
const resourceType = item.resourceType ?? item.defaultResourceType;
|
77
|
+
return resourceType !== "WIDGET";
|
78
|
+
}
|
79
|
+
function createMenuList(items) {
|
80
|
+
const menus = [];
|
81
|
+
const menuMap = {};
|
82
|
+
const filterMenus = (items2, container) => {
|
83
|
+
items2.forEach((item) => {
|
84
|
+
const icon = item.icon || item.defaultIcon;
|
85
|
+
const path = getCompletePath(item);
|
86
|
+
const name = getMenuName(item);
|
87
|
+
const newItem = {
|
88
|
+
icon,
|
89
|
+
path,
|
90
|
+
name,
|
91
|
+
children: [],
|
92
|
+
openIndicator: item.openIndicator,
|
93
|
+
resourceId: item.resourceId
|
94
|
+
};
|
95
|
+
menuMap[newItem.resourceId] = newItem;
|
96
|
+
if (isMenuItem(item)) {
|
97
|
+
container.push(newItem);
|
98
|
+
}
|
99
|
+
if (item.children) {
|
100
|
+
filterMenus(item.children, newItem.children);
|
101
|
+
}
|
102
|
+
});
|
103
|
+
};
|
104
|
+
filterMenus(items, menus);
|
105
|
+
return { menuList: menus, menuMap };
|
106
|
+
}
|
107
|
+
function iterateNestedArray(items, callback) {
|
108
|
+
function trevel(items2, parent) {
|
109
|
+
return items2.map((item) => {
|
110
|
+
const handledItem = callback(item, parent);
|
111
|
+
if (handledItem && handledItem.children) {
|
112
|
+
handledItem.children = trevel(handledItem.children, handledItem);
|
113
|
+
}
|
114
|
+
return handledItem;
|
115
|
+
}).filter((v) => v);
|
116
|
+
}
|
117
|
+
return trevel(items);
|
118
|
+
}
|
119
|
+
function findFirstEnableCode(data) {
|
120
|
+
if (!Array.isArray(data) || data.length === 0) {
|
121
|
+
return null;
|
122
|
+
}
|
123
|
+
let queue = [...data];
|
124
|
+
while (queue.length > 0) {
|
125
|
+
const node = queue.shift();
|
126
|
+
if (!node?.disabled) {
|
127
|
+
return node?.value || null;
|
128
|
+
}
|
129
|
+
if (Array.isArray(node.children) && node.children.length > 0) {
|
130
|
+
queue = queue.concat(node.children);
|
131
|
+
}
|
132
|
+
}
|
133
|
+
return null;
|
134
|
+
}
|
135
|
+
function findAllEnableTopLevelCodes(nodes) {
|
136
|
+
const codes = [];
|
137
|
+
function findEnabledNodes(nodes2) {
|
138
|
+
for (const node of nodes2) {
|
139
|
+
if (!node.disabled) {
|
140
|
+
codes.push(node.value);
|
141
|
+
} else if (node.children) {
|
142
|
+
findEnabledNodes(node.children);
|
143
|
+
}
|
144
|
+
}
|
145
|
+
}
|
146
|
+
findEnabledNodes(nodes);
|
147
|
+
return codes;
|
148
|
+
}
|
149
|
+
|
150
|
+
// src/api.ts
|
151
|
+
import axios from "axios";
|
152
|
+
|
153
|
+
// src/config.ts
|
154
|
+
var USER_INFO_KEY = "opms_user_info";
|
155
|
+
var RESOURCE_KEY = "opms_resources";
|
156
|
+
var TOKEN_KEY = "omps_authorization";
|
157
|
+
var ORG_COMPANY_KEY = "opms_org_company";
|
158
|
+
|
159
|
+
// src/api.ts
|
160
|
+
var axiosClient = axios.create();
|
161
|
+
axiosClient.interceptors.request.use((config) => {
|
162
|
+
const token = storage.getItem(TOKEN_KEY);
|
163
|
+
if (token) {
|
164
|
+
config.headers.Authorization = token;
|
165
|
+
}
|
166
|
+
return config;
|
167
|
+
});
|
168
|
+
axiosClient.interceptors.response.use(
|
169
|
+
(response) => {
|
170
|
+
const res = response.data;
|
171
|
+
const { code, msg } = res;
|
172
|
+
if (code !== 200) {
|
173
|
+
throw new Error(msg);
|
174
|
+
}
|
175
|
+
return res;
|
176
|
+
},
|
177
|
+
(error) => {
|
178
|
+
return Promise.reject(error);
|
179
|
+
}
|
180
|
+
);
|
181
|
+
async function login(baseUrl, authorizationCode) {
|
182
|
+
return await axiosClient.get(
|
183
|
+
`${baseUrl}/opmsDefaultAuth/oauthLogin`,
|
184
|
+
{
|
185
|
+
params: { authorizationCode }
|
186
|
+
}
|
187
|
+
);
|
188
|
+
}
|
189
|
+
async function logout(baseUrl) {
|
190
|
+
return await axiosClient.post(
|
191
|
+
`${baseUrl}/opmsDefaultAuth/logout`
|
192
|
+
);
|
193
|
+
}
|
194
|
+
async function getUserInfo(baseUrl) {
|
195
|
+
return await axiosClient.post(
|
196
|
+
`${baseUrl}/opmsDefaultUser/userInfo`,
|
197
|
+
{}
|
198
|
+
);
|
199
|
+
}
|
200
|
+
async function queryResource(baseUrl, params) {
|
201
|
+
return await axiosClient.post(
|
202
|
+
`${baseUrl}/opmsDefaultUser/userResource`,
|
203
|
+
params
|
204
|
+
);
|
205
|
+
}
|
206
|
+
async function getUserOrgTree(baseUrl, params) {
|
207
|
+
return await axiosClient.post(
|
208
|
+
`${baseUrl}/opmsDefaultUser/orgTree`,
|
209
|
+
params
|
210
|
+
);
|
211
|
+
}
|
212
|
+
async function queryOrgCompanies(baseUrl, params) {
|
213
|
+
return await axiosClient.post(
|
214
|
+
`${baseUrl}/opmsDefaultUser/branches`,
|
215
|
+
params
|
216
|
+
);
|
217
|
+
}
|
218
|
+
|
219
|
+
// src/permission.ts
|
220
|
+
var Permission = class {
|
221
|
+
constructor(options) {
|
222
|
+
this._userInfo = null;
|
223
|
+
this._orgTree = null;
|
224
|
+
this._orgCompany = null;
|
225
|
+
this.resources = [];
|
226
|
+
this.resourceMap = {};
|
227
|
+
this.widgetMap = {};
|
228
|
+
this.menuList = [];
|
229
|
+
this.menuMap = {};
|
230
|
+
this.baseUrl = options.baseUrl;
|
231
|
+
this.systemId = options.systemId;
|
232
|
+
storage.setSystemId(this.systemId);
|
233
|
+
}
|
234
|
+
/**
|
235
|
+
* Login using code from URL, save userInfo
|
236
|
+
*/
|
237
|
+
async login() {
|
238
|
+
const url = new URL(window.location.href);
|
239
|
+
const code = url.searchParams.get("code");
|
240
|
+
if (!code) throw new Error("No code found in URL");
|
241
|
+
const { obj, success, msg } = await login(this.baseUrl, code);
|
242
|
+
if (!success) {
|
243
|
+
throw new Error(msg);
|
244
|
+
}
|
245
|
+
const { token } = obj;
|
246
|
+
url.searchParams.delete("code");
|
247
|
+
storage.setItem(TOKEN_KEY, token);
|
248
|
+
return token;
|
249
|
+
}
|
250
|
+
/**
|
251
|
+
* Logout and clear userInfo
|
252
|
+
*/
|
253
|
+
async logout() {
|
254
|
+
await logout(this.baseUrl);
|
255
|
+
this.clear();
|
256
|
+
}
|
257
|
+
clear() {
|
258
|
+
this._userInfo = null;
|
259
|
+
this._orgTree = null;
|
260
|
+
this._orgCompany = null;
|
261
|
+
this.resources = [];
|
262
|
+
this.resourceMap = {};
|
263
|
+
this.widgetMap = {};
|
264
|
+
this.menuList = [];
|
265
|
+
this.menuMap = {};
|
266
|
+
storage.removeItem(RESOURCE_KEY);
|
267
|
+
storage.removeItem(TOKEN_KEY);
|
268
|
+
storage.removeItem(USER_INFO_KEY);
|
269
|
+
storage.removeItem(ORG_COMPANY_KEY);
|
270
|
+
}
|
271
|
+
async getUserInfo() {
|
272
|
+
const data = await getUserInfo(this.baseUrl);
|
273
|
+
const { obj, success, msg } = data;
|
274
|
+
if (!success) {
|
275
|
+
return Promise.reject(new Error(msg));
|
276
|
+
}
|
277
|
+
this._userInfo = obj;
|
278
|
+
return obj;
|
279
|
+
}
|
280
|
+
/**
|
281
|
+
* Get resources and process to menuList, menuMap, widgetMap (matches app store logic)
|
282
|
+
*/
|
283
|
+
async getResources() {
|
284
|
+
const resourcesObj = storage.getItem(RESOURCE_KEY) || null;
|
285
|
+
if (resourcesObj) {
|
286
|
+
const { menuList: menuList2, menuMap: menuMap2 } = createMenuList(resourcesObj.resources);
|
287
|
+
return {
|
288
|
+
resources: resourcesObj.resources,
|
289
|
+
resourceMap: resourcesObj.resourceMap,
|
290
|
+
widgetMap: resourcesObj.widgetMap,
|
291
|
+
menuList: menuList2,
|
292
|
+
menuMap: menuMap2
|
293
|
+
};
|
294
|
+
}
|
295
|
+
const { obj, success, msg } = await queryResource(this.baseUrl, {
|
296
|
+
systemId: this.systemId
|
297
|
+
});
|
298
|
+
if (!success) {
|
299
|
+
return Promise.reject(new Error(msg));
|
300
|
+
}
|
301
|
+
const resources = obj;
|
302
|
+
const { resourceMap, widgetMap } = createResourceMap(resources);
|
303
|
+
const { menuList, menuMap } = createMenuList(resources);
|
304
|
+
this.resources = resources;
|
305
|
+
this.resourceMap = resourceMap;
|
306
|
+
this.widgetMap = widgetMap;
|
307
|
+
this.menuList = menuList;
|
308
|
+
this.menuMap = menuMap;
|
309
|
+
storage.setItem(
|
310
|
+
RESOURCE_KEY,
|
311
|
+
{
|
312
|
+
resources,
|
313
|
+
resourceMap,
|
314
|
+
widgetMap
|
315
|
+
},
|
316
|
+
60 * 24
|
317
|
+
// 24 hours
|
318
|
+
);
|
319
|
+
return {
|
320
|
+
resources,
|
321
|
+
resourceMap,
|
322
|
+
widgetMap,
|
323
|
+
menuList,
|
324
|
+
menuMap
|
325
|
+
};
|
326
|
+
}
|
327
|
+
/**
|
328
|
+
* Query and process organization tree
|
329
|
+
*/
|
330
|
+
async queryOrgs() {
|
331
|
+
try {
|
332
|
+
const data = await getUserOrgTree(this.baseUrl, {
|
333
|
+
systemId: this.systemId,
|
334
|
+
name: "COS"
|
335
|
+
});
|
336
|
+
const { obj, success, msg } = data;
|
337
|
+
if (!success) {
|
338
|
+
return Promise.reject(new Error(msg));
|
339
|
+
}
|
340
|
+
const orgTree = obj;
|
341
|
+
const newTree = iterateNestedArray([orgTree], (item) => {
|
342
|
+
return {
|
343
|
+
label: item.orgShortName,
|
344
|
+
value: item.orgCode,
|
345
|
+
title: item.orgShortName,
|
346
|
+
key: item.orgCode,
|
347
|
+
nodeLevel: item.nodeLevel,
|
348
|
+
children: item.children,
|
349
|
+
disabled: !item.hasPermission,
|
350
|
+
orgType: item.orgType
|
351
|
+
};
|
352
|
+
})?.[0];
|
353
|
+
this._orgTree = newTree;
|
354
|
+
return newTree;
|
355
|
+
} catch (error) {
|
356
|
+
console.log(error);
|
357
|
+
return null;
|
358
|
+
}
|
359
|
+
}
|
360
|
+
async queryCompanies() {
|
361
|
+
try {
|
362
|
+
let orgCompanyList = storage.getItem(ORG_COMPANY_KEY);
|
363
|
+
if (!orgCompanyList) {
|
364
|
+
const { obj } = await queryOrgCompanies(this.baseUrl, { queryAllBranches: true });
|
365
|
+
orgCompanyList = obj;
|
366
|
+
storage.setItem(ORG_COMPANY_KEY, orgCompanyList);
|
367
|
+
}
|
368
|
+
this._orgCompany = orgCompanyList;
|
369
|
+
} catch (error) {
|
370
|
+
console.log(error);
|
371
|
+
}
|
372
|
+
}
|
373
|
+
isLogin() {
|
374
|
+
return !!storage.getItem(TOKEN_KEY);
|
375
|
+
}
|
376
|
+
getToken() {
|
377
|
+
return storage.getItem(TOKEN_KEY);
|
378
|
+
}
|
379
|
+
// --- Getters ---
|
380
|
+
get userInfo() {
|
381
|
+
if (!this._userInfo) {
|
382
|
+
this._userInfo = storage.getItem(USER_INFO_KEY);
|
383
|
+
}
|
384
|
+
if (!this._userInfo) return null;
|
385
|
+
const { account, name, ehrId, crewCode, crewId } = this._userInfo;
|
386
|
+
return { account, name, ehrId, crewCode, crewId };
|
387
|
+
}
|
388
|
+
get hasRootAuth() {
|
389
|
+
const orgs = this._userInfo?.organizations || [];
|
390
|
+
const first = orgs[0];
|
391
|
+
if (!first) return false;
|
392
|
+
return first.searchPath === "#1";
|
393
|
+
}
|
394
|
+
get userOrganizations() {
|
395
|
+
return this._userInfo?.userOrganizations || [];
|
396
|
+
}
|
397
|
+
get allCompanyOptions() {
|
398
|
+
const orgCompany = this._orgCompany;
|
399
|
+
if (!orgCompany) return [];
|
400
|
+
const list = orgCompany.map((v) => ({
|
401
|
+
label: v.orgName,
|
402
|
+
value: v.orgCode
|
403
|
+
}));
|
404
|
+
list.unshift({ label: "\u5168\u516C\u53F8", value: "EA" });
|
405
|
+
return list;
|
406
|
+
}
|
407
|
+
get companyOptions() {
|
408
|
+
const orgTree = this._orgTree;
|
409
|
+
if (!orgTree) return [];
|
410
|
+
const list = (orgTree.children || []).map(
|
411
|
+
({ children, ...others }) => ({ ...others, disabled: false })
|
412
|
+
);
|
413
|
+
if (this.hasRootAuth) {
|
414
|
+
list.unshift({ ...orgTree, label: "\u5168\u516C\u53F8", children: [] });
|
415
|
+
}
|
416
|
+
return list;
|
417
|
+
}
|
418
|
+
get firstCompanyOrgCode() {
|
419
|
+
return this.companyOptions?.[0]?.value ?? "";
|
420
|
+
}
|
421
|
+
get unitOptions() {
|
422
|
+
const orgTree = this._orgTree;
|
423
|
+
if (!orgTree) return [];
|
424
|
+
return orgTree.disabled ? orgTree.children ?? [] : [orgTree];
|
425
|
+
}
|
426
|
+
get allUnitOptions() {
|
427
|
+
const orgTree = this._orgTree;
|
428
|
+
if (!orgTree) return [];
|
429
|
+
const newTree = iterateNestedArray(
|
430
|
+
[orgTree],
|
431
|
+
({ disabled, ...other }) => ({ ...other })
|
432
|
+
)?.[0];
|
433
|
+
return newTree ? newTree.children ?? [] : [];
|
434
|
+
}
|
435
|
+
get firstUnitOrgCode() {
|
436
|
+
if (this.hasRootAuth) {
|
437
|
+
return this.companyOptions[1]?.value ?? "";
|
438
|
+
}
|
439
|
+
const orgTree = this._orgTree;
|
440
|
+
if (!orgTree) return "";
|
441
|
+
return findFirstEnableCode([orgTree]) ?? "";
|
442
|
+
}
|
443
|
+
get topLevelUnitOrgCodes() {
|
444
|
+
const orgTree = this._orgTree;
|
445
|
+
if (!orgTree) return [];
|
446
|
+
return findAllEnableTopLevelCodes(
|
447
|
+
Array.isArray(orgTree) ? orgTree : [orgTree]
|
448
|
+
);
|
449
|
+
}
|
450
|
+
};
|
451
|
+
|
452
|
+
// src/sso.ts
|
453
|
+
function jumpToSSOLogin({
|
454
|
+
baseUrl,
|
455
|
+
redirectUrl,
|
456
|
+
clientId = "testSA19"
|
457
|
+
}) {
|
458
|
+
const { location } = window;
|
459
|
+
const ssoUrl = new URL(`${baseUrl}/idp/oauth2/authorize`);
|
460
|
+
const defaultUrl = new URL(
|
461
|
+
location.origin + location.pathname + location.search
|
462
|
+
);
|
463
|
+
ssoUrl.searchParams.set("client_id", clientId);
|
464
|
+
ssoUrl.searchParams.set("response_type", "code");
|
465
|
+
ssoUrl.searchParams.set("redirect_uri", redirectUrl || defaultUrl.toString());
|
466
|
+
window.location.href = ssoUrl.toString();
|
467
|
+
}
|
468
|
+
function jumpToSSOLogout({
|
469
|
+
baseUrl,
|
470
|
+
redirectToUrl,
|
471
|
+
clientId = "testSA19"
|
472
|
+
}) {
|
473
|
+
if (!baseUrl) {
|
474
|
+
throw new Error("baseUrl is required");
|
475
|
+
}
|
476
|
+
const { location } = window;
|
477
|
+
const ssoLoginUrl = `${baseUrl}/idp/oauth2/authorize?client_id=${clientId}&response_type=code&redirect_uri=${location.origin}/`;
|
478
|
+
const redirectUrl = encodeURIComponent(redirectToUrl || ssoLoginUrl);
|
479
|
+
const logoutUrl = new URL(`${baseUrl}/idp/profile/OAUTH2/Redirect/GLO`);
|
480
|
+
logoutUrl.searchParams.set("response_type", "code");
|
481
|
+
logoutUrl.searchParams.set("entityId", clientId);
|
482
|
+
logoutUrl.searchParams.set("redirectToLogin", "true");
|
483
|
+
logoutUrl.searchParams.set("redirctToUrl", redirectUrl);
|
484
|
+
window.location.href = logoutUrl.toString();
|
485
|
+
}
|
486
|
+
export {
|
487
|
+
Permission as OpmsPermission,
|
488
|
+
jumpToSSOLogin,
|
489
|
+
jumpToSSOLogout
|
490
|
+
};
|
package/package.json
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
{
|
2
|
+
"name": "@mu-cabin/opms-permission",
|
3
|
+
"version": "0.1.0",
|
4
|
+
"description": "Frontend SDK for OPMS permission and auth management.",
|
5
|
+
"main": "dist/index.cjs.js",
|
6
|
+
"module": "dist/index.mjs.js",
|
7
|
+
"types": "dist/index.d.ts",
|
8
|
+
"files": ["dist"],
|
9
|
+
"scripts": {
|
10
|
+
"build": "tsup src/index.ts --dts --format cjs,esm",
|
11
|
+
"dev": "tsup src/index.ts --watch --dts --format cjs,esm",
|
12
|
+
"clean": "rimraf dist",
|
13
|
+
"prepublishOnly": "npm run build",
|
14
|
+
"publish:package": "npm run clean && npm run build && npm publish"
|
15
|
+
},
|
16
|
+
"publishConfig": {
|
17
|
+
"access": "public"
|
18
|
+
},
|
19
|
+
"keywords": ["opms", "permission", "auth", "sdk"],
|
20
|
+
"author": "",
|
21
|
+
"license": "MIT",
|
22
|
+
"devDependencies": {
|
23
|
+
"axios": "^1.6.0",
|
24
|
+
"tsup": "^8.0.0",
|
25
|
+
"typescript": "^5.0.0",
|
26
|
+
"rimraf": "^5.0.5"
|
27
|
+
},
|
28
|
+
"dependencies": {}
|
29
|
+
}
|