@lark-apaas/client-toolkit 1.2.0-alpha.9 → 1.2.1-0.alpha.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 +68 -0
- package/lib/apis/components/ActiveLink.d.ts +26 -0
- package/lib/apis/components/ActiveLink.js +66 -0
- package/lib/apis/components/UniversalLink.d.ts +12 -0
- package/lib/apis/components/UniversalLink.js +26 -0
- package/lib/apis/tools/services.d.ts +1 -0
- package/lib/apis/tools/services.js +1 -0
- package/lib/apis/trace.d.ts +1 -0
- package/lib/apis/trace.js +1 -0
- package/lib/apis/utils/getEnv.d.ts +1 -0
- package/lib/apis/utils/getEnv.js +2 -0
- package/lib/auth.d.ts +1 -0
- package/lib/auth.js +2 -0
- package/lib/components/AppContainer/IframeBridge.d.ts +0 -1
- package/lib/components/AppContainer/IframeBridge.js +4 -22
- package/lib/components/AppContainer/index.d.ts +5 -2
- package/lib/components/AppContainer/index.js +9 -46
- package/lib/components/AppContainer/utils/childApi.js +1 -0
- package/lib/components/AppContainer/utils/observable.js +5 -2
- package/lib/components/ErrorRender/index.js +5 -11
- package/lib/components/PagePlaceholder/index.js +1 -1
- package/lib/components/User/UserSelect.js +1 -13
- package/lib/index.js +3 -1
- package/lib/integrations/services/DepartmentService.d.ts +10 -0
- package/lib/integrations/services/DepartmentService.js +29 -0
- package/lib/integrations/services/UserProfileService.d.ts +14 -0
- package/lib/integrations/services/UserProfileService.js +36 -0
- package/lib/integrations/services/UserService.d.ts +12 -0
- package/lib/integrations/services/UserService.js +46 -0
- package/lib/integrations/services/index.d.ts +4 -0
- package/lib/integrations/services/index.js +4 -0
- package/lib/integrations/services/types.d.ts +91 -0
- package/lib/integrations/services/types.js +0 -0
- package/lib/logger/batch-logger.js +3 -2
- package/lib/logger/intercept-global-error.js +16 -14
- package/lib/logger/log-types.d.ts +4 -4
- package/lib/logger/log-types.js +1 -1
- package/lib/logger/selected-logs.js +1 -2
- package/lib/runtime/axios.d.ts +5 -0
- package/lib/runtime/axios.js +2 -0
- package/lib/runtime/dayjs.d.ts +5 -0
- package/lib/runtime/dayjs.js +2 -0
- package/lib/runtime/iframe-bridge.d.ts +11 -0
- package/lib/runtime/iframe-bridge.js +29 -0
- package/lib/runtime/index.d.ts +23 -0
- package/lib/runtime/index.js +16 -0
- package/lib/runtime/observable.d.ts +5 -0
- package/lib/runtime/observable.js +2 -0
- package/lib/runtime/server-log.d.ts +5 -0
- package/lib/runtime/server-log.js +41 -0
- package/lib/runtime/styles.d.ts +5 -0
- package/lib/runtime/styles.js +1 -0
- package/lib/theme-layer.css +2 -1
- package/lib/trace/index.d.ts +13 -0
- package/lib/trace/index.js +6 -0
- package/lib/utils/axiosConfig.d.ts +10 -3
- package/lib/utils/axiosConfig.js +123 -7
- package/lib/utils/getAxiosForBackend.js +1 -1
- package/lib/utils/getParentOrigin.d.ts +0 -5
- package/lib/utils/getParentOrigin.js +13 -12
- package/lib/utils/hmr-api.d.ts +39 -0
- package/lib/utils/hmr-api.js +36 -0
- package/lib/utils/module-hot.d.ts +9 -5
- package/lib/utils/module-hot.js +9 -10
- package/lib/utils/postMessage.d.ts +2 -1
- package/lib/utils/postMessage.js +19 -5
- package/lib/utils/requestManager.js +1 -3
- package/package.json +22 -11
- package/lib/components/AppContainer/utils/listenHot.d.ts +0 -1
- package/lib/components/AppContainer/utils/listenHot.js +0 -57
- package/lib/logger/__tests__/batch-logger.test.d.ts +0 -1
- package/lib/logger/__tests__/batch-logger.test.js +0 -367
package/README.md
CHANGED
|
@@ -6,3 +6,71 @@
|
|
|
6
6
|
|
|
7
7
|
* 导出所有对外暴露的 API 和组件,并通过 npm exports 来控制
|
|
8
8
|
* 其他目录作为 internal 实现,不对外暴露
|
|
9
|
+
|
|
10
|
+
### auth 目录
|
|
11
|
+
|
|
12
|
+
* 包含与权限相关的 API 和组件
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### 开发组件 - 使用 CanRole 组件 (推荐)
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { CanRole } from '@lark-apaas/client-toolkit/auth';
|
|
19
|
+
|
|
20
|
+
function Home() {
|
|
21
|
+
return (
|
|
22
|
+
<div>
|
|
23
|
+
<CanRole roles={['role_admin']}>
|
|
24
|
+
<div>管理员按钮</div>
|
|
25
|
+
</CanRole>
|
|
26
|
+
<CanRole roles={['role_admin', 'role_editor']}>
|
|
27
|
+
<div>编辑按钮</div>
|
|
28
|
+
</CanRole>
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 开发组件 - 使用 AbilityContext 处理复杂场景
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import { useContext } from 'react';
|
|
38
|
+
import { AbilityContext, ROLE_SUBJECT } from '@lark-apaas/client-toolkit/auth';
|
|
39
|
+
|
|
40
|
+
function Home() {
|
|
41
|
+
const ability = useContext(AbilityContext);
|
|
42
|
+
return (
|
|
43
|
+
<div>
|
|
44
|
+
{ability.can('role_admin', ROLE_SUBJECT) || ability.can('role_editor', ROLE_SUBJECT) ? (
|
|
45
|
+
<div>可见的仪表盘</div>
|
|
46
|
+
) : null}
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 开发组件 - 进阶示例
|
|
53
|
+
|
|
54
|
+
### 菜单按权限过滤
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
import { useContext } from 'react';
|
|
58
|
+
import { AbilityContext, ROLE_SUBJECT } from '@lark-apaas/client-toolkit/auth';
|
|
59
|
+
|
|
60
|
+
const menus = [
|
|
61
|
+
{ name: 'Dashboard', path: '/dashboard', p: { action: 'role_admin', subject: ROLE_SUBJECT } },
|
|
62
|
+
{ name: 'Users', path: '/users', p: { action: 'role_editor', subject: ROLE_SUBJECT } },
|
|
63
|
+
{ name: 'Settings', path: '/settings', p: { action: 'role_admin', subject: ROLE_SUBJECT } },
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
function Nav() {
|
|
67
|
+
const ability = useContext(AbilityContext);
|
|
68
|
+
return (
|
|
69
|
+
<nav>
|
|
70
|
+
{menus.map(m => ability.can(m.p.action, m.p.subject) && (
|
|
71
|
+
<a key={m.path} href={m.path}>{m.name}</a>
|
|
72
|
+
))}
|
|
73
|
+
</nav>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type NavLinkProps } from 'react-router-dom';
|
|
3
|
+
export interface ActiveLinkProps extends Omit<NavLinkProps, 'to'> {
|
|
4
|
+
to: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* ActiveLink 组件
|
|
8
|
+
*
|
|
9
|
+
* 替换 react-router-dom 的 NavLink,扩展支持 hash 路由跳转和激活态功能
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* // 普通路由跳转
|
|
13
|
+
* <ActiveLink to="/dashboard">Dashboard</ActiveLink>
|
|
14
|
+
*
|
|
15
|
+
* // hash 路由跳转
|
|
16
|
+
* <ActiveLink to="#section1">Section 1</ActiveLink>
|
|
17
|
+
*
|
|
18
|
+
* // 使用函数式 className 显示激活态
|
|
19
|
+
* <ActiveLink
|
|
20
|
+
* to="#features"
|
|
21
|
+
* className={({isActive}) => isActive ? 'active' : ''}
|
|
22
|
+
* >
|
|
23
|
+
* Features
|
|
24
|
+
* </ActiveLink>
|
|
25
|
+
*/
|
|
26
|
+
export declare const ActiveLink: React.ForwardRefExoticComponent<ActiveLinkProps & React.RefAttributes<HTMLAnchorElement>>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useEffect, useState } from "react";
|
|
3
|
+
import { NavLink } from "react-router-dom";
|
|
4
|
+
const ActiveLink = /*#__PURE__*/ forwardRef(({ to, onClick, className, style, children, ...rest }, ref)=>{
|
|
5
|
+
const [currentHash, setCurrentHash] = useState(()=>'undefined' != typeof window ? window.location.hash : '');
|
|
6
|
+
const isHashRoute = 'string' == typeof to && to.startsWith('#');
|
|
7
|
+
useEffect(()=>{
|
|
8
|
+
if (!isHashRoute) return;
|
|
9
|
+
const handleHashChange = ()=>{
|
|
10
|
+
setCurrentHash(window.location.hash);
|
|
11
|
+
};
|
|
12
|
+
window.addEventListener('hashchange', handleHashChange);
|
|
13
|
+
return ()=>window.removeEventListener('hashchange', handleHashChange);
|
|
14
|
+
}, [
|
|
15
|
+
isHashRoute
|
|
16
|
+
]);
|
|
17
|
+
const isActive = isHashRoute && currentHash === to;
|
|
18
|
+
if (!isHashRoute) return /*#__PURE__*/ jsx(NavLink, {
|
|
19
|
+
ref: ref,
|
|
20
|
+
to: to,
|
|
21
|
+
onClick: onClick,
|
|
22
|
+
className: className,
|
|
23
|
+
style: style,
|
|
24
|
+
...rest,
|
|
25
|
+
children: children
|
|
26
|
+
});
|
|
27
|
+
const handleHashClick = (e)=>{
|
|
28
|
+
onClick?.(e);
|
|
29
|
+
if (e.defaultPrevented) return;
|
|
30
|
+
e.preventDefault();
|
|
31
|
+
const targetId = to.slice(1);
|
|
32
|
+
const targetElement = document.getElementById(targetId);
|
|
33
|
+
history.pushState(null, '', to);
|
|
34
|
+
setCurrentHash(to);
|
|
35
|
+
window.dispatchEvent(new Event('hashchange'));
|
|
36
|
+
if (targetElement && 'function' == typeof targetElement.scrollIntoView) targetElement.scrollIntoView({
|
|
37
|
+
behavior: 'smooth'
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
const computedClassName = 'function' == typeof className ? className({
|
|
41
|
+
isActive,
|
|
42
|
+
isPending: false,
|
|
43
|
+
isTransitioning: false
|
|
44
|
+
}) : className;
|
|
45
|
+
const computedStyle = 'function' == typeof style ? style({
|
|
46
|
+
isActive,
|
|
47
|
+
isPending: false,
|
|
48
|
+
isTransitioning: false
|
|
49
|
+
}) : style;
|
|
50
|
+
const computedChildren = 'function' == typeof children ? children({
|
|
51
|
+
isActive,
|
|
52
|
+
isPending: false,
|
|
53
|
+
isTransitioning: false
|
|
54
|
+
}) : children;
|
|
55
|
+
return /*#__PURE__*/ jsx("a", {
|
|
56
|
+
ref: ref,
|
|
57
|
+
href: to,
|
|
58
|
+
onClick: handleHashClick,
|
|
59
|
+
className: computedClassName,
|
|
60
|
+
style: computedStyle,
|
|
61
|
+
...rest,
|
|
62
|
+
children: computedChildren
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
ActiveLink.displayName = 'ActiveLink';
|
|
66
|
+
export { ActiveLink };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface UniversalLinkProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {
|
|
3
|
+
/** 链接目标:内部路由、hash 锚点或外部 URL */
|
|
4
|
+
to: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* 统一的链接组件,用于替换原生 <a> 标签
|
|
8
|
+
* - 内部路由(/dashboard)→ react-router Link
|
|
9
|
+
* - Hash 锚点(#section)→ <a>
|
|
10
|
+
* - 外链(https://...)→ <a target="_blank">
|
|
11
|
+
*/
|
|
12
|
+
export declare const UniversalLink: React.ForwardRefExoticComponent<UniversalLinkProps & React.RefAttributes<HTMLAnchorElement>>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import react from "react";
|
|
3
|
+
import { Link } from "react-router-dom";
|
|
4
|
+
function isInternalRoute(to) {
|
|
5
|
+
return !to.startsWith('#') && !to.startsWith('http://') && !to.startsWith('https://') && !to.startsWith('//');
|
|
6
|
+
}
|
|
7
|
+
function isExternalLink(to) {
|
|
8
|
+
return to.startsWith('http://') || to.startsWith('https://') || to.startsWith('//');
|
|
9
|
+
}
|
|
10
|
+
const UniversalLink_UniversalLink = /*#__PURE__*/ react.forwardRef(function({ to, ...props }, ref) {
|
|
11
|
+
if (isInternalRoute(to)) return /*#__PURE__*/ jsx(Link, {
|
|
12
|
+
to: to,
|
|
13
|
+
ref: ref,
|
|
14
|
+
...props
|
|
15
|
+
});
|
|
16
|
+
return /*#__PURE__*/ jsx("a", {
|
|
17
|
+
href: to,
|
|
18
|
+
ref: ref,
|
|
19
|
+
...props,
|
|
20
|
+
...isExternalLink(to) && {
|
|
21
|
+
target: props.target ?? '_blank',
|
|
22
|
+
rel: props.rel ?? 'noopener noreferrer'
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
export { UniversalLink_UniversalLink as UniversalLink };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../../integrations/services';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../../integrations/services/index.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../trace';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../trace/index.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { getEnv } from '../../utils/getParentOrigin';
|
package/lib/auth.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { CanRole, AbilityContext, ROLE_SUBJECT } from '@lark-apaas/auth-sdk';
|
package/lib/auth.js
ADDED
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef } from "react";
|
|
3
3
|
import { useLocation, useNavigate } from "react-router-dom";
|
|
4
|
-
import { connectToParent } from "penpal";
|
|
5
4
|
import { useUpdatingRef } from "../../hooks/useUpdatingRef.js";
|
|
6
5
|
import { submitPostMessage } from "../../utils/postMessage.js";
|
|
7
|
-
import { getPreviewParentOrigin } from "../../utils/getParentOrigin.js";
|
|
8
|
-
import { childApi } from "./utils/childApi.js";
|
|
9
|
-
import "./utils/listenHot.js";
|
|
10
6
|
var IframeBridge_RouteMessageType = /*#__PURE__*/ function(RouteMessageType) {
|
|
11
7
|
RouteMessageType["RouteChange"] = "RouteChange";
|
|
12
8
|
RouteMessageType["RouteBack"] = "RouteBack";
|
|
@@ -16,32 +12,18 @@ var IframeBridge_RouteMessageType = /*#__PURE__*/ function(RouteMessageType) {
|
|
|
16
12
|
function isRouteMessageType(type) {
|
|
17
13
|
return Object.values(IframeBridge_RouteMessageType).includes(type);
|
|
18
14
|
}
|
|
19
|
-
async function connectParent() {
|
|
20
|
-
submitPostMessage({
|
|
21
|
-
type: 'PreviewReady',
|
|
22
|
-
data: {}
|
|
23
|
-
});
|
|
24
|
-
const connection = connectToParent({
|
|
25
|
-
parentOrigin: getPreviewParentOrigin(),
|
|
26
|
-
methods: {
|
|
27
|
-
...childApi
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
await connection.promise;
|
|
31
|
-
}
|
|
32
|
-
'production' !== process.env.NODE_ENV && connectParent();
|
|
33
15
|
function IframeBridge() {
|
|
34
16
|
const location = useLocation();
|
|
35
17
|
const navigate = useNavigate();
|
|
36
18
|
const navigateRef = useUpdatingRef(navigate);
|
|
37
19
|
const isActive = useRef(false);
|
|
38
|
-
const historyBack = useCallback(()=>{
|
|
20
|
+
const historyBack = useCallback((_payload)=>{
|
|
39
21
|
navigateRef.current(-1);
|
|
40
22
|
isActive.current = true;
|
|
41
23
|
}, [
|
|
42
24
|
navigateRef
|
|
43
25
|
]);
|
|
44
|
-
const historyForward = useCallback(()=>{
|
|
26
|
+
const historyForward = useCallback((_payload)=>{
|
|
45
27
|
navigateRef.current(1);
|
|
46
28
|
isActive.current = true;
|
|
47
29
|
}, [
|
|
@@ -69,8 +51,8 @@ function IframeBridge() {
|
|
|
69
51
|
location
|
|
70
52
|
]);
|
|
71
53
|
const handleMessage = useCallback((event)=>{
|
|
72
|
-
const
|
|
73
|
-
if (isRouteMessageType(data
|
|
54
|
+
const data = event.data ?? {};
|
|
55
|
+
if ('string' == typeof data.type && isRouteMessageType(data.type)) operatorMessage[data.type](data.data);
|
|
74
56
|
}, [
|
|
75
57
|
operatorMessage
|
|
76
58
|
]);
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { IBaseThemeProviderProps } from '../theme';
|
|
3
|
-
import '../../
|
|
3
|
+
import '../../runtime';
|
|
4
|
+
interface IBaseAuthProviderProps {
|
|
5
|
+
enableAuth?: boolean;
|
|
6
|
+
}
|
|
4
7
|
declare const AppContainer: React.FC<{
|
|
5
8
|
children: React.ReactNode;
|
|
6
|
-
} & IBaseThemeProviderProps>;
|
|
9
|
+
} & IBaseThemeProviderProps & IBaseAuthProviderProps>;
|
|
7
10
|
export default AppContainer;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect,
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
3
|
import { ConfigProvider } from "antd";
|
|
4
4
|
import { MiaodaInspector } from "@lark-apaas/miaoda-inspector";
|
|
5
5
|
import zh_CN from "antd/locale/zh_CN";
|
|
@@ -8,20 +8,14 @@ import { defaultUIConfig } from "../theme/ui-config.js";
|
|
|
8
8
|
import { Toaster } from "./sonner.js";
|
|
9
9
|
import { PageHoc } from "./PageHoc.js";
|
|
10
10
|
import { findValueByPixel, generateTailwindRadiusToken, themeColorTokenMap, themeMetaOptions } from "../theme/index.js";
|
|
11
|
-
import { registerDayjsPlugins } from "./dayjsPlugins.js";
|
|
12
|
-
import "../../index.css";
|
|
13
|
-
import { initAxiosConfig } from "../../utils/axiosConfig.js";
|
|
14
11
|
import { reportTeaEvent } from "./utils/tea.js";
|
|
15
12
|
import { useAppInfo } from "../../hooks/index.js";
|
|
16
13
|
import { TrackKey } from "../../types/tea.js";
|
|
17
14
|
import safety from "./safety.js";
|
|
18
15
|
import { getAppId } from "../../utils/getAppId.js";
|
|
19
|
-
import { ServerLogSSEClient } from "../../server-log/index.js";
|
|
20
16
|
import QueryProvider from "../QueryProvider/index.js";
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
initAxiosConfig();
|
|
24
|
-
initObservable();
|
|
17
|
+
import { AuthProvider } from "@lark-apaas/auth-sdk";
|
|
18
|
+
import "../../runtime/index.js";
|
|
25
19
|
const isMiaodaPreview = window.IS_MIAODA_PREVIEW;
|
|
26
20
|
const readCssVarColor = (varName, fallback)=>{
|
|
27
21
|
try {
|
|
@@ -33,9 +27,8 @@ const readCssVarColor = (varName, fallback)=>{
|
|
|
33
27
|
}
|
|
34
28
|
};
|
|
35
29
|
const App = (props)=>{
|
|
36
|
-
const { themeMeta = {} } = props;
|
|
30
|
+
const { themeMeta = {}, enableAuth } = props;
|
|
37
31
|
useAppInfo();
|
|
38
|
-
const serverLogClientRef = useRef(null);
|
|
39
32
|
const { rem } = findValueByPixel(themeMetaOptions.themeRadius, themeMeta.borderRadius) || {
|
|
40
33
|
rem: '0.625'
|
|
41
34
|
};
|
|
@@ -47,40 +40,6 @@ const App = (props)=>{
|
|
|
47
40
|
borderRadius: radiusToken
|
|
48
41
|
}
|
|
49
42
|
};
|
|
50
|
-
useEffect(()=>{
|
|
51
|
-
if ('production' !== process.env.NODE_ENV && window.parent !== window) {
|
|
52
|
-
try {
|
|
53
|
-
const backendUrl = window.location.origin;
|
|
54
|
-
serverLogClientRef.current = new ServerLogSSEClient({
|
|
55
|
-
serverUrl: backendUrl,
|
|
56
|
-
sseEndpoint: '/dev/logs/server-logs/stream',
|
|
57
|
-
debug: true
|
|
58
|
-
});
|
|
59
|
-
serverLogClientRef.current.start();
|
|
60
|
-
console.log('[AppContainer] Server log SSE client started');
|
|
61
|
-
} catch (error) {
|
|
62
|
-
console.error('[AppContainer] Failed to start server log SSE client:', error);
|
|
63
|
-
}
|
|
64
|
-
const handleVisibilityChange = ()=>{
|
|
65
|
-
if (!serverLogClientRef.current) return;
|
|
66
|
-
if (document.hidden) {
|
|
67
|
-
serverLogClientRef.current.pause();
|
|
68
|
-
console.log('[AppContainer] Tab hidden, SSE paused');
|
|
69
|
-
} else {
|
|
70
|
-
serverLogClientRef.current.resume();
|
|
71
|
-
console.log('[AppContainer] Tab visible, SSE resumed');
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
75
|
-
return ()=>{
|
|
76
|
-
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
|
77
|
-
if (serverLogClientRef.current) {
|
|
78
|
-
serverLogClientRef.current.stop();
|
|
79
|
-
console.log('[AppContainer] Server log SSE client stopped');
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
}, []);
|
|
84
43
|
useEffect(()=>{
|
|
85
44
|
if (isMiaodaPreview) fetch(`${location.origin}/ai/api/feida_preview/csrf`).then(()=>{
|
|
86
45
|
setTimeout(()=>{
|
|
@@ -99,7 +58,10 @@ const App = (props)=>{
|
|
|
99
58
|
}
|
|
100
59
|
});
|
|
101
60
|
}, []);
|
|
102
|
-
return /*#__PURE__*/ jsxs(
|
|
61
|
+
return /*#__PURE__*/ jsxs(AuthProvider, {
|
|
62
|
+
config: {
|
|
63
|
+
enable: enableAuth
|
|
64
|
+
},
|
|
103
65
|
children: [
|
|
104
66
|
/*#__PURE__*/ jsx(Toaster, {}),
|
|
105
67
|
'production' !== process.env.NODE_ENV && /*#__PURE__*/ jsx(MiaodaInspector, {
|
|
@@ -247,6 +209,7 @@ const AppContainer_AppContainer = (props)=>{
|
|
|
247
209
|
},
|
|
248
210
|
children: /*#__PURE__*/ jsx(App, {
|
|
249
211
|
themeMeta: props.themeMeta,
|
|
212
|
+
enableAuth: props.enableAuth,
|
|
250
213
|
children: children
|
|
251
214
|
})
|
|
252
215
|
})
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { AppEnv, observable } from "@lark-apaas/observable-web";
|
|
2
2
|
const initObservable = ()=>{
|
|
3
3
|
try {
|
|
4
|
+
const appId = window.appId;
|
|
4
5
|
observable.start({
|
|
5
6
|
serviceName: "app",
|
|
6
7
|
env: 'development' === process.env.NODE_ENV ? AppEnv.Dev : AppEnv.Prod,
|
|
7
8
|
collectorUrl: {
|
|
8
|
-
log: `/spark/app/${
|
|
9
|
-
|
|
9
|
+
log: `/spark/app/${appId}/runtime/api/v1/observability/logs/collect`,
|
|
10
|
+
trace: `/spark/app/${appId}/runtime/api/v1/observability/traces/collect`,
|
|
11
|
+
metric: `/spark/app/${appId}/runtime/api/v1/observability/metrics/collect`,
|
|
12
|
+
time: `/spark/api/v1/observability/app/${appId}/current_server_timestamp`
|
|
10
13
|
}
|
|
11
14
|
});
|
|
12
15
|
} catch (error) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect } from "react";
|
|
3
3
|
import { logger } from "../../logger/index.js";
|
|
4
|
-
import {
|
|
4
|
+
import { getHmrApi } from "../../utils/hmr-api.js";
|
|
5
5
|
import { submitPostMessage } from "../../utils/postMessage.js";
|
|
6
6
|
const RenderError = (props)=>{
|
|
7
7
|
const { error, resetErrorBoundary } = props;
|
|
@@ -27,16 +27,10 @@ const RenderError = (props)=>{
|
|
|
27
27
|
]);
|
|
28
28
|
useEffect(()=>{
|
|
29
29
|
if (!resetErrorBoundary) return;
|
|
30
|
-
const
|
|
31
|
-
if (
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
});
|
|
35
|
-
hot.addStatusHandler(handler);
|
|
36
|
-
return ()=>{
|
|
37
|
-
hot.removeStatusHandler(handler);
|
|
38
|
-
};
|
|
39
|
-
}
|
|
30
|
+
const hmr = getHmrApi();
|
|
31
|
+
if (hmr) return hmr.onSuccess(()=>{
|
|
32
|
+
resetErrorBoundary();
|
|
33
|
+
});
|
|
40
34
|
}, [
|
|
41
35
|
resetErrorBoundary
|
|
42
36
|
]);
|
|
@@ -15,7 +15,7 @@ const PagePlaceholder = ({ title = '页面待开发', description = '页面暂
|
|
|
15
15
|
children: title
|
|
16
16
|
}),
|
|
17
17
|
/*#__PURE__*/ jsx("div", {
|
|
18
|
-
className: "text-center text-muted-foreground text-base leading-6",
|
|
18
|
+
className: "text-center text-muted-foreground text-base leading-6 line-clamp-2 sm:max-w-[480px]",
|
|
19
19
|
children: description
|
|
20
20
|
})
|
|
21
21
|
]
|
|
@@ -67,20 +67,8 @@ const UserSelect = ({ mode = 'single', defaultValue, value, onChange, placeholde
|
|
|
67
67
|
if (!normalizedIds.length) return void setUiValue(void 0);
|
|
68
68
|
const fetchProfiles = async ()=>{
|
|
69
69
|
try {
|
|
70
|
-
const ids = normalizedIds.map((id)=>Number(id)).filter((id)=>Number.isFinite(id));
|
|
71
|
-
if (!ids.length) {
|
|
72
|
-
const profiles = normalizedIds.map((id)=>inputProfilesMap.get(id) ?? {
|
|
73
|
-
user_id: id,
|
|
74
|
-
name: '',
|
|
75
|
-
avatar: '',
|
|
76
|
-
email: '',
|
|
77
|
-
status: 1
|
|
78
|
-
});
|
|
79
|
-
setUiValue('single' === mode ? profiles[0] : profiles);
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
70
|
const dataloom = await getDataloom();
|
|
83
|
-
const response = await dataloom.service.user.getByIds(
|
|
71
|
+
const response = await dataloom.service.user.getByIds(normalizedIds);
|
|
84
72
|
const fetchedList = Array.isArray(response?.data) ? response?.data : Array.isArray(response?.data?.user_list) ? response?.data?.user_list : [];
|
|
85
73
|
const fetchedMap = new Map();
|
|
86
74
|
fetchedList.forEach((profile)=>{
|
package/lib/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createClient } from "@lark-apaas/client-capability";
|
|
2
2
|
import { normalizeBasePath } from "./utils/utils.js";
|
|
3
|
+
import { logger } from "./logger/index.js";
|
|
3
4
|
import { version } from "../package.json";
|
|
4
5
|
const capabilityClient = createClient({
|
|
5
6
|
baseURL: normalizeBasePath(process.env.CLIENT_BASE_PATH),
|
|
@@ -7,7 +8,8 @@ const capabilityClient = createClient({
|
|
|
7
8
|
headers: {
|
|
8
9
|
'X-Suda-Csrf-Token': window.csrfToken ?? ''
|
|
9
10
|
}
|
|
10
|
-
}
|
|
11
|
+
},
|
|
12
|
+
logger: logger
|
|
11
13
|
});
|
|
12
14
|
const src = {
|
|
13
15
|
version: version
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SearchDepartmentsParams, SearchDepartmentsResponse } from './types';
|
|
2
|
+
export type DepartmentServiceConfig = {
|
|
3
|
+
getAppId?: () => string | null | undefined;
|
|
4
|
+
searchDepartmentUrl?: (appId: string) => string;
|
|
5
|
+
};
|
|
6
|
+
export declare class DepartmentService {
|
|
7
|
+
private config;
|
|
8
|
+
constructor(config?: DepartmentServiceConfig);
|
|
9
|
+
searchDepartments(params: SearchDepartmentsParams): Promise<SearchDepartmentsResponse>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { getAppId } from "../../utils/getAppId.js";
|
|
2
|
+
const DEFAULT_CONFIG = {
|
|
3
|
+
getAppId: ()=>getAppId(window.location.pathname),
|
|
4
|
+
searchDepartmentUrl: (appId)=>`/af/app/${appId}/runtime/api/v1/account/search_department`
|
|
5
|
+
};
|
|
6
|
+
class DepartmentService {
|
|
7
|
+
config;
|
|
8
|
+
constructor(config = {}){
|
|
9
|
+
this.config = {
|
|
10
|
+
...DEFAULT_CONFIG,
|
|
11
|
+
...config
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
async searchDepartments(params) {
|
|
15
|
+
const appId = this.config.getAppId();
|
|
16
|
+
if (!appId) throw new Error('Failed to get appId');
|
|
17
|
+
const response = await fetch(this.config.searchDepartmentUrl(appId), {
|
|
18
|
+
method: 'POST',
|
|
19
|
+
headers: {
|
|
20
|
+
'Content-Type': 'application/json'
|
|
21
|
+
},
|
|
22
|
+
body: JSON.stringify(params),
|
|
23
|
+
credentials: 'include'
|
|
24
|
+
});
|
|
25
|
+
if (!response.ok) throw new Error('Failed to search departments');
|
|
26
|
+
return response.json();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export { DepartmentService };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AccountType, UserProfileData } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* 获取 CDN 资源 URL
|
|
4
|
+
*/
|
|
5
|
+
export declare function getAssetsUrl(path: string): string;
|
|
6
|
+
export type UserProfileServiceConfig = {
|
|
7
|
+
getAppId?: () => string | null | undefined;
|
|
8
|
+
userProfileUrl?: (appId: string) => string;
|
|
9
|
+
};
|
|
10
|
+
export declare class UserProfileService {
|
|
11
|
+
private config;
|
|
12
|
+
constructor(config?: UserProfileServiceConfig);
|
|
13
|
+
getUserProfile(userId: string, accountType?: AccountType, signal?: AbortSignal): Promise<UserProfileData>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { getAppId } from "../../utils/getAppId.js";
|
|
2
|
+
const CDN_HOST = 'https://lf3-static.bytednsdoc.com';
|
|
3
|
+
function getAssetsUrl(path) {
|
|
4
|
+
return `${CDN_HOST}${path}`;
|
|
5
|
+
}
|
|
6
|
+
const DEFAULT_CONFIG = {
|
|
7
|
+
getAppId: ()=>getAppId(window.location.pathname),
|
|
8
|
+
userProfileUrl: (appId)=>`/af/app/${appId}/runtime/api/v1/account/user_profile`
|
|
9
|
+
};
|
|
10
|
+
class UserProfileService {
|
|
11
|
+
config;
|
|
12
|
+
constructor(config = {}){
|
|
13
|
+
this.config = {
|
|
14
|
+
...DEFAULT_CONFIG,
|
|
15
|
+
...config
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
async getUserProfile(userId, accountType = 'apaas', signal) {
|
|
19
|
+
const appId = this.config.getAppId();
|
|
20
|
+
if (!appId) throw new Error('Failed to get appId');
|
|
21
|
+
const params = new URLSearchParams();
|
|
22
|
+
if ('lark' === accountType) params.append('larkUserID', userId);
|
|
23
|
+
else params.append('userID', userId);
|
|
24
|
+
const response = await fetch(`${this.config.userProfileUrl(appId)}?${params.toString()}`, {
|
|
25
|
+
signal,
|
|
26
|
+
headers: {
|
|
27
|
+
'Content-Type': 'application/json'
|
|
28
|
+
},
|
|
29
|
+
credentials: 'include'
|
|
30
|
+
});
|
|
31
|
+
if (!response.ok) throw new Error(`Failed to fetch user profile: ${response.status}`);
|
|
32
|
+
const data = await response.json();
|
|
33
|
+
return data.data;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export { UserProfileService, getAssetsUrl };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { BatchGetUsersResponse, SearchUsersParams, SearchUsersResponse } from './types';
|
|
2
|
+
export type UserServiceConfig = {
|
|
3
|
+
getAppId?: () => string | null | undefined;
|
|
4
|
+
searchUserUrl?: (appId: string) => string;
|
|
5
|
+
listUsersUrl?: (appId: string) => string;
|
|
6
|
+
};
|
|
7
|
+
export declare class UserService {
|
|
8
|
+
private config;
|
|
9
|
+
constructor(config?: UserServiceConfig);
|
|
10
|
+
searchUsers(params: SearchUsersParams): Promise<SearchUsersResponse>;
|
|
11
|
+
listUsersByIds(userIds: string[]): Promise<BatchGetUsersResponse>;
|
|
12
|
+
}
|