@lark-apaas/client-toolkit 1.2.35-alpha.2 → 1.2.35-alpha.3
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/lib/apis/tools/storage.js +2 -1
- package/lib/components/AppContainer/index.js +33 -37
- package/lib/components/AppContainer/safety.js +40 -43
- package/lib/components/ErrorRender/index.d.ts +2 -2
- package/lib/components/ErrorRender/index.js +2 -1
- package/lib/components/NotFoundRender/index.js +8 -3
- package/lib/components/PagePlaceholder/index.js +10 -5
- package/lib/components/User/UserProfile/UserProfileUI.js +5 -4
- package/lib/components/User/UserSelectUI/UserSelectUI.js +4 -2
- package/lib/components/ui/confirm.js +8 -4
- package/lib/index.js +2 -1
- package/lib/integrations/dataloom.d.ts +1 -1
- package/lib/integrations/dataloom.js +9 -4
- package/lib/locales/index.d.ts +15 -0
- package/lib/locales/index.js +10 -0
- package/lib/locales/messages.d.ts +12 -0
- package/lib/locales/messages.js +108 -0
- package/lib/utils/getInitialInfo.d.ts +7 -0
- package/lib/utils/getInitialInfo.js +24 -3
- package/lib/utils/locale.d.ts +17 -0
- package/lib/utils/locale.js +41 -0
- package/package.json +2 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useState } from "react";
|
|
2
|
+
import react, { Suspense, useEffect, useMemo, 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";
|
|
@@ -10,20 +10,33 @@ import { reportTeaEvent } from "./utils/tea.js";
|
|
|
10
10
|
import { useAppInfo } from "../../hooks/index.js";
|
|
11
11
|
import { TrackKey } from "../../types/tea.js";
|
|
12
12
|
import { slardar } from "@lark-apaas/internal-slardar";
|
|
13
|
-
import safety from "./safety.js";
|
|
14
13
|
import { getAppId } from "../../utils/getAppId.js";
|
|
15
14
|
import { isNewPathEnabled } from "../../utils/apiPath.js";
|
|
16
15
|
import QueryProvider from "../QueryProvider/index.js";
|
|
17
16
|
import { AuthProvider } from "@lark-apaas/auth-sdk";
|
|
18
17
|
import "../../runtime/index.js";
|
|
18
|
+
const Safety = /*#__PURE__*/ react.lazy(()=>import("./safety.js"));
|
|
19
19
|
const isMiaodaPreview = window.IS_MIAODA_PREVIEW;
|
|
20
|
-
const
|
|
20
|
+
const readAllCssVarColors = ()=>{
|
|
21
21
|
try {
|
|
22
|
-
if ('undefined' == typeof document) return
|
|
23
|
-
const
|
|
24
|
-
|
|
22
|
+
if ('undefined' == typeof document) return {};
|
|
23
|
+
const styles = getComputedStyle(document.documentElement);
|
|
24
|
+
const read = (name)=>styles.getPropertyValue(name).trim() || void 0;
|
|
25
|
+
return {
|
|
26
|
+
background: read('--background'),
|
|
27
|
+
destructive: read('--destructive'),
|
|
28
|
+
primary: read('--primary'),
|
|
29
|
+
foreground: read('--foreground'),
|
|
30
|
+
warning: read('--warning'),
|
|
31
|
+
success: read('--success'),
|
|
32
|
+
muted: read('--muted'),
|
|
33
|
+
mutedForeground: read('--muted-foreground'),
|
|
34
|
+
border: read('--border'),
|
|
35
|
+
popover: read('--popover'),
|
|
36
|
+
accent: read('--accent')
|
|
37
|
+
};
|
|
25
38
|
} catch {
|
|
26
|
-
return
|
|
39
|
+
return {};
|
|
27
40
|
}
|
|
28
41
|
};
|
|
29
42
|
const appFlags = process.env.APP_FLAGS || {};
|
|
@@ -69,13 +82,17 @@ const App = (props)=>{
|
|
|
69
82
|
}, []);
|
|
70
83
|
const appId = getAppId();
|
|
71
84
|
const permissionApiUrl = isNewPathEnabled() ? `/app/${appId}/__runtime__/api/v1/permissions/roles` : `/spark/app/${appId}/runtime/api/v1/permissions/roles`;
|
|
72
|
-
|
|
73
|
-
config: {
|
|
85
|
+
const authConfig = useMemo(()=>({
|
|
74
86
|
enable: enableAuth,
|
|
75
87
|
permissionApi: {
|
|
76
88
|
url: permissionApiUrl
|
|
77
89
|
}
|
|
78
|
-
},
|
|
90
|
+
}), [
|
|
91
|
+
enableAuth,
|
|
92
|
+
permissionApiUrl
|
|
93
|
+
]);
|
|
94
|
+
return /*#__PURE__*/ jsxs(AuthProvider, {
|
|
95
|
+
config: authConfig,
|
|
79
96
|
children: [
|
|
80
97
|
!disableToaster && true !== appFlags.customToaster && /*#__PURE__*/ jsx(Toaster, {}),
|
|
81
98
|
'production' !== process.env.NODE_ENV && /*#__PURE__*/ jsx(MiaodaInspector, {}),
|
|
@@ -88,19 +105,7 @@ const App = (props)=>{
|
|
|
88
105
|
};
|
|
89
106
|
const AppContainer_AppContainer = (props)=>{
|
|
90
107
|
const { children } = props;
|
|
91
|
-
const [cssColors, setCssColors] = useState(
|
|
92
|
-
background: readCssVarColor('--background'),
|
|
93
|
-
destructive: readCssVarColor('--destructive'),
|
|
94
|
-
primary: readCssVarColor('--primary'),
|
|
95
|
-
foreground: readCssVarColor('--foreground'),
|
|
96
|
-
warning: readCssVarColor('--warning'),
|
|
97
|
-
success: readCssVarColor('--success'),
|
|
98
|
-
muted: readCssVarColor('--muted'),
|
|
99
|
-
mutedForeground: readCssVarColor('--muted-foreground'),
|
|
100
|
-
border: readCssVarColor('--border'),
|
|
101
|
-
popover: readCssVarColor('--popover'),
|
|
102
|
-
accent: readCssVarColor('--accent')
|
|
103
|
-
}));
|
|
108
|
+
const [cssColors, setCssColors] = useState(readAllCssVarColors);
|
|
104
109
|
useEffect(()=>{
|
|
105
110
|
if ('production' === process.env.NODE_ENV) return ()=>{};
|
|
106
111
|
const observer = new MutationObserver((mutations)=>{
|
|
@@ -119,19 +124,7 @@ const AppContainer_AppContainer = (props)=>{
|
|
|
119
124
|
const maxRetries = 10;
|
|
120
125
|
const retryDelay = 400;
|
|
121
126
|
const updateColors = ()=>{
|
|
122
|
-
const newColors =
|
|
123
|
-
background: readCssVarColor('--background'),
|
|
124
|
-
destructive: readCssVarColor('--destructive'),
|
|
125
|
-
primary: readCssVarColor('--primary'),
|
|
126
|
-
foreground: readCssVarColor('--foreground'),
|
|
127
|
-
warning: readCssVarColor('--warning'),
|
|
128
|
-
success: readCssVarColor('--success'),
|
|
129
|
-
muted: readCssVarColor('--muted'),
|
|
130
|
-
mutedForeground: readCssVarColor('--muted-foreground'),
|
|
131
|
-
border: readCssVarColor('--border'),
|
|
132
|
-
popover: readCssVarColor('--popover'),
|
|
133
|
-
accent: readCssVarColor('--accent')
|
|
134
|
-
};
|
|
127
|
+
const newColors = readAllCssVarColors();
|
|
135
128
|
setCssColors((currentColors)=>{
|
|
136
129
|
if (JSON.stringify(currentColors) !== JSON.stringify(newColors)) {
|
|
137
130
|
retryCount = 0;
|
|
@@ -203,7 +196,10 @@ const AppContainer_AppContainer = (props)=>{
|
|
|
203
196
|
};
|
|
204
197
|
return /*#__PURE__*/ jsxs(Fragment, {
|
|
205
198
|
children: [
|
|
206
|
-
/*#__PURE__*/ jsx(
|
|
199
|
+
/*#__PURE__*/ jsx(Suspense, {
|
|
200
|
+
fallback: null,
|
|
201
|
+
children: /*#__PURE__*/ jsx(Safety, {})
|
|
202
|
+
}),
|
|
207
203
|
/*#__PURE__*/ jsx(QueryProvider, {
|
|
208
204
|
children: /*#__PURE__*/ jsx(ConfigProvider, {
|
|
209
205
|
locale: zh_CN,
|
|
@@ -1,59 +1,58 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef, useState } from "react";
|
|
3
3
|
import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover.js";
|
|
4
|
-
import { getCsrfToken } from "../../utils/getCsrfToken.js";
|
|
5
4
|
import { getAppId } from "../../utils/getAppId.js";
|
|
5
|
+
import { getCsrfToken } from "../../utils/getCsrfToken.js";
|
|
6
6
|
import { isNewPathEnabled } from "../../utils/apiPath.js";
|
|
7
7
|
import { useIsMobile } from "../../hooks/index.js";
|
|
8
8
|
import { X } from "lucide-react";
|
|
9
9
|
import { Sheet, SheetContent, SheetTrigger } from "../ui/drawer.js";
|
|
10
|
+
import { t } from "../../locales/index.js";
|
|
10
11
|
const Component = ()=>{
|
|
11
12
|
const HasClosedKey = `miaoda-creatByMiaoda-has-closed-${getAppId()}`;
|
|
12
13
|
const [visible, setVisible] = useState(!window.localStorage?.getItem(HasClosedKey));
|
|
13
14
|
const [open, setOpen] = useState(false);
|
|
14
15
|
const isMobile = useIsMobile();
|
|
15
|
-
const [userinfo, setUserinfo] = useState(null);
|
|
16
|
-
const [isInternetVisible, setIsInternetVisible] = useState(false);
|
|
17
|
-
const [showBadge, setShowBadge] = useState(true);
|
|
18
|
-
const [badgeLoaded, setBadgeLoaded] = useState(false);
|
|
19
16
|
const timeoutRef = useRef(null);
|
|
17
|
+
const platformData = window.__platform_data__ || window.__platform__ || {};
|
|
18
|
+
const appId = getAppId() || platformData.appId;
|
|
19
|
+
const showBadge = false !== platformData.showBadge;
|
|
20
|
+
const [tenantName, setTenantName] = useState('');
|
|
21
|
+
const [isInternetVisible, setIsInternetVisible] = useState(false);
|
|
20
22
|
useEffect(()=>{
|
|
21
|
-
|
|
23
|
+
if (!showBadge || !visible) return;
|
|
24
|
+
const csrfToken = getCsrfToken() ?? '';
|
|
22
25
|
const csrfHeaders = {
|
|
23
|
-
'X-Suda-Csrf-Token':
|
|
26
|
+
'X-Suda-Csrf-Token': csrfToken
|
|
24
27
|
};
|
|
25
28
|
const tenantInfoUrl = isNewPathEnabled() ? `/app/${appId}/__runtime__/api/v1/studio/tenant_info` : `/spark/b/${appId}/tenant_info`;
|
|
26
29
|
fetch(tenantInfoUrl, {
|
|
27
30
|
headers: csrfHeaders
|
|
28
31
|
}).then((res)=>res.json()).then((data)=>{
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}).then((res)=>res.json()).then((data)=>{
|
|
36
|
-
const badge = data?.data?.app_info?.show_badge;
|
|
37
|
-
setShowBadge(false !== badge);
|
|
38
|
-
}).catch(()=>{
|
|
39
|
-
setShowBadge(true);
|
|
40
|
-
}).finally(()=>{
|
|
41
|
-
setBadgeLoaded(true);
|
|
32
|
+
if (data?.status_code === '0' || data?.code === 0) {
|
|
33
|
+
setTenantName(data?.data?.tenant_info?.name || '');
|
|
34
|
+
setIsInternetVisible(data?.data?.is_internet_visible || false);
|
|
35
|
+
}
|
|
36
|
+
}).catch((e)=>{
|
|
37
|
+
console.warn('Failed to fetch tenant info:', e);
|
|
42
38
|
});
|
|
43
|
-
}, [
|
|
39
|
+
}, [
|
|
40
|
+
appId,
|
|
41
|
+
showBadge,
|
|
42
|
+
visible
|
|
43
|
+
]);
|
|
44
44
|
useEffect(()=>{
|
|
45
45
|
if (isMobile) {
|
|
46
46
|
const link = document.createElement('link');
|
|
47
47
|
link.rel = 'preload';
|
|
48
48
|
link.as = 'image';
|
|
49
|
-
link.href = '
|
|
49
|
+
link.href = t('safety.cover.mobile');
|
|
50
50
|
document.head.appendChild(link);
|
|
51
51
|
}
|
|
52
52
|
}, [
|
|
53
53
|
isMobile
|
|
54
54
|
]);
|
|
55
55
|
if ('production' !== process.env.NODE_ENV) return null;
|
|
56
|
-
if (!badgeLoaded) return null;
|
|
57
56
|
if (!showBadge) return null;
|
|
58
57
|
if (!visible) return null;
|
|
59
58
|
if (isMobile) return /*#__PURE__*/ jsxs(Sheet, {
|
|
@@ -74,7 +73,7 @@ const Component = ()=>{
|
|
|
74
73
|
}),
|
|
75
74
|
/*#__PURE__*/ jsx("p", {
|
|
76
75
|
className: "shrink-0 min-w-[28px] m-0! text-[#EBEBEB]",
|
|
77
|
-
children:
|
|
76
|
+
children: t('safety.badge.label')
|
|
78
77
|
})
|
|
79
78
|
]
|
|
80
79
|
})
|
|
@@ -90,7 +89,7 @@ const Component = ()=>{
|
|
|
90
89
|
onClick: ()=>setOpen(false)
|
|
91
90
|
}),
|
|
92
91
|
/*#__PURE__*/ jsx("img", {
|
|
93
|
-
src:
|
|
92
|
+
src: t('safety.cover.mobile'),
|
|
94
93
|
alt: "",
|
|
95
94
|
className: "w-full h-full",
|
|
96
95
|
onClick: ()=>{
|
|
@@ -110,12 +109,11 @@ const Component = ()=>{
|
|
|
110
109
|
src: "https://lf3-static.bytednsdoc.com/obj/eden-cn/LMfspH/ljhwZthlaukjlkulzlp/icon/icon_company_outlined.svg",
|
|
111
110
|
className: "shrink-0 w-[14px] h-[14px]"
|
|
112
111
|
}),
|
|
113
|
-
/*#__PURE__*/
|
|
112
|
+
/*#__PURE__*/ jsx("p", {
|
|
114
113
|
className: "shrink-0 min-w-[96px] m-0! text-[#646A73] text-sm",
|
|
115
|
-
children:
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
]
|
|
114
|
+
children: t('safety.tenant.operated', {
|
|
115
|
+
name: tenantName
|
|
116
|
+
})
|
|
119
117
|
})
|
|
120
118
|
]
|
|
121
119
|
}),
|
|
@@ -128,7 +126,7 @@ const Component = ()=>{
|
|
|
128
126
|
}),
|
|
129
127
|
/*#__PURE__*/ jsx("p", {
|
|
130
128
|
className: "shrink-0 min-w-[163px] m-0! text-[#646A73] text-sm",
|
|
131
|
-
children:
|
|
129
|
+
children: t('safety.ai.disclaimer')
|
|
132
130
|
})
|
|
133
131
|
]
|
|
134
132
|
})
|
|
@@ -146,14 +144,14 @@ const Component = ()=>{
|
|
|
146
144
|
setTimeout(()=>setVisible(false), 200);
|
|
147
145
|
window.localStorage?.setItem(HasClosedKey, 'true');
|
|
148
146
|
},
|
|
149
|
-
children:
|
|
147
|
+
children: t('safety.button.dontShowAgain')
|
|
150
148
|
}),
|
|
151
149
|
/*#__PURE__*/ jsx("div", {
|
|
152
150
|
className: "flex-1 flex rounded-[99px] items-center justify-center border-[0.5px] border-black bg-black text-white cursor-pointer text-lg py-[12px]",
|
|
153
151
|
onClick: ()=>{
|
|
154
152
|
window.open('https://miaoda.feishu.cn/landing', '_blank');
|
|
155
153
|
},
|
|
156
|
-
children:
|
|
154
|
+
children: t('safety.button.learnMore')
|
|
157
155
|
})
|
|
158
156
|
]
|
|
159
157
|
})
|
|
@@ -186,7 +184,7 @@ const Component = ()=>{
|
|
|
186
184
|
}),
|
|
187
185
|
/*#__PURE__*/ jsx("p", {
|
|
188
186
|
className: "shrink-0 min-w-[60px] m-0! text-[#EBEBEB]",
|
|
189
|
-
children:
|
|
187
|
+
children: t('safety.badge.builtWith')
|
|
190
188
|
})
|
|
191
189
|
]
|
|
192
190
|
})
|
|
@@ -210,7 +208,7 @@ const Component = ()=>{
|
|
|
210
208
|
className: "flex flex-col bg-[#1A1A1A]",
|
|
211
209
|
children: [
|
|
212
210
|
/*#__PURE__*/ jsx("img", {
|
|
213
|
-
src:
|
|
211
|
+
src: t('safety.cover.pc'),
|
|
214
212
|
alt: "",
|
|
215
213
|
className: "w-[286px] h-[128px] cursor-pointer",
|
|
216
214
|
onClick: ()=>{
|
|
@@ -230,12 +228,11 @@ const Component = ()=>{
|
|
|
230
228
|
src: "https://lf3-static.bytednsdoc.com/obj/eden-cn/LMfspH/ljhwZthlaukjlkulzlp/icon/icon_company_outlined.svg",
|
|
231
229
|
className: "shrink-0 w-[12px] h-[12px]"
|
|
232
230
|
}),
|
|
233
|
-
/*#__PURE__*/
|
|
231
|
+
/*#__PURE__*/ jsx("p", {
|
|
234
232
|
className: "shrink-0 min-w-[96px] m-0! text-[#a6a6a6]",
|
|
235
|
-
children:
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
]
|
|
233
|
+
children: t('safety.tenant.operated', {
|
|
234
|
+
name: tenantName
|
|
235
|
+
})
|
|
239
236
|
})
|
|
240
237
|
]
|
|
241
238
|
}),
|
|
@@ -248,7 +245,7 @@ const Component = ()=>{
|
|
|
248
245
|
}),
|
|
249
246
|
/*#__PURE__*/ jsx("p", {
|
|
250
247
|
className: "shrink-0 min-w-[163px] m-0! text-[#a6a6a6]",
|
|
251
|
-
children:
|
|
248
|
+
children: t('safety.ai.disclaimer')
|
|
252
249
|
})
|
|
253
250
|
]
|
|
254
251
|
})
|
|
@@ -266,7 +263,7 @@ const Component = ()=>{
|
|
|
266
263
|
setVisible(false);
|
|
267
264
|
window.localStorage?.setItem(HasClosedKey, 'true');
|
|
268
265
|
},
|
|
269
|
-
children:
|
|
266
|
+
children: t('safety.button.dontShowAgain')
|
|
270
267
|
}),
|
|
271
268
|
/*#__PURE__*/ jsx("div", {
|
|
272
269
|
className: "flex-1 flex rounded-[8px] items-center justify-center h-[34px] border-[0.5px] border-solid border-[#ffffff1c] hover:border-[#ffffff33] bg-[#ffffff08] hover:bg-[#ffffff14] cursor-pointer text-[#ebebeb]",
|
|
@@ -274,7 +271,7 @@ const Component = ()=>{
|
|
|
274
271
|
onClick: ()=>{
|
|
275
272
|
window.open('https://miaoda.feishu.cn/landing', '_blank');
|
|
276
273
|
},
|
|
277
|
-
children:
|
|
274
|
+
children: t('safety.button.learnMore')
|
|
278
275
|
})
|
|
279
276
|
]
|
|
280
277
|
})
|
|
@@ -3,6 +3,7 @@ import { useEffect } from "react";
|
|
|
3
3
|
import { logger } from "../../logger/index.js";
|
|
4
4
|
import { getHmrApi } from "../../utils/hmr-api.js";
|
|
5
5
|
import { submitPostMessage } from "../../utils/postMessage.js";
|
|
6
|
+
import { t } from "../../locales/index.js";
|
|
6
7
|
const RenderError = (props)=>{
|
|
7
8
|
const { error, resetErrorBoundary } = props;
|
|
8
9
|
useEffect(()=>{
|
|
@@ -46,7 +47,7 @@ const RenderError = (props)=>{
|
|
|
46
47
|
}),
|
|
47
48
|
/*#__PURE__*/ jsx("p", {
|
|
48
49
|
className: "text-l/[22px] text-[14px] text-[#1F2329] font-medium",
|
|
49
|
-
children:
|
|
50
|
+
children: t('errorRender.title')
|
|
50
51
|
})
|
|
51
52
|
]
|
|
52
53
|
})
|
|
@@ -2,16 +2,21 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useLocation } from "react-router-dom";
|
|
3
3
|
import { useEffect } from "react";
|
|
4
4
|
import { logger } from "../../logger/index.js";
|
|
5
|
+
import { t } from "../../locales/index.js";
|
|
5
6
|
const NotFound = ()=>{
|
|
6
7
|
const location = useLocation();
|
|
7
8
|
useEffect(()=>{
|
|
8
9
|
logger.log({
|
|
9
10
|
level: 'error',
|
|
10
11
|
args: [
|
|
11
|
-
|
|
12
|
+
t('notFound.logger.pageNotExist', {
|
|
13
|
+
path: location.pathname
|
|
14
|
+
})
|
|
12
15
|
],
|
|
13
16
|
meta: {
|
|
14
|
-
repairMessage:
|
|
17
|
+
repairMessage: t('notFound.logger.repairMessage', {
|
|
18
|
+
path: location.pathname
|
|
19
|
+
}),
|
|
15
20
|
noStacktrace: true,
|
|
16
21
|
stacktrace: [],
|
|
17
22
|
type: 'not-found'
|
|
@@ -30,7 +35,7 @@ const NotFound = ()=>{
|
|
|
30
35
|
}),
|
|
31
36
|
/*#__PURE__*/ jsx("p", {
|
|
32
37
|
className: "text-l/[22px] text-[14px] text-[#1F2329] font-medium",
|
|
33
|
-
children:
|
|
38
|
+
children: t('notFound.pageNotExist')
|
|
34
39
|
})
|
|
35
40
|
]
|
|
36
41
|
});
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import "react";
|
|
3
|
-
|
|
3
|
+
import { t } from "../../locales/index.js";
|
|
4
|
+
const PagePlaceholder_PagePlaceholder = ({ title, description })=>{
|
|
5
|
+
const defaultTitle = t('pagePlaceholder.title');
|
|
6
|
+
const defaultDescription = t("pagePlaceholder.description");
|
|
7
|
+
return /*#__PURE__*/ jsxs("div", {
|
|
4
8
|
className: "flex flex-col items-center justify-center px-6 w-full h-[calc(100vh-64px)] text-center",
|
|
5
9
|
children: [
|
|
6
10
|
/*#__PURE__*/ jsx("img", {
|
|
@@ -12,13 +16,14 @@ const PagePlaceholder = ({ title = '页面待开发', description = '页面暂
|
|
|
12
16
|
}),
|
|
13
17
|
/*#__PURE__*/ jsx("div", {
|
|
14
18
|
className: "text-center text-foreground text-base font-medium mb-1 leading-6",
|
|
15
|
-
children: title
|
|
19
|
+
children: title ?? defaultTitle
|
|
16
20
|
}),
|
|
17
21
|
/*#__PURE__*/ jsx("div", {
|
|
18
22
|
className: "text-center text-muted-foreground text-base leading-6 line-clamp-2 sm:max-w-[480px]",
|
|
19
|
-
children: description
|
|
23
|
+
children: description ?? defaultDescription
|
|
20
24
|
})
|
|
21
25
|
]
|
|
22
26
|
});
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
};
|
|
28
|
+
const PagePlaceholder = PagePlaceholder_PagePlaceholder;
|
|
29
|
+
export { PagePlaceholder as default };
|
|
@@ -3,6 +3,7 @@ import { useState } from "react";
|
|
|
3
3
|
import { clsxWithTw } from "../../../utils/utils.js";
|
|
4
4
|
import { getChatAppLink } from "./utils.js";
|
|
5
5
|
import { OverflowTooltipText } from "../../ui/overflow-tooltip-text.js";
|
|
6
|
+
import { t } from "../../../locales/index.js";
|
|
6
7
|
function UserProfileUI({ user }) {
|
|
7
8
|
const [avatarError, setAvatarError] = useState(false);
|
|
8
9
|
const canShowAvatar = Boolean(user.avatar) && !avatarError;
|
|
@@ -52,7 +53,7 @@ function UserProfileUI({ user }) {
|
|
|
52
53
|
}),
|
|
53
54
|
'inactive' === user.status && /*#__PURE__*/ jsx("span", {
|
|
54
55
|
className: "shrink-0 inline-flex items-center rounded-sm bg-destructive px-2 py-0.5 text-xs font-medium text-white",
|
|
55
|
-
children:
|
|
56
|
+
children: t('userProfile.status.inactive')
|
|
56
57
|
})
|
|
57
58
|
]
|
|
58
59
|
}),
|
|
@@ -75,7 +76,7 @@ function UserProfileUI({ user }) {
|
|
|
75
76
|
fill: "currentColor"
|
|
76
77
|
})
|
|
77
78
|
}),
|
|
78
|
-
|
|
79
|
+
t('userProfile.button.message')
|
|
79
80
|
]
|
|
80
81
|
})
|
|
81
82
|
}) : null
|
|
@@ -90,7 +91,7 @@ function UserProfileUI({ user }) {
|
|
|
90
91
|
children: [
|
|
91
92
|
/*#__PURE__*/ jsx("span", {
|
|
92
93
|
className: "min-w-[74px] text-sm text-muted-foreground",
|
|
93
|
-
children:
|
|
94
|
+
children: t('userProfile.label.department')
|
|
94
95
|
}),
|
|
95
96
|
/*#__PURE__*/ jsx("span", {
|
|
96
97
|
className: clsxWithTw('flex-1 break-all text-sm text-foreground', {
|
|
@@ -105,7 +106,7 @@ function UserProfileUI({ user }) {
|
|
|
105
106
|
children: [
|
|
106
107
|
/*#__PURE__*/ jsx("span", {
|
|
107
108
|
className: "min-w-[74px] text-sm text-muted-foreground",
|
|
108
|
-
children:
|
|
109
|
+
children: t('userProfile.label.email')
|
|
109
110
|
}),
|
|
110
111
|
user.email ? /*#__PURE__*/ jsx("a", {
|
|
111
112
|
href: `mailto:${user.email}`,
|
|
@@ -7,7 +7,9 @@ import { MultipleSelectionTags } from "./MultipleSelectionTags.js";
|
|
|
7
7
|
import { SingleSelectionPreview } from "./SingleSelectionPreview.js";
|
|
8
8
|
import { ActionButtons } from "./ActionButtons.js";
|
|
9
9
|
import { Dropdown } from "./Dropdown.js";
|
|
10
|
-
|
|
10
|
+
import { t } from "../../../locales/index.js";
|
|
11
|
+
const UserSelectUI = ({ mode = 'single', value, onChange, placeholder, disabled = false, allowClear = true, onSearch, loading = false, options = [] })=>{
|
|
12
|
+
const defaultPlaceholder = t('userSelect.placeholder');
|
|
11
13
|
const [isOpen, setIsOpen] = useState(false);
|
|
12
14
|
const [searchText, setSearchText] = useState('');
|
|
13
15
|
const [searchResults, setSearchResults] = useState([]);
|
|
@@ -135,7 +137,7 @@ const UserSelectUI = ({ mode = 'single', value, onChange, placeholder = '请选
|
|
|
135
137
|
const singleSelectedUser = 'single' === mode && hasSelection ? selectedUsers[0] : void 0;
|
|
136
138
|
const showSingleSelection = !!singleSelectedUser && 0 === searchText.length;
|
|
137
139
|
const canClear = allowClear && hasSelection && !disabled;
|
|
138
|
-
const inputPlaceholder = hasSelection ? '' : placeholder;
|
|
140
|
+
const inputPlaceholder = hasSelection ? '' : placeholder ?? defaultPlaceholder;
|
|
139
141
|
const focusInputAtStart = ()=>{
|
|
140
142
|
const input = inputRef.current;
|
|
141
143
|
if (!input) return;
|
|
@@ -4,11 +4,15 @@ import { createRoot } from "react-dom/client";
|
|
|
4
4
|
import { Content, Description, Overlay, Portal, Root, Title } from "@radix-ui/react-dialog";
|
|
5
5
|
import { Button } from "./button.js";
|
|
6
6
|
import { clsxWithTw } from "../../utils/utils.js";
|
|
7
|
+
import { t } from "../../locales/index.js";
|
|
7
8
|
function showConfirm(options) {
|
|
8
9
|
const opts = 'string' == typeof options ? {
|
|
9
10
|
message: options
|
|
10
11
|
} : options;
|
|
11
|
-
const { title
|
|
12
|
+
const { title, message, confirmText, cancelText, variant = 'default' } = opts;
|
|
13
|
+
const defaultTitle = t('confirm.title');
|
|
14
|
+
const defaultConfirmText = t('confirm.confirmText');
|
|
15
|
+
const defaultCancelText = t('confirm.cancelText');
|
|
12
16
|
return new Promise((resolve)=>{
|
|
13
17
|
const container = document.createElement('div');
|
|
14
18
|
document.body.appendChild(container);
|
|
@@ -26,10 +30,10 @@ function showConfirm(options) {
|
|
|
26
30
|
resolve(false);
|
|
27
31
|
}
|
|
28
32
|
root.render(/*#__PURE__*/ jsx(ConfirmDialog, {
|
|
29
|
-
title: title,
|
|
33
|
+
title: title ?? defaultTitle,
|
|
30
34
|
message: message,
|
|
31
|
-
confirmText: confirmText,
|
|
32
|
-
cancelText: cancelText,
|
|
35
|
+
confirmText: confirmText ?? defaultConfirmText,
|
|
36
|
+
cancelText: cancelText ?? defaultCancelText,
|
|
33
37
|
variant: variant,
|
|
34
38
|
onConfirm: handleConfirm,
|
|
35
39
|
onCancel: handleCancel
|
package/lib/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { getAppId } from "./utils/getAppId.js";
|
|
|
4
4
|
import { isNewPathEnabled } from "./utils/apiPath.js";
|
|
5
5
|
import { logger } from "./logger/index.js";
|
|
6
6
|
import { showToast } from "./components/ui/toast.js";
|
|
7
|
+
import { t } from "./locales/index.js";
|
|
7
8
|
import { version } from "../package.json";
|
|
8
9
|
import { showConfirm } from "./components/ui/confirm.js";
|
|
9
10
|
const _appId = getAppId();
|
|
@@ -20,7 +21,7 @@ const capabilityClient = createClient({
|
|
|
20
21
|
},
|
|
21
22
|
logger: logger,
|
|
22
23
|
onRateLimitError: ()=>{
|
|
23
|
-
showToast('
|
|
24
|
+
showToast(t('index.rateLimitError'));
|
|
24
25
|
}
|
|
25
26
|
});
|
|
26
27
|
const src = {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
declare const createDataLoomClient: (url?: string, pat?: string) => import("@
|
|
1
|
+
declare const createDataLoomClient: (url?: string, pat?: string) => import("@data-loom/js").DataloomClient<any, "public", any>;
|
|
2
2
|
/** 获取dataloom实例 */
|
|
3
3
|
export declare function getDataloom(): Promise<ReturnType<typeof createDataLoomClient>>;
|
|
4
4
|
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { splitWorkspaceUrl } from "../utils/url.js";
|
|
2
|
-
import { createClient } from "@
|
|
2
|
+
import { createClient } from "@data-loom/js";
|
|
3
3
|
import { getAppId } from "../utils/getAppId.js";
|
|
4
|
-
import {
|
|
4
|
+
import { getAppPublished } from "../utils/getInitialInfo.js";
|
|
5
5
|
const createDataLoomClient = (url, pat)=>{
|
|
6
6
|
const { baseUrl, workspace } = url ? splitWorkspaceUrl(url) : {
|
|
7
7
|
baseUrl: '',
|
|
@@ -13,7 +13,12 @@ const createDataLoomClient = (url, pat)=>{
|
|
|
13
13
|
enableDataloomLog: 'production' !== process.env.NODE_ENV,
|
|
14
14
|
requestRateLimit: 'production' !== process.env.NODE_ENV ? 100 : void 0,
|
|
15
15
|
brandName: 'miaoda',
|
|
16
|
-
appId
|
|
16
|
+
appId,
|
|
17
|
+
onError: (error, dataloomInstance)=>{
|
|
18
|
+
if (error?.status === 401) {
|
|
19
|
+
if (error?.code === 'k_ident_013001') dataloomInstance.service.session.redirectToLogin();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
17
22
|
}
|
|
18
23
|
});
|
|
19
24
|
};
|
|
@@ -22,7 +27,7 @@ let pendingPromise = null;
|
|
|
22
27
|
function getDataloom() {
|
|
23
28
|
if (dataloom) return Promise.resolve(dataloom);
|
|
24
29
|
if (pendingPromise) return pendingPromise;
|
|
25
|
-
pendingPromise =
|
|
30
|
+
pendingPromise = getAppPublished().then((info)=>{
|
|
26
31
|
const DATALOOM_CLIENT_URL = info?.app_runtime_extra?.url;
|
|
27
32
|
const DATALOOM_PAT = info?.app_runtime_extra?.token;
|
|
28
33
|
dataloom = createDataLoomClient(DATALOOM_CLIENT_URL, DATALOOM_PAT);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 获取当前语言环境下的翻译文案。
|
|
3
|
+
*
|
|
4
|
+
* 支持 `{key}` 形式的变量替换:
|
|
5
|
+
* ```ts
|
|
6
|
+
* t('safety.tenant.operated', { name: '字节' })
|
|
7
|
+
* // zh → "字节运营"
|
|
8
|
+
* // en → "Operated by 字节"
|
|
9
|
+
* ```
|
|
10
|
+
*
|
|
11
|
+
* 如果 key 不存在,fallback 到中文;中文也没有则返回 key 本身。
|
|
12
|
+
*/
|
|
13
|
+
export declare function t(key: string, params?: Record<string, string>): string;
|
|
14
|
+
export { getLocale } from '../utils/locale';
|
|
15
|
+
export type { Locale } from '../utils/locale';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import messages from "./messages.js";
|
|
2
|
+
import { getLocale } from "../utils/locale.js";
|
|
3
|
+
function t(key, params) {
|
|
4
|
+
const locale = getLocale();
|
|
5
|
+
const entry = messages[key];
|
|
6
|
+
let text = entry?.[locale] ?? entry?.zh ?? key;
|
|
7
|
+
if (params) for (const [k, v] of Object.entries(params))text = text.replace(`{${k}}`, v);
|
|
8
|
+
return text;
|
|
9
|
+
}
|
|
10
|
+
export { getLocale, t };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 中英文文案源(统一管理)。
|
|
3
|
+
*
|
|
4
|
+
* 每条文案同时包含 zh 和 en,方便对照和维护。
|
|
5
|
+
* Key 规则:`{组件}.{区域}.{标识}`
|
|
6
|
+
* 文案来源:https://bytedance.larkoffice.com/wiki/BJCQwv9auiP9erkudMfcVIIGnRg
|
|
7
|
+
*/
|
|
8
|
+
declare const messages: Record<string, {
|
|
9
|
+
zh: string;
|
|
10
|
+
en: string;
|
|
11
|
+
}>;
|
|
12
|
+
export default messages;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
const messages_messages = {
|
|
2
|
+
'safety.badge.label': {
|
|
3
|
+
zh: '妙搭',
|
|
4
|
+
en: 'Spark'
|
|
5
|
+
},
|
|
6
|
+
'safety.badge.builtWith': {
|
|
7
|
+
zh: '由妙搭搭建',
|
|
8
|
+
en: 'Built with Spark'
|
|
9
|
+
},
|
|
10
|
+
'safety.ai.disclaimer': {
|
|
11
|
+
zh: '包含 AI 生成内容,请注意甄别',
|
|
12
|
+
en: 'AI-generated content. Use with care.'
|
|
13
|
+
},
|
|
14
|
+
'safety.tenant.operated': {
|
|
15
|
+
zh: '{name}运营',
|
|
16
|
+
en: 'Operated by {name}'
|
|
17
|
+
},
|
|
18
|
+
'safety.button.dontShowAgain': {
|
|
19
|
+
zh: '不再展示',
|
|
20
|
+
en: "Don't show again"
|
|
21
|
+
},
|
|
22
|
+
'safety.button.learnMore': {
|
|
23
|
+
zh: '了解更多',
|
|
24
|
+
en: 'Learn more'
|
|
25
|
+
},
|
|
26
|
+
'safety.cover.pc': {
|
|
27
|
+
zh: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/LMfspH/ljhwZthlaukjlkulzlp/logo/miaodacover.png',
|
|
28
|
+
en: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/LMfspH/ljhwZthlaukjlkulzlp/logo/miaodacover-weben.png'
|
|
29
|
+
},
|
|
30
|
+
'safety.cover.mobile': {
|
|
31
|
+
zh: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/LMfspH/ljhwZthlaukjlkulzlp/logo/miaodacover-mobile.png',
|
|
32
|
+
en: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/LMfspH/ljhwZthlaukjlkulzlp/logo/miaodacover-mobileen.png'
|
|
33
|
+
},
|
|
34
|
+
'index.rateLimitError': {
|
|
35
|
+
zh: '应用额度已耗尽,请联系应用开发者',
|
|
36
|
+
en: 'Application quota exhausted, please contact the app developer'
|
|
37
|
+
},
|
|
38
|
+
'theme.themeColors.title': {
|
|
39
|
+
zh: '主题色',
|
|
40
|
+
en: 'Theme Color'
|
|
41
|
+
},
|
|
42
|
+
'theme.themeRadius.title': {
|
|
43
|
+
zh: '圆角',
|
|
44
|
+
en: 'Border Radius'
|
|
45
|
+
},
|
|
46
|
+
'theme.themeSpaces.title': {
|
|
47
|
+
zh: '间距',
|
|
48
|
+
en: 'Spacing'
|
|
49
|
+
},
|
|
50
|
+
'errorRender.title': {
|
|
51
|
+
zh: '页面出错了',
|
|
52
|
+
en: 'Something went wrong'
|
|
53
|
+
},
|
|
54
|
+
'notFound.pageNotExist': {
|
|
55
|
+
zh: '页面不存在',
|
|
56
|
+
en: 'Page not found'
|
|
57
|
+
},
|
|
58
|
+
'notFound.logger.pageNotExist': {
|
|
59
|
+
zh: '页面 {path} 不存在',
|
|
60
|
+
en: 'Page {path} does not exist'
|
|
61
|
+
},
|
|
62
|
+
'notFound.logger.repairMessage': {
|
|
63
|
+
zh: '页面 {path} 不存在,帮我创建对应页面,并配置到"client/src/app.tsx"路由表中',
|
|
64
|
+
en: 'Page {path} does not exist, help me create the corresponding page and configure it in the "client/src/app.tsx" routing table'
|
|
65
|
+
},
|
|
66
|
+
'pagePlaceholder.title': {
|
|
67
|
+
zh: '页面待开发',
|
|
68
|
+
en: 'Page coming soon'
|
|
69
|
+
},
|
|
70
|
+
"pagePlaceholder.description": {
|
|
71
|
+
zh: '页面暂未开发,请耐心等待...',
|
|
72
|
+
en: 'This page is under development, please wait...'
|
|
73
|
+
},
|
|
74
|
+
'confirm.title': {
|
|
75
|
+
zh: '提示',
|
|
76
|
+
en: 'Confirm'
|
|
77
|
+
},
|
|
78
|
+
'confirm.confirmText': {
|
|
79
|
+
zh: '确认',
|
|
80
|
+
en: 'Confirm'
|
|
81
|
+
},
|
|
82
|
+
'confirm.cancelText': {
|
|
83
|
+
zh: '取消',
|
|
84
|
+
en: 'Cancel'
|
|
85
|
+
},
|
|
86
|
+
'userSelect.placeholder': {
|
|
87
|
+
zh: '请选择用户',
|
|
88
|
+
en: 'Select user'
|
|
89
|
+
},
|
|
90
|
+
'userProfile.status.inactive': {
|
|
91
|
+
zh: '暂停使用',
|
|
92
|
+
en: 'Inactive'
|
|
93
|
+
},
|
|
94
|
+
'userProfile.button.message': {
|
|
95
|
+
zh: '消息',
|
|
96
|
+
en: 'Message'
|
|
97
|
+
},
|
|
98
|
+
'userProfile.label.department': {
|
|
99
|
+
zh: '部门',
|
|
100
|
+
en: 'Department'
|
|
101
|
+
},
|
|
102
|
+
'userProfile.label.email': {
|
|
103
|
+
zh: '邮箱',
|
|
104
|
+
en: 'Email'
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
const messages = messages_messages;
|
|
108
|
+
export { messages as default };
|
|
@@ -13,6 +13,13 @@ interface AppRuntimePublished {
|
|
|
13
13
|
interface BucketConfig {
|
|
14
14
|
default_bucket_id?: string;
|
|
15
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* 从API获取已发布的应用信息(仅全栈沙箱模式下使用)
|
|
18
|
+
*/
|
|
19
|
+
export declare function getAppPublished(): Promise<{
|
|
20
|
+
app_info?: AppRuntimePublished;
|
|
21
|
+
app_runtime_extra?: AppRuntimeExtra;
|
|
22
|
+
} | undefined>;
|
|
16
23
|
declare let initialInfo: {
|
|
17
24
|
app_info?: AppRuntimePublished;
|
|
18
25
|
app_runtime_extra?: AppRuntimeExtra;
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { getAppId } from "./getAppId.js";
|
|
2
2
|
import { getCsrfToken } from "./getCsrfToken.js";
|
|
3
3
|
import { isNewPathEnabled } from "./apiPath.js";
|
|
4
|
+
function syncBucketIdToWindow(info) {
|
|
5
|
+
if (!info) return;
|
|
6
|
+
const bucketId = info.app_runtime_extra?.bucket?.default_bucket_id;
|
|
7
|
+
if (bucketId) window._bucket_id = bucketId;
|
|
8
|
+
}
|
|
4
9
|
async function getAppPublished() {
|
|
5
10
|
try {
|
|
6
11
|
const headers = {
|
|
@@ -18,24 +23,40 @@ async function getAppPublished() {
|
|
|
18
23
|
credentials: 'include'
|
|
19
24
|
});
|
|
20
25
|
const res = await response.json();
|
|
21
|
-
if (0 === res.code || '0' === res.status_code)
|
|
26
|
+
if (0 === res.code || '0' === res.status_code) {
|
|
27
|
+
syncBucketIdToWindow(res.data);
|
|
28
|
+
return res.data;
|
|
29
|
+
}
|
|
22
30
|
console.error('Error fetching published app info:', res);
|
|
23
31
|
} catch (error) {
|
|
24
32
|
console.error('Error fetching published app info:', error);
|
|
25
33
|
}
|
|
26
34
|
}
|
|
35
|
+
function getPreloadedInfo() {
|
|
36
|
+
try {
|
|
37
|
+
const platformData = window.__platform__?.appPublished;
|
|
38
|
+
if (platformData && 'object' == typeof platformData) return platformData;
|
|
39
|
+
} catch {}
|
|
40
|
+
}
|
|
27
41
|
let initialInfo;
|
|
28
42
|
let pendingPromise = null;
|
|
29
43
|
function getInitialInfo(refresh = false) {
|
|
30
44
|
if (initialInfo && !refresh) return Promise.resolve(initialInfo);
|
|
45
|
+
if (!initialInfo && !refresh) {
|
|
46
|
+
const preloaded = getPreloadedInfo();
|
|
47
|
+
if (preloaded) {
|
|
48
|
+
initialInfo = preloaded;
|
|
49
|
+
syncBucketIdToWindow(initialInfo);
|
|
50
|
+
return Promise.resolve(initialInfo);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
31
53
|
if (pendingPromise && !refresh) return pendingPromise;
|
|
32
54
|
pendingPromise = getAppPublished().then((info)=>{
|
|
33
55
|
initialInfo = info;
|
|
34
|
-
if (initialInfo) window._bucket_id = initialInfo.app_runtime_extra?.bucket?.default_bucket_id;
|
|
35
56
|
return initialInfo;
|
|
36
57
|
}).finally(()=>{
|
|
37
58
|
pendingPromise = null;
|
|
38
59
|
});
|
|
39
60
|
return pendingPromise;
|
|
40
61
|
}
|
|
41
|
-
export { getInitialInfo };
|
|
62
|
+
export { getAppPublished, getInitialInfo };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 统一的语言判断方法。
|
|
3
|
+
*
|
|
4
|
+
* 生产环境(`process.env.NODE_ENV === 'production'`):
|
|
5
|
+
* 只看浏览器容器语言(navigator.language)。
|
|
6
|
+
*
|
|
7
|
+
* 非生产环境(沙箱预览场景):
|
|
8
|
+
* 优先级 query > localStorage > navigator。
|
|
9
|
+
* URL query `locale`(值为 `zh_CN` / `en_US`)由宿主平台(miaoda)
|
|
10
|
+
* 通过 iframe src 透传,确保产物内的语言与宿主一致;
|
|
11
|
+
* 读到 query 时会同步写入 localStorage,便于后续无 query 的跳转/刷新场景使用。
|
|
12
|
+
*
|
|
13
|
+
* 返回标准化 locale code:'zh' | 'en'。
|
|
14
|
+
* 目前只区分中英文,未来扩展时在此调整。
|
|
15
|
+
*/
|
|
16
|
+
export type Locale = 'zh' | 'en';
|
|
17
|
+
export declare function getLocale(): Locale;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const STORAGE_KEY = 'miaoda:preview-locale';
|
|
2
|
+
function toLocale(value) {
|
|
3
|
+
const lang = value ?? 'zh';
|
|
4
|
+
return lang.toLowerCase().startsWith('zh') ? 'zh' : 'en';
|
|
5
|
+
}
|
|
6
|
+
function readLocaleFromQuery() {
|
|
7
|
+
if ('undefined' == typeof window) return null;
|
|
8
|
+
try {
|
|
9
|
+
const value = new URLSearchParams(window.location.search).get('locale');
|
|
10
|
+
return value && value.length > 0 ? value : null;
|
|
11
|
+
} catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function readLocaleFromStorage() {
|
|
16
|
+
if ('undefined' == typeof window) return null;
|
|
17
|
+
try {
|
|
18
|
+
const value = window.localStorage?.getItem(STORAGE_KEY);
|
|
19
|
+
return value && value.length > 0 ? value : null;
|
|
20
|
+
} catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function writeLocaleToStorage(value) {
|
|
25
|
+
if ('undefined' == typeof window) return;
|
|
26
|
+
try {
|
|
27
|
+
window.localStorage?.setItem(STORAGE_KEY, value);
|
|
28
|
+
} catch {}
|
|
29
|
+
}
|
|
30
|
+
function getLocale() {
|
|
31
|
+
if ('production' === process.env.NODE_ENV) return toLocale(navigator.language);
|
|
32
|
+
const fromQuery = readLocaleFromQuery();
|
|
33
|
+
if (fromQuery) {
|
|
34
|
+
writeLocaleToStorage(fromQuery);
|
|
35
|
+
return toLocale(fromQuery);
|
|
36
|
+
}
|
|
37
|
+
const fromStorage = readLocaleFromStorage();
|
|
38
|
+
if (fromStorage) return toLocale(fromStorage);
|
|
39
|
+
return toLocale(navigator.language);
|
|
40
|
+
}
|
|
41
|
+
export { getLocale };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-apaas/client-toolkit",
|
|
3
|
-
"version": "1.2.35-alpha.
|
|
3
|
+
"version": "1.2.35-alpha.3",
|
|
4
4
|
"types": "./lib/index.d.ts",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"files": [
|
|
@@ -98,10 +98,10 @@
|
|
|
98
98
|
"dependencies": {
|
|
99
99
|
"@ant-design/colors": "^7.2.1",
|
|
100
100
|
"@ant-design/cssinjs": "^1.24.0",
|
|
101
|
+
"@data-loom/js": "0.4.13",
|
|
101
102
|
"@lark-apaas/aily-web-sdk": "^0.0.9",
|
|
102
103
|
"@lark-apaas/auth-sdk": "^0.1.4",
|
|
103
104
|
"@lark-apaas/client-capability": "^0.1.6",
|
|
104
|
-
"@lark-apaas/dataloom": "0.1.1-alpha.1",
|
|
105
105
|
"@lark-apaas/internal-slardar": "^0.0.3",
|
|
106
106
|
"@lark-apaas/miaoda-inspector": "^1.0.23",
|
|
107
107
|
"@lark-apaas/observable-web": "^1.0.6",
|