@blocklet/pages-kit-block-studio 0.4.66 → 0.4.68

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.
@@ -1,6 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  /* eslint-disable */
3
3
  // @ts-nocheck
4
+ // NOTICE: This file is very important, please do not change it!
5
+ // import BlockStudio from '@blocklet/pages-kit-block-studio/frontend';
6
+ // export default BlockStudio;
4
7
  import { createAuthServiceSessionContext } from '@arcblock/did-connect/lib/Session';
5
8
  import { LocaleProvider, useLocaleContext } from '@arcblock/ux/lib/Locale/context';
6
9
  import Toast, { ToastProvider } from '@arcblock/ux/lib/Toast';
@@ -20,7 +23,7 @@ import { Alert, Box, Button, CircularProgress, Dialog, DialogContent, DialogTitl
20
23
  import { useDebounceFn, useReactive } from 'ahooks';
21
24
  import cloneDeep from 'lodash/cloneDeep';
22
25
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
23
- import { Suspense, useCallback, useEffect, useMemo } from 'react';
26
+ import { Suspense, useCallback, useContext, useEffect, useMemo } from 'react';
24
27
  import { DndProvider } from 'react-dnd';
25
28
  import { HTML5Backend } from 'react-dnd-html5-backend';
26
29
  import { Navigate, useLocation, useNavigate } from 'react-router-dom';
@@ -35,7 +38,11 @@ const api = createAxios({
35
38
  }, {
36
39
  componentDid: PAGES_KIT_BLOCK_STUDIO_BLOCKLET_DID,
37
40
  });
38
- const { SessionProvider, SessionContext, SessionConsumer, withSession } = createAuthServiceSessionContext();
41
+ const { SessionProvider, SessionContext } = createAuthServiceSessionContext();
42
+ function useSessionContext() {
43
+ const info = useContext(SessionContext);
44
+ return info;
45
+ }
39
46
  const LEFT_DRAWER_WIDTH = 200;
40
47
  const RIGHT_DRAWER_WIDTH = 300;
41
48
  const ComparisonPreviewDialog = ({ open, title, leftTitle, leftContent, rightTitle, rightContent, description = '确认后将更新配置。', loading, onConfirm, onClose, }) => {
@@ -77,7 +84,7 @@ function Layout({ loadState, loadedData }) {
77
84
  updatedAt: '',
78
85
  },
79
86
  data: {},
80
- allBlocks: {},
87
+ allResources: {},
81
88
  searchValue: '',
82
89
  newBlockParams: {
83
90
  name: '',
@@ -94,13 +101,18 @@ function Layout({ loadState, loadedData }) {
94
101
  loading: false,
95
102
  onConfirm: async () => { },
96
103
  },
104
+ init: false,
105
+ allComponents: [],
97
106
  });
98
107
  const { locale } = useLocaleContext();
108
+ const { session } = useSessionContext();
99
109
  const location = useLocation();
100
110
  const navigate = useNavigate();
101
111
  const staticData = useStaticData();
102
112
  const { routes, firstRoute, currentPage } = useMemo(() => {
103
- const routes = Object.keys(staticData).sort((a, b) => a.localeCompare(b));
113
+ const routes = Object.keys(staticData)
114
+ .sort((a, b) => a.localeCompare(b))
115
+ .filter((route) => staticData[route]?.main?.isDeleted !== true);
104
116
  return {
105
117
  routes,
106
118
  firstRoute: routes.find((route) => route !== '404' && route !== '*'),
@@ -119,29 +131,14 @@ function Layout({ loadState, loadedData }) {
119
131
  const response = await api.get(`/api/blocks?path=${encodeURIComponent(metadataPath)}`);
120
132
  return response.data || {};
121
133
  };
122
- const getAllBlocks = async () => {
123
- const response = await api.get('/api/blocks/all');
134
+ const getAllResources = async () => {
135
+ const response = await api.get('/api/blocks/resources');
136
+ return response.data || [];
137
+ };
138
+ const getAllComponents = async () => {
139
+ const response = await api.get('/api/blocks/components');
124
140
  return response.data || [];
125
141
  };
126
- const debouncedUpdateMetadata = useDebounceFn(async (metadata) => {
127
- const { metadataPath } = getStaticData() || {};
128
- if (metadataPath) {
129
- try {
130
- const metdadataWithoutRenderer = cloneDeep(metadata);
131
- delete metdadataWithoutRenderer.renderer;
132
- await api.post('/api/blocks', {
133
- path: metadataPath,
134
- content: metdadataWithoutRenderer,
135
- });
136
- }
137
- catch (error) {
138
- console.error('Failed to write metadata:', error);
139
- }
140
- }
141
- }, { wait: 500 });
142
- useEffect(() => {
143
- debouncedUpdateMetadata.run(state.metadata);
144
- }, [state.metadata, debouncedUpdateMetadata]);
145
142
  useEffect(() => {
146
143
  if (currentPage && state.injectBlocks.length === 0) {
147
144
  state.injectBlocks.push({
@@ -164,19 +161,46 @@ function Layout({ loadState, loadedData }) {
164
161
  type: 'react-component',
165
162
  },
166
163
  };
164
+ state.init = true;
167
165
  }
168
166
  catch (error) {
169
167
  console.error('Failed to fetch metadata:', error);
170
168
  }
171
169
  }
172
170
  };
173
- const fetchAllBlocks = async () => {
174
- const allBlocks = await getAllBlocks();
175
- state.allBlocks = allBlocks;
171
+ const fetchAllResources = async () => {
172
+ const allResources = await getAllResources();
173
+ state.allResources = allResources;
174
+ };
175
+ const fetchAllComponents = async () => {
176
+ const allComponents = await getAllComponents();
177
+ state.allComponents = allComponents;
176
178
  };
177
179
  fetchMetadata();
178
- fetchAllBlocks();
180
+ fetchAllResources();
181
+ fetchAllComponents();
179
182
  }, [location.pathname]); // 当路由变化时重新获取
183
+ const debouncedUpdateMetadata = useDebounceFn(async (metadata) => {
184
+ if (!state.init)
185
+ return;
186
+ const { metadataPath } = getStaticData() || {};
187
+ if (metadataPath) {
188
+ try {
189
+ const metdadataWithoutRenderer = cloneDeep(metadata);
190
+ delete metdadataWithoutRenderer.renderer;
191
+ await api.post('/api/blocks', {
192
+ path: metadataPath,
193
+ content: metdadataWithoutRenderer,
194
+ });
195
+ }
196
+ catch (error) {
197
+ console.error('Failed to write metadata:', error);
198
+ }
199
+ }
200
+ }, { wait: 500 });
201
+ useEffect(() => {
202
+ debouncedUpdateMetadata.run(state.metadata);
203
+ }, [state.metadata, debouncedUpdateMetadata]);
180
204
  const mergedAllBlocks = useMemo(() => {
181
205
  // Add all staticData code and importPath mappings
182
206
  if (import.meta.env.DEV) {
@@ -194,13 +218,13 @@ function Layout({ loadState, loadedData }) {
194
218
  };
195
219
  }
196
220
  return {
197
- ...state.allBlocks,
221
+ ...state.allResources,
198
222
  [state.metadata.id]: {
199
- ...state.allBlocks[state.metadata.id],
223
+ ...state.allResources[state.metadata.id],
200
224
  data: state.metadata,
201
225
  },
202
226
  };
203
- }, [state.allBlocks, state.metadata, staticData]);
227
+ }, [state.allResources, state.metadata, staticData]);
204
228
  const onCloseCreateBlock = () => {
205
229
  state.createBlockOpen = false;
206
230
  state.newBlockParams = {
@@ -229,223 +253,237 @@ function Layout({ loadState, loadedData }) {
229
253
  !location.search.includes('no-redirect=true')) {
230
254
  return _jsx(Navigate, { to: `${firstRoute ?? '/'}`, replace: true });
231
255
  }
232
- return (_jsx(StyledEngineProvider, { injectFirst: true, children: _jsx(ThemeProvider, { theme: theme, children: _jsx(SessionProvider, { serviceHost: basename, protectedRoutes: [`${basename}/*`], children: _jsx(DndProvider, { backend: HTML5Backend, children: _jsxs(StyledDashboard, { HeaderProps: {
233
- // @ts-ignore
234
- homeLink: joinURL(basename),
235
- addons: (addons) => {
236
- return [
237
- _jsx(Button, { onClick: () => (state.createResourceOpen = true), children: "Create Resource" }, "logout"),
238
- ...addons,
239
- ];
240
- },
241
- }, MenusDrawerProps: { sx: { [`.${backdropClasses.root}`]: { top: 64 } } }, children: [_jsxs(Drawer, { variant: "permanent", sx: {
242
- width: LEFT_DRAWER_WIDTH,
243
- flexShrink: 0,
244
- zIndex: 1000,
245
- '& .MuiDrawer-paper': {
246
- width: LEFT_DRAWER_WIDTH,
247
- boxSizing: 'border-box',
248
- position: 'relative',
249
- height: '100%',
250
- },
251
- }, children: [_jsxs(Stack, { gap: 1, direction: "row", alignItems: "center", sx: { py: 2, pr: 1, pl: 0.5 }, children: [_jsx(TextField, { placeholder: "Search Blocks...", sx: { minWidth: 120 }, onChange: (e) => {
252
- state.searchValue = e.target.value;
253
- } }), _jsx(Button, { variant: "contained", sx: { minWidth: 'auto' }, onClick: () => {
254
- state.createBlockOpen = true;
255
- }, children: _jsx(AddIcon, { fontSize: "small" }) })] }), _jsx(List, { sx: { pr: 1, overflowY: 'auto' }, children: routes
256
- .map((route) => {
257
- const routeName = route;
258
- const staticDataInRoute = staticData[route]?.main;
259
- if (state.searchValue && !routeName?.toLowerCase().includes(state.searchValue?.toLowerCase())) {
260
- return null;
261
- }
262
- return (_jsx(ListItem, { disablePadding: true, children: _jsx(ListItemButton, { selected: currentPage.pageId === route, onClick: () => {
263
- navigate(route);
264
- }, sx: {
265
- borderRadius: 1,
266
- mb: 1,
267
- width: '100%',
268
- textOverflow: 'ellipsis',
269
- whiteSpace: 'nowrap',
270
- overflowX: 'hidden',
271
- transition: 'all 0.3s ease',
272
- '&.Mui-selected': {
273
- backgroundColor: 'primary.main',
274
- color: 'white',
275
- '&:hover': {
276
- backgroundColor: 'primary.main',
277
- },
278
- },
279
- fontSize: '14px',
280
- }, children: _jsx(Tooltip, { title: staticDataInRoute.blockName || routeName, children: _jsx("div", { style: {
281
- width: '100%',
282
- overflow: 'hidden',
283
- textOverflow: 'ellipsis',
284
- }, children: staticDataInRoute.blockName || routeName }) }) }) }, route));
285
- })
286
- .filter(Boolean) })] }), _jsx(Box, { sx: { flex: 1, overflowX: 'hidden', overflowY: getStaticData()?.isHtml ? 'hidden' : 'auto' }, children: _jsx(ThemeProvider, { theme: pagesTheme, children: _jsx(Suspense, { children: getRenderContent() }) }) }), _jsx(Drawer, { variant: "permanent", anchor: "right", sx: {
287
- width: RIGHT_DRAWER_WIDTH,
288
- flexShrink: 0,
289
- zIndex: 1000,
290
- '& .MuiDrawer-paper': {
291
- width: RIGHT_DRAWER_WIDTH,
292
- boxSizing: 'border-box',
293
- position: 'relative',
294
- height: '100%',
295
- },
296
- }, children: _jsxs(List, { sx: { display: 'flex', flexDirection: 'column', gap: 1 }, children: [_jsx(ListItem, { children: _jsx(Box, { sx: { width: '100%' }, children: _jsx(BasicInfo, { config: state.metadata }) }) }), _jsx(ListItem, { children: _jsxs(Box, { sx: { width: '100%' }, children: [_jsx(PropertiesConfig, { config: state.metadata, currentLocale: locale, defaultLocale: "en", allComponents: mergedAllBlocks, onUpdateConfig: (updater) => {
297
- updater(state.metadata);
298
- }, useI18nEditor: false }), _jsxs(Stack, { direction: "column", spacing: 1, sx: { mt: 1 }, children: [_jsx(Button, { variant: "contained", size: "small", color: "primary", onClick: async () => {
299
- try {
300
- const { dataPath } = getStaticData() || {};
301
- if (!dataPath) {
302
- Toast.error('无法找到组件路径');
303
- return;
304
- }
305
- Toast.info('正在分析组件接口...');
306
- const response = await api.post('/api/blocks/interface-to-properties', {
307
- componentPath: dataPath,
308
- });
309
- if (response.data.success) {
310
- const { currentProperties, newProperties } = response.data;
311
- state.previewDialog = {
312
- open: true,
313
- title: 'Interface → Properties 预览',
314
- leftTitle: '当前 Properties',
315
- leftContent: JSON.stringify(currentProperties, null, 2),
316
- rightTitle: '新 Properties',
317
- rightContent: JSON.stringify(newProperties, null, 2),
318
- description: '确认后将更新metadata文件。这将保留现有配置值,但可能更改属性结构。',
319
- loading: false,
320
- onConfirm: async () => {
321
- state.previewDialog.loading = true;
322
- try {
323
- const updateResponse = await api.post('/api/blocks/interface-to-properties', {
324
- componentPath: dataPath,
325
- write: true,
326
- });
327
- if (updateResponse.data.success) {
328
- Toast.success('Metadata生成成功!');
329
- // 更新当前的metadata状态
330
- state.metadata = {
331
- ...updateResponse.data.metadata,
332
- renderer: state.metadata.renderer,
333
- };
334
- state.previewDialog.open = false;
335
- }
336
- else {
337
- throw new Error(updateResponse.data.error || '生成失败');
338
- }
339
- }
340
- finally {
341
- state.previewDialog.loading = false;
342
- }
343
- },
344
- };
345
- }
346
- else {
347
- Toast.error(response.data.error || '预览失败');
348
- }
349
- }
350
- catch (error) {
351
- console.error('生成预览失败:', error);
352
- Toast.error('生成预览失败');
353
- }
354
- }, children: "Interface \u2192 Properties" }), _jsx(Button, { variant: "outlined", size: "small", color: "primary", onClick: async () => {
355
- try {
356
- const { dataPath } = getStaticData() || {};
357
- if (!dataPath) {
358
- Toast.error('无法找到组件路径');
359
- return;
360
- }
361
- Toast.info('正在生成TypeScript接口预览...');
362
- const response = await api.post('/api/blocks/properties-to-interface', {
363
- componentPath: dataPath,
364
- });
365
- if (response.data.success) {
366
- const { currentInterface, newInterface } = response.data;
367
- state.previewDialog = {
368
- open: true,
369
- title: 'Properties → Interface 预览',
370
- leftTitle: '当前接口',
371
- leftContent: currentInterface,
372
- rightTitle: '新接口',
373
- rightContent: newInterface,
374
- description: '确认后将更新TypeScript接口。这将覆盖当前的接口定义。',
375
- loading: false,
376
- onConfirm: async () => {
377
- state.previewDialog.loading = true;
378
- try {
379
- const updateResponse = await api.post('/api/blocks/properties-to-interface', {
380
- componentPath: dataPath,
381
- write: true,
382
- });
383
- if (updateResponse.data.success) {
384
- Toast.success('TypeScript接口生成成功!');
385
- state.previewDialog.open = false;
386
- }
387
- else {
388
- throw new Error(updateResponse.data.error || '生成失败');
389
- }
390
- }
391
- finally {
392
- state.previewDialog.loading = false;
393
- }
394
- },
395
- };
256
+ return (_jsx(DndProvider, { backend: HTML5Backend, children: _jsxs(StyledDashboard, { HeaderProps: {
257
+ // @ts-ignore
258
+ homeLink: joinURL(basename),
259
+ addons: (addons) => {
260
+ return [
261
+ _jsx(Button, { onClick: async () => {
262
+ if (!session?.user?.did) {
263
+ Toast.warning('请先连接钱包');
264
+ await session.login();
265
+ setTimeout(() => {
266
+ state.createResourceOpen = true;
267
+ }, 1000);
268
+ }
269
+ else {
270
+ state.createResourceOpen = true;
271
+ }
272
+ }, children: "Create Resource" }, "logout"),
273
+ ...addons,
274
+ ];
275
+ },
276
+ }, MenusDrawerProps: { sx: { [`.${backdropClasses.root}`]: { top: 64 } } }, children: [_jsxs(Drawer, { variant: "permanent", sx: {
277
+ width: LEFT_DRAWER_WIDTH,
278
+ flexShrink: 0,
279
+ zIndex: 1000,
280
+ '& .MuiDrawer-paper': {
281
+ width: LEFT_DRAWER_WIDTH,
282
+ boxSizing: 'border-box',
283
+ position: 'relative',
284
+ height: '100%',
285
+ },
286
+ }, children: [_jsxs(Stack, { gap: 1, direction: "row", alignItems: "center", sx: { py: 2, pr: 1, pl: 0.5 }, children: [_jsx(TextField, { placeholder: "Search Blocks...", sx: { minWidth: 120 }, onChange: (e) => {
287
+ state.searchValue = e.target.value;
288
+ } }), _jsx(Button, { variant: "contained", sx: { minWidth: 'auto' }, onClick: () => {
289
+ state.createBlockOpen = true;
290
+ }, children: _jsx(AddIcon, { fontSize: "small" }) })] }), _jsx(List, { sx: { pr: 1, overflowY: 'auto' }, children: routes
291
+ .map((route) => {
292
+ const routeName = route;
293
+ const staticDataInRoute = staticData[route]?.main;
294
+ if (state.searchValue && !routeName?.toLowerCase().includes(state.searchValue?.toLowerCase())) {
295
+ return null;
296
+ }
297
+ if (!state.allComponents?.find(({ blockName }) => `/${blockName}` === routeName)) {
298
+ return null;
299
+ }
300
+ return (_jsx(ListItem, { disablePadding: true, children: _jsx(ListItemButton, { selected: currentPage.pageId === route, onClick: () => {
301
+ navigate(route);
302
+ }, sx: {
303
+ borderRadius: 1,
304
+ mb: 1,
305
+ width: '100%',
306
+ textOverflow: 'ellipsis',
307
+ whiteSpace: 'nowrap',
308
+ overflowX: 'hidden',
309
+ transition: 'all 0.3s ease',
310
+ '&.Mui-selected': {
311
+ backgroundColor: 'primary.main',
312
+ color: 'white',
313
+ '&:hover': {
314
+ backgroundColor: 'primary.main',
315
+ },
316
+ },
317
+ fontSize: '14px',
318
+ }, children: _jsx(Tooltip, { title: staticDataInRoute.blockName || routeName, children: _jsx("div", { style: {
319
+ width: '100%',
320
+ overflow: 'hidden',
321
+ textOverflow: 'ellipsis',
322
+ }, children: staticDataInRoute.blockName || routeName }) }) }) }, route));
323
+ })
324
+ .filter(Boolean) })] }), _jsx(Box, { sx: { flex: 1, overflowX: 'hidden', overflowY: getStaticData()?.isHtml ? 'hidden' : 'auto' }, children: _jsx(ThemeProvider, { theme: pagesTheme, children: _jsx(Suspense, { children: getRenderContent() }) }) }), _jsx(Drawer, { variant: "permanent", anchor: "right", sx: {
325
+ width: RIGHT_DRAWER_WIDTH,
326
+ flexShrink: 0,
327
+ zIndex: 1000,
328
+ '& .MuiDrawer-paper': {
329
+ width: RIGHT_DRAWER_WIDTH,
330
+ boxSizing: 'border-box',
331
+ position: 'relative',
332
+ height: '100%',
333
+ },
334
+ }, children: _jsxs(List, { sx: { display: 'flex', flexDirection: 'column', gap: 1 }, children: [_jsx(ListItem, { children: _jsx(Box, { sx: { width: '100%' }, children: _jsx(BasicInfo, { config: state.metadata }) }) }), _jsx(ListItem, { children: _jsxs(Box, { sx: { width: '100%' }, children: [_jsx(PropertiesConfig, { config: state.metadata, currentLocale: locale, defaultLocale: "en", allComponents: mergedAllBlocks, onUpdateConfig: (updater) => {
335
+ updater(state.metadata);
336
+ }, useI18nEditor: false }), _jsxs(Stack, { direction: "column", spacing: 1, sx: { mt: 1 }, children: [_jsx(Button, { variant: "contained", size: "small", color: "primary", onClick: async () => {
337
+ try {
338
+ const { dataPath } = getStaticData() || {};
339
+ if (!dataPath) {
340
+ Toast.error('无法找到组件路径');
341
+ return;
342
+ }
343
+ Toast.info('正在分析组件接口...');
344
+ const response = await api.post('/api/blocks/interface-to-properties', {
345
+ componentPath: dataPath,
346
+ });
347
+ if (response.data.success) {
348
+ const { currentProperties, newProperties } = response.data;
349
+ state.previewDialog = {
350
+ open: true,
351
+ title: 'Interface → Properties 预览',
352
+ leftTitle: '当前 Properties',
353
+ leftContent: JSON.stringify(currentProperties, null, 2),
354
+ rightTitle: '新 Properties',
355
+ rightContent: JSON.stringify(newProperties, null, 2),
356
+ description: '确认后将更新metadata文件。这将保留现有配置值,但可能更改属性结构。',
357
+ loading: false,
358
+ onConfirm: async () => {
359
+ state.previewDialog.loading = true;
360
+ try {
361
+ const updateResponse = await api.post('/api/blocks/interface-to-properties', {
362
+ componentPath: dataPath,
363
+ write: true,
364
+ });
365
+ if (updateResponse.data.success) {
366
+ Toast.success('Metadata生成成功!');
367
+ // 更新当前的metadata状态
368
+ state.metadata = {
369
+ ...updateResponse.data.metadata,
370
+ renderer: state.metadata.renderer,
371
+ };
372
+ state.previewDialog.open = false;
373
+ }
374
+ else {
375
+ throw new Error(updateResponse.data.error || '生成失败');
376
+ }
396
377
  }
397
- else {
398
- Toast.error(response.data.error || '预览失败');
378
+ finally {
379
+ state.previewDialog.loading = false;
399
380
  }
400
- }
401
- catch (error) {
402
- console.error('生成接口预览失败:', error);
403
- Toast.error('生成接口预览失败');
404
- }
405
- }, children: "Properties \u2192 Interface" })] })] }) }), state.metadata.id && (_jsx(ListItem, { children: _jsx(Box, { sx: { width: '100%' }, children: _jsx(ParametersConfig, { config: state.metadata, allComponents: mergedAllBlocks, defaultLocale: "en", locale: locale,
406
- // 不需要 propertiesValue
407
- // propertiesValue={{}}
408
- onChange: ({ key, value }) => {
409
- state.data = {
410
- ...state.data,
411
- [key]: value.value,
412
- };
413
- }, props: {
414
- ...state.data,
415
- } }) }) }))] }) }), _jsx(CreateResource, { open: state.createResourceOpen, onClose: () => {
416
- state.createResourceOpen = false;
417
- } }), _jsxs(Dialog, { open: state.createBlockOpen, onClose: onCloseCreateBlock, children: [_jsx(DialogTitle, { children: "Create New Block" }), _jsx(DialogContent, { children: _jsxs(Stack, { spacing: 2, sx: { pt: 1, minWidth: 300 }, children: [_jsx(TextField, { autoFocus: true, required: true, label: "Name", fullWidth: true, value: state.newBlockParams.name, onChange: (e) => {
418
- state.newBlockParams.name = e.target.value.replace(/[^a-zA-Z0-9-]/g, '');
419
- } }), _jsx(TextField, { label: "Description", fullWidth: true, multiline: true, rows: 3, value: state.newBlockParams.description, onChange: (e) => {
420
- state.newBlockParams.description = e.target.value;
421
- } }), _jsx(Button, { variant: "contained", fullWidth: true, onClick: async () => {
422
- if (!state.newBlockParams.name) {
423
- Toast.warning('Block name is required');
424
- return;
381
+ },
382
+ };
383
+ }
384
+ else {
385
+ Toast.error(response.data.error || '预览失败');
386
+ }
425
387
  }
426
- if (routes.some((route) => {
427
- const staticDataInRoute = staticData[route]?.main;
428
- return (staticDataInRoute?.blockName?.toLowerCase() === state.newBlockParams.name.toLowerCase());
429
- })) {
430
- Toast.warning('Block name already exists, please change it');
431
- return;
388
+ catch (error) {
389
+ console.error('生成预览失败:', error);
390
+ Toast.error('生成预览失败');
432
391
  }
392
+ }, children: "Interface \u2192 Properties" }), _jsx(Button, { variant: "outlined", size: "small", color: "primary", onClick: async () => {
433
393
  try {
434
- await api.post('/api/blocks/create', state.newBlockParams);
435
- navigate(`/${state.newBlockParams.name}?no-redirect=true`);
436
- // wait for the file to be created
437
- setTimeout(() => {
438
- window.location.reload();
439
- }, 100);
440
- onCloseCreateBlock();
394
+ const { dataPath } = getStaticData() || {};
395
+ if (!dataPath) {
396
+ Toast.error('无法找到组件路径');
397
+ return;
398
+ }
399
+ Toast.info('正在生成TypeScript接口预览...');
400
+ const response = await api.post('/api/blocks/properties-to-interface', {
401
+ componentPath: dataPath,
402
+ });
403
+ if (response.data.success) {
404
+ const { currentInterface, newInterface } = response.data;
405
+ state.previewDialog = {
406
+ open: true,
407
+ title: 'Properties → Interface 预览',
408
+ leftTitle: '当前接口',
409
+ leftContent: currentInterface,
410
+ rightTitle: '新接口',
411
+ rightContent: newInterface,
412
+ description: '确认后将更新TypeScript接口。这将覆盖当前的接口定义。',
413
+ loading: false,
414
+ onConfirm: async () => {
415
+ state.previewDialog.loading = true;
416
+ try {
417
+ const updateResponse = await api.post('/api/blocks/properties-to-interface', {
418
+ componentPath: dataPath,
419
+ write: true,
420
+ });
421
+ if (updateResponse.data.success) {
422
+ Toast.success('TypeScript接口生成成功!');
423
+ state.previewDialog.open = false;
424
+ }
425
+ else {
426
+ throw new Error(updateResponse.data.error || '生成失败');
427
+ }
428
+ }
429
+ finally {
430
+ state.previewDialog.loading = false;
431
+ }
432
+ },
433
+ };
434
+ }
435
+ else {
436
+ Toast.error(response.data.error || '预览失败');
437
+ }
441
438
  }
442
439
  catch (error) {
443
- console.error('Failed to create block:', error);
444
- Toast.error('Failed to create block');
440
+ console.error('生成接口预览失败:', error);
441
+ Toast.error('生成接口预览失败');
445
442
  }
446
- }, children: "Create" })] }) })] }), _jsx(ComparisonPreviewDialog, { open: state.previewDialog.open, title: state.previewDialog.title, leftTitle: state.previewDialog.leftTitle, leftContent: state.previewDialog.leftContent, rightTitle: state.previewDialog.rightTitle, rightContent: state.previewDialog.rightContent, description: state.previewDialog.description, loading: state.previewDialog.loading, onConfirm: state.previewDialog.onConfirm, onClose: () => {
447
- state.previewDialog.open = false;
448
- } })] }) }) }) }) }));
443
+ }, children: "Properties \u2192 Interface" })] })] }) }), state.metadata.id && (_jsx(ListItem, { children: _jsx(Box, { sx: { width: '100%' }, children: _jsx(ParametersConfig, { config: state.metadata, allComponents: mergedAllBlocks, defaultLocale: "en", locale: locale,
444
+ // 不需要 propertiesValue
445
+ // propertiesValue={{}}
446
+ onChange: ({ key, value }) => {
447
+ state.data = {
448
+ ...state.data,
449
+ [key]: value.value,
450
+ };
451
+ }, props: {
452
+ ...state.data,
453
+ } }) }) }))] }) }), _jsx(CreateResource, { open: state.createResourceOpen, onClose: () => {
454
+ state.createResourceOpen = false;
455
+ } }), _jsxs(Dialog, { open: state.createBlockOpen, onClose: onCloseCreateBlock, children: [_jsx(DialogTitle, { children: "Create New Block" }), _jsx(DialogContent, { children: _jsxs(Stack, { spacing: 2, sx: { pt: 1, minWidth: 300 }, children: [_jsx(TextField, { autoFocus: true, required: true, label: "Name", fullWidth: true, value: state.newBlockParams.name, onChange: (e) => {
456
+ state.newBlockParams.name = e.target.value.replace(/[^a-zA-Z0-9-]/g, '');
457
+ } }), _jsx(TextField, { label: "Description", fullWidth: true, multiline: true, rows: 3, value: state.newBlockParams.description, onChange: (e) => {
458
+ state.newBlockParams.description = e.target.value;
459
+ } }), _jsx(Button, { variant: "contained", fullWidth: true, onClick: async () => {
460
+ if (!state.newBlockParams.name) {
461
+ Toast.warning('Block name is required');
462
+ return;
463
+ }
464
+ if (routes.some((route) => {
465
+ const staticDataInRoute = staticData[route]?.main;
466
+ return staticDataInRoute?.blockName?.toLowerCase() === state.newBlockParams.name.toLowerCase();
467
+ })) {
468
+ Toast.warning('Block name already exists, please change it');
469
+ return;
470
+ }
471
+ try {
472
+ await api.post('/api/blocks/create', state.newBlockParams);
473
+ navigate(`/${state.newBlockParams.name}?no-redirect=true`);
474
+ // wait for the file to be created
475
+ setTimeout(() => {
476
+ window.location.reload();
477
+ }, 100);
478
+ onCloseCreateBlock();
479
+ }
480
+ catch (error) {
481
+ console.error('Failed to create block:', error);
482
+ Toast.error('Failed to create block');
483
+ }
484
+ }, children: "Create" })] }) })] }), _jsx(ComparisonPreviewDialog, { open: state.previewDialog.open, title: state.previewDialog.title, leftTitle: state.previewDialog.leftTitle, leftContent: state.previewDialog.leftContent, rightTitle: state.previewDialog.rightTitle, rightContent: state.previewDialog.rightContent, description: state.previewDialog.description, loading: state.previewDialog.loading, onConfirm: state.previewDialog.onConfirm, onClose: () => {
485
+ state.previewDialog.open = false;
486
+ } })] }) }));
449
487
  }
450
488
  const StyledDashboard = styled(Dashboard) `
451
489
  .dashboard-content {
@@ -485,7 +523,7 @@ const StyledDashboard = styled(Dashboard) `
485
523
  }
486
524
  `;
487
525
  function LayoutWrapper({ loadState, loadedData, ...rest }) {
488
- return (_jsx(ToastProvider, { children: _jsx(LocaleProvider, { translations: translations, fallbackLocale: "en", children: _jsx(Layout, { loadState: loadState, loadedData: loadedData }) }) }));
526
+ return (_jsx(StyledEngineProvider, { injectFirst: true, children: _jsx(ThemeProvider, { theme: theme, children: _jsx(ToastProvider, { children: _jsx(LocaleProvider, { translations: translations, fallbackLocale: "en", children: _jsx(SessionProvider, { serviceHost: basename, protectedRoutes: [`${basename}/*`], children: _jsx(Layout, { loadState: loadState, loadedData: loadedData }) }) }) }) }) }));
489
527
  }
490
528
  export default LayoutWrapper;
491
529
  function CreateResource({ open, onClose }) {