chore(hera-core): 迁移 hera-core 的通知、移动端入口到其他插件 (#1571)
Co-authored-by: sealday <sealday@gmail.com> Reviewed-on: daoyoucloud/tachybase#1571
This commit is contained in:
parent
f411efb1bc
commit
2425e43621
@ -26,6 +26,8 @@ import {
|
|||||||
} from '../..';
|
} from '../..';
|
||||||
import { Plugin } from '../../application/Plugin';
|
import { Plugin } from '../../application/Plugin';
|
||||||
import { VariablesProvider } from '../../variables';
|
import { VariablesProvider } from '../../variables';
|
||||||
|
import { PageTab } from '../page-style/PageTab';
|
||||||
|
import { usePageStyle } from '../page-style/usePageStyle';
|
||||||
import { NoticeArea } from './NoticeArea';
|
import { NoticeArea } from './NoticeArea';
|
||||||
|
|
||||||
const useStyles = createStyles(({ css, token }) => {
|
const useStyles = createStyles(({ css, token }) => {
|
||||||
@ -407,6 +409,7 @@ export const InternalAdminLayout = (props: any) => {
|
|||||||
const app = useApp();
|
const app = useApp();
|
||||||
const params = useParams<any>();
|
const params = useParams<any>();
|
||||||
const { styles } = useStyles();
|
const { styles } = useStyles();
|
||||||
|
const pageStyle = usePageStyle();
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<GlobalStyleForAdminLayout />
|
<GlobalStyleForAdminLayout />
|
||||||
@ -447,7 +450,7 @@ export const InternalAdminLayout = (props: any) => {
|
|||||||
{params.name && <Layout.Sider className={styles.sider} theme={'light'} ref={sideMenuRef}></Layout.Sider>}
|
{params.name && <Layout.Sider className={styles.sider} theme={'light'} ref={sideMenuRef}></Layout.Sider>}
|
||||||
<Layout.Content className={styles.main}>
|
<Layout.Content className={styles.main}>
|
||||||
<header className={styles.mainHeader}></header>
|
<header className={styles.mainHeader}></header>
|
||||||
<Outlet />
|
{params.name && pageStyle === 'tab' ? <PageTab /> : <Outlet />}
|
||||||
</Layout.Content>
|
</Layout.Content>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
|
@ -9,7 +9,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { Navigate, useNavigate } from 'react-router-dom';
|
import { Navigate, useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { useAPIClient } from '../api-client';
|
import { useAPIClient } from '../api-client';
|
||||||
import { Application, AttachmentPreviewManager, NoticeLevel, useApp } from '../application';
|
import { Application, NoticeLevel, useApp } from '../application';
|
||||||
import { Plugin } from '../application/Plugin';
|
import { Plugin } from '../application/Plugin';
|
||||||
import { BlockSchemaComponentPlugin } from '../block-provider';
|
import { BlockSchemaComponentPlugin } from '../block-provider';
|
||||||
import { CollectionPlugin } from '../collection-manager';
|
import { CollectionPlugin } from '../collection-manager';
|
||||||
@ -24,15 +24,14 @@ import { ACLPlugin } from './acl';
|
|||||||
import { AdminLayoutPlugin } from './admin-layout';
|
import { AdminLayoutPlugin } from './admin-layout';
|
||||||
import { PluginAssistant } from './assistant';
|
import { PluginAssistant } from './assistant';
|
||||||
import { AttachmentPreviewPlugin } from './attachment-preview';
|
import { AttachmentPreviewPlugin } from './attachment-preview';
|
||||||
import { PluginBuiltInCollections } from './built-in-collections';
|
|
||||||
import { PluginContextMenu } from './context-menu';
|
import { PluginContextMenu } from './context-menu';
|
||||||
import { RemoteDocumentTitlePlugin } from './document-title';
|
import { RemoteDocumentTitlePlugin } from './document-title';
|
||||||
import { LocalePlugin } from './locale/LocalePlugin';
|
import { LocalePlugin } from './locale/LocalePlugin';
|
||||||
|
import { PluginPageStyle } from './page-style';
|
||||||
import { PinnedListPlugin } from './pinned-list';
|
import { PinnedListPlugin } from './pinned-list';
|
||||||
import { PMPlugin } from './pm';
|
import { PMPlugin } from './pm';
|
||||||
import { SystemSettingsPlugin } from './system-settings';
|
import { SystemSettingsPlugin } from './system-settings';
|
||||||
import { UserSettingsPlugin } from './user-settings';
|
import { UserSettingsPlugin } from './user-settings';
|
||||||
import { PluginWebNotification } from './web-notification/PluginWebNotification';
|
|
||||||
|
|
||||||
export { AdminProvider, NoticeArea } from './admin-layout';
|
export { AdminProvider, NoticeArea } from './admin-layout';
|
||||||
export * from './context-menu';
|
export * from './context-menu';
|
||||||
@ -358,5 +357,6 @@ export class BuiltInPlugin extends Plugin {
|
|||||||
await this.app.pm.add(PluginAssistant, { name: 'drag-assistant' });
|
await this.app.pm.add(PluginAssistant, { name: 'drag-assistant' });
|
||||||
await this.app.pm.add(UserSettingsPlugin, { name: 'user-settings' });
|
await this.app.pm.add(UserSettingsPlugin, { name: 'user-settings' });
|
||||||
await this.app.pm.add(AttachmentPreviewPlugin, { name: 'attachment-preview' });
|
await this.app.pm.add(AttachmentPreviewPlugin, { name: 'attachment-preview' });
|
||||||
|
await this.app.pm.add(PluginPageStyle, { name: 'page-style' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import React, { createContext, useEffect, useState } from 'react';
|
import React, { createContext, useEffect, useState } from 'react';
|
||||||
import { useCurrentUserContext, useCurrentUserSettingsMenu } from '@tachybase/client';
|
|
||||||
|
|
||||||
import { TabsProps } from 'antd';
|
import { TabsProps } from 'antd';
|
||||||
|
|
||||||
|
import { useCurrentUserContext, useCurrentUserSettingsMenu } from '../../user';
|
||||||
import { useTabSettings } from './useTabSettings';
|
import { useTabSettings } from './useTabSettings';
|
||||||
|
|
||||||
export interface PageStyleContextValue {
|
export interface PageStyleContextValue {
|
@ -1,9 +1,11 @@
|
|||||||
import React, { useContext, useEffect } from 'react';
|
import React, { useContext, useEffect } from 'react';
|
||||||
import { css, RemoteSchemaComponent, useDocumentTitle } from '@tachybase/client';
|
|
||||||
|
|
||||||
|
import { css } from '@emotion/css';
|
||||||
import { Tabs } from 'antd';
|
import { Tabs } from 'antd';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { RemoteSchemaComponent } from '../../schema-component';
|
||||||
|
import { useDocumentTitle } from '../document-title';
|
||||||
import { PageStyleContext } from './PageStyle.provider';
|
import { PageStyleContext } from './PageStyle.provider';
|
||||||
|
|
||||||
export const PageTab = () => {
|
export const PageTab = () => {
|
@ -1,5 +1,4 @@
|
|||||||
import { Plugin } from '@tachybase/client';
|
import { Plugin } from '../../application/Plugin';
|
||||||
|
|
||||||
import { PageStyleProvider } from './PageStyle.provider';
|
import { PageStyleProvider } from './PageStyle.provider';
|
||||||
|
|
||||||
export class PluginPageStyle extends Plugin {
|
export class PluginPageStyle extends Plugin {
|
@ -1,8 +1,11 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { SelectWithTitle, useAPIClient, useCurrentUserContext } from '@tachybase/client';
|
|
||||||
import { error } from '@tachybase/utils/client';
|
import { error } from '@tachybase/utils/client';
|
||||||
|
|
||||||
import { useTranslation } from '../../locale';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import { useAPIClient } from '../../api-client';
|
||||||
|
import { SelectWithTitle } from '../../common';
|
||||||
|
import { useCurrentUserContext } from '../../user';
|
||||||
|
|
||||||
export const useTabSettings = () => {
|
export const useTabSettings = () => {
|
||||||
return {
|
return {
|
@ -38,8 +38,8 @@ const useStyles = createStyles(({ css }) => {
|
|||||||
|
|
||||||
export const PinnedPluginList = () => {
|
export const PinnedPluginList = () => {
|
||||||
const { allowAll, snippets } = useACLRoleContext();
|
const { allowAll, snippets } = useACLRoleContext();
|
||||||
const getSnippetsAllow = (aclKey) => {
|
const getSnippetsAllow = (aclKey, isPublic = false) => {
|
||||||
return allowAll || snippets?.includes(aclKey);
|
return allowAll || isPublic || snippets?.includes(aclKey);
|
||||||
};
|
};
|
||||||
const { styles } = useStyles();
|
const { styles } = useStyles();
|
||||||
const ctx = useContext(PinnedPluginListContext);
|
const ctx = useContext(PinnedPluginListContext);
|
||||||
@ -48,7 +48,7 @@ export const PinnedPluginList = () => {
|
|||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
{Object.keys(ctx.items)
|
{Object.keys(ctx.items)
|
||||||
.sort((a, b) => ctx.items[a].order - ctx.items[b].order)
|
.sort((a, b) => ctx.items[a].order - ctx.items[b].order)
|
||||||
.filter((key) => getSnippetsAllow(ctx.items[key].snippet))
|
.filter((key) => getSnippetsAllow(ctx.items[key].snippet, ctx.items[key].isPublic))
|
||||||
.map((key) => {
|
.map((key) => {
|
||||||
const Action = get(components, ctx.items[key].component);
|
const Action = get(components, ctx.items[key].component);
|
||||||
return Action ? <Action key={key} /> : null;
|
return Action ? <Action key={key} /> : null;
|
||||||
|
@ -845,5 +845,8 @@
|
|||||||
"Full Screen":"Full Screen",
|
"Full Screen":"Full Screen",
|
||||||
"Designer Mode":"Designer Mode",
|
"Designer Mode":"Designer Mode",
|
||||||
"Embedded page": "Embedded page",
|
"Embedded page": "Embedded page",
|
||||||
|
"Page style": "Page Style",
|
||||||
|
"classical": "classical",
|
||||||
|
"tabs": "tabs",
|
||||||
"There are no full screen blocks available at the current location":"There are no full screen blocks available at the current location"
|
"There are no full screen blocks available at the current location":"There are no full screen blocks available at the current location"
|
||||||
}
|
}
|
||||||
|
@ -961,5 +961,8 @@
|
|||||||
"User settings":"个人设置",
|
"User settings":"个人设置",
|
||||||
"Import collection": "导入数据表",
|
"Import collection": "导入数据表",
|
||||||
"Embedded page": "嵌入页面",
|
"Embedded page": "嵌入页面",
|
||||||
|
"Page style": "页面样式",
|
||||||
|
"classical": "经典",
|
||||||
|
"tabs": "标签",
|
||||||
"Demonstration text": "演示文本"
|
"Demonstration text": "演示文本"
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,5 @@ export * from './custom-components/CustomField';
|
|||||||
export * from './fields/AssociatedField';
|
export * from './fields/AssociatedField';
|
||||||
export * from './fields/CalcResult';
|
export * from './fields/CalcResult';
|
||||||
export * from './fields/Expression';
|
export * from './fields/Expression';
|
||||||
export * from './system/MobileLink';
|
|
||||||
export * from './system/Notifications';
|
|
||||||
export * from './system/OnlineUserProvider';
|
export * from './system/OnlineUserProvider';
|
||||||
export * from './excel-table/ExcelFile';
|
export * from './excel-table/ExcelFile';
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { css } from '@tachybase/client';
|
|
||||||
|
|
||||||
import { MobileOutlined } from '@ant-design/icons';
|
|
||||||
import { Button, Tooltip } from 'antd';
|
|
||||||
|
|
||||||
import { useTranslation } from '../../locale';
|
|
||||||
|
|
||||||
export const MobileLink = () => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={css`
|
|
||||||
.ant-btn {
|
|
||||||
border: 0;
|
|
||||||
height: var(--tb-header-height);
|
|
||||||
width: 46px;
|
|
||||||
border-radius: 0;
|
|
||||||
background: none;
|
|
||||||
color: rgba(255, 255, 255, 0.65) !important;
|
|
||||||
&:hover {
|
|
||||||
background: rgba(255, 255, 255, 0.1) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
style={{ display: 'inline-block' }}
|
|
||||||
>
|
|
||||||
<Tooltip title={t('Mobile UI')} placement="bottom">
|
|
||||||
<Button role="button" href="/mobile">
|
|
||||||
<MobileOutlined />
|
|
||||||
</Button>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,104 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { css, useRequest } from '@tachybase/client';
|
|
||||||
import { dayjs } from '@tachybase/utils/client';
|
|
||||||
|
|
||||||
import { BellOutlined } from '@ant-design/icons';
|
|
||||||
import { Badge, Button, List, Popover } from 'antd';
|
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
|
|
||||||
import { useInitializationLinkKey, useLinkKey } from '../../hooks/useNotifications';
|
|
||||||
|
|
||||||
export const Notifications = () => {
|
|
||||||
// 注册链接关联,链接管理使用tachybase代码方式注册数据表
|
|
||||||
useInitializationLinkKey();
|
|
||||||
const linkDetail = useLinkKey();
|
|
||||||
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const { data, run } = useRequest<{ data: any }>({
|
|
||||||
url: `/system_notifications:get`,
|
|
||||||
});
|
|
||||||
const getNotificationList = data?.data || [];
|
|
||||||
const { run: updateRead } = useRequest<{ data: any }>(
|
|
||||||
{
|
|
||||||
url: `/system_notifications:update`,
|
|
||||||
params: {
|
|
||||||
ids: getNotificationList.map((item) => item.id),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
onSuccess() {
|
|
||||||
run();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
const content = (
|
|
||||||
<List
|
|
||||||
style={{ width: '400px' }}
|
|
||||||
itemLayout="horizontal"
|
|
||||||
dataSource={getNotificationList}
|
|
||||||
footer={
|
|
||||||
<Button.Group style={{ width: '100%' }}>
|
|
||||||
<Button
|
|
||||||
block
|
|
||||||
onClick={() => {
|
|
||||||
updateRead();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
全部已读
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
block
|
|
||||||
onClick={() => {
|
|
||||||
navigate(linkDetail?.link);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
查看更多
|
|
||||||
</Button>
|
|
||||||
</Button.Group>
|
|
||||||
}
|
|
||||||
renderItem={(item: any) => (
|
|
||||||
<List.Item>
|
|
||||||
<List.Item.Meta title={item.title} description={dayjs(item.createdAt).calendar()} />
|
|
||||||
{item.content}
|
|
||||||
</List.Item>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={css`
|
|
||||||
.ant-btn {
|
|
||||||
border: 0;
|
|
||||||
height: var(--tb-header-height);
|
|
||||||
width: 46px;
|
|
||||||
border-radius: 0;
|
|
||||||
background: none;
|
|
||||||
color: rgba(255, 255, 255, 0.65) !important;
|
|
||||||
&:hover {
|
|
||||||
background: rgba(255, 255, 255, 0.1) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
padding-top: 12px;
|
|
||||||
}
|
|
||||||
.ant-badge-count {
|
|
||||||
min-width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
line-height: 10px;
|
|
||||||
font-size: 9px;
|
|
||||||
border-radius: 5px;
|
|
||||||
padding: 0 2px;
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
style={{ display: 'inline-block' }}
|
|
||||||
>
|
|
||||||
<Popover content={content}>
|
|
||||||
<Badge count={getNotificationList.length} size="small" offset={[-15, 15]}>
|
|
||||||
<Button role="button">
|
|
||||||
<BellOutlined />
|
|
||||||
</Button>
|
|
||||||
</Badge>
|
|
||||||
</Popover>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@ -2,7 +2,6 @@ import React, { useEffect } from 'react';
|
|||||||
import { useCurrentUserSettingsMenu } from '@tachybase/client';
|
import { useCurrentUserSettingsMenu } from '@tachybase/client';
|
||||||
|
|
||||||
import { usePluginVersion } from '../../hooks/usePluginVersion';
|
import { usePluginVersion } from '../../hooks/usePluginVersion';
|
||||||
import { useTabSettings } from '../page-style/useTabSettings';
|
|
||||||
|
|
||||||
const useHeraVersion = () => {
|
const useHeraVersion = () => {
|
||||||
const version = usePluginVersion();
|
const version = usePluginVersion();
|
||||||
@ -16,10 +15,9 @@ const useHeraVersion = () => {
|
|||||||
export const HeraVersionProvider = ({ children }) => {
|
export const HeraVersionProvider = ({ children }) => {
|
||||||
const { addMenuItem } = useCurrentUserSettingsMenu();
|
const { addMenuItem } = useCurrentUserSettingsMenu();
|
||||||
const heraVersion = useHeraVersion();
|
const heraVersion = useHeraVersion();
|
||||||
const tabItem = useTabSettings();
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
addMenuItem(heraVersion, { before: 'divider_1' });
|
addMenuItem(heraVersion, { before: 'divider_1' });
|
||||||
}, [addMenuItem, tabItem, heraVersion]);
|
}, [addMenuItem, heraVersion]);
|
||||||
|
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
};
|
};
|
||||||
|
@ -21,7 +21,6 @@ import { EmbedPlugin } from './features/embed';
|
|||||||
import { PluginFieldAppends } from './features/field-appends';
|
import { PluginFieldAppends } from './features/field-appends';
|
||||||
import { PluginHeraVersion } from './features/hera-version';
|
import { PluginHeraVersion } from './features/hera-version';
|
||||||
import { PluginOutbound } from './features/outbound';
|
import { PluginOutbound } from './features/outbound';
|
||||||
import { PluginPageStyle } from './features/page-style';
|
|
||||||
import { PluginPDF } from './features/pdf';
|
import { PluginPDF } from './features/pdf';
|
||||||
import { PluginSheet } from './features/sheet';
|
import { PluginSheet } from './features/sheet';
|
||||||
import { useGetCustomAssociatedComponents } from './hooks/useGetCustomAssociatedComponents';
|
import { useGetCustomAssociatedComponents } from './hooks/useGetCustomAssociatedComponents';
|
||||||
@ -36,7 +35,7 @@ import {
|
|||||||
} from './interfaces';
|
} from './interfaces';
|
||||||
import { TstzrangeFieldInterface } from './interfaces/TstzrangeFieldInterface';
|
import { TstzrangeFieldInterface } from './interfaces/TstzrangeFieldInterface';
|
||||||
import { Locale, tval } from './locale';
|
import { Locale, tval } from './locale';
|
||||||
import { AdminLayout, DetailsPage, PageLayout } from './pages';
|
import { DetailsPage, PageLayout } from './pages';
|
||||||
import { CreateSubmitActionInitializer, SettingBlockInitializer } from './schema-initializer';
|
import { CreateSubmitActionInitializer, SettingBlockInitializer } from './schema-initializer';
|
||||||
import { useCreateActionProps } from './schema-initializer/actions/hooks/useCreateActionProps';
|
import { useCreateActionProps } from './schema-initializer/actions/hooks/useCreateActionProps';
|
||||||
import { EditTitle, IsTablePageSize, PageModeSetting, usePaginationVisible } from './schema-settings';
|
import { EditTitle, IsTablePageSize, PageModeSetting, usePaginationVisible } from './schema-settings';
|
||||||
@ -59,7 +58,6 @@ export class PluginCoreClient extends Plugin {
|
|||||||
await this.app.pm.add(PluginGroupBlock);
|
await this.app.pm.add(PluginGroupBlock);
|
||||||
await this.app.pm.add(EmbedPlugin);
|
await this.app.pm.add(EmbedPlugin);
|
||||||
await this.app.pm.add(DepartmentsPlugin);
|
await this.app.pm.add(DepartmentsPlugin);
|
||||||
await this.app.pm.add(PluginPageStyle);
|
|
||||||
await this.app.pm.add(PluginHeraVersion);
|
await this.app.pm.add(PluginHeraVersion);
|
||||||
await this.app.pm.add(PluginPDF);
|
await this.app.pm.add(PluginPDF);
|
||||||
await this.app.pm.add(PluginOutbound);
|
await this.app.pm.add(PluginOutbound);
|
||||||
@ -115,7 +113,6 @@ export class PluginCoreClient extends Plugin {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.app.addComponents({
|
this.app.addComponents({
|
||||||
AdminLayout,
|
|
||||||
AssociatedField,
|
AssociatedField,
|
||||||
CalcResult,
|
CalcResult,
|
||||||
CodeMirror,
|
CodeMirror,
|
||||||
|
@ -1,384 +0,0 @@
|
|||||||
import React, { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
||||||
import {
|
|
||||||
AdminProvider,
|
|
||||||
css,
|
|
||||||
CurrentUser,
|
|
||||||
findByUid,
|
|
||||||
findMenuItem,
|
|
||||||
NoticeArea,
|
|
||||||
PinnedPluginList,
|
|
||||||
SchemaComponent,
|
|
||||||
useACLRoleContext,
|
|
||||||
useAdminSchemaUid,
|
|
||||||
useApp,
|
|
||||||
useDocumentTitle,
|
|
||||||
useRequest,
|
|
||||||
useSystemSettings,
|
|
||||||
useToken,
|
|
||||||
} from '@tachybase/client';
|
|
||||||
|
|
||||||
import { useSessionStorageState } from 'ahooks';
|
|
||||||
import { App, Layout } from 'antd';
|
|
||||||
import { Link, Outlet, useMatch, useNavigate, useParams } from 'react-router-dom';
|
|
||||||
|
|
||||||
import { MobileLink } from '../components/system/MobileLink';
|
|
||||||
import { Notifications } from '../components/system/Notifications';
|
|
||||||
import { OnlineUserDropdown } from '../components/system/OnlineUserProvider';
|
|
||||||
import { PageTab } from '../features/page-style/PageTab';
|
|
||||||
import { usePageStyle } from '../features/page-style/usePageStyle';
|
|
||||||
|
|
||||||
const filterByACL = (schema, options) => {
|
|
||||||
const { allowAll, allowMenuItemIds = [] } = options;
|
|
||||||
if (allowAll) {
|
|
||||||
return schema;
|
|
||||||
}
|
|
||||||
const filterSchema = (s) => {
|
|
||||||
if (!s) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (const key in s.properties) {
|
|
||||||
if (Object.prototype.hasOwnProperty.call(s.properties, key)) {
|
|
||||||
const element = s.properties[key];
|
|
||||||
if (element['x-uid'] && !allowMenuItemIds.includes(element['x-uid'])) {
|
|
||||||
delete s.properties[key];
|
|
||||||
}
|
|
||||||
if (element['x-uid']) {
|
|
||||||
filterSchema(element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
filterSchema(schema);
|
|
||||||
return schema;
|
|
||||||
};
|
|
||||||
|
|
||||||
const SchemaIdContext = createContext(null);
|
|
||||||
const useMenuProps = () => {
|
|
||||||
const defaultSelectedUid = useContext(SchemaIdContext);
|
|
||||||
return {
|
|
||||||
selectedUid: defaultSelectedUid,
|
|
||||||
defaultSelectedUid,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const MenuEditor = (props) => {
|
|
||||||
const { notification } = App.useApp();
|
|
||||||
const [hasNotice, setHasNotice] = useSessionStorageState('plugin-notice', { defaultValue: false });
|
|
||||||
const { setTitle } = useDocumentTitle();
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const params = useParams<any>();
|
|
||||||
const isMatchAdmin = useMatch('/admin');
|
|
||||||
const isMatchAdminName = useMatch('/admin/:name');
|
|
||||||
const defaultSelectedUid = params.name;
|
|
||||||
const { sideMenuRef } = props;
|
|
||||||
const ctx = useACLRoleContext();
|
|
||||||
const [current, setCurrent] = useState(null);
|
|
||||||
const onSelect = ({ item }) => {
|
|
||||||
const schema = item.props.schema;
|
|
||||||
setTitle(schema.title);
|
|
||||||
setCurrent(schema);
|
|
||||||
navigate(`/admin/${schema['x-uid']}`);
|
|
||||||
};
|
|
||||||
const adminSchemaUid = useAdminSchemaUid();
|
|
||||||
const { data, loading } = useRequest<{
|
|
||||||
data: any;
|
|
||||||
}>(
|
|
||||||
{
|
|
||||||
url: `/uiSchemas:getJsonSchema/${adminSchemaUid}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
refreshDeps: [adminSchemaUid],
|
|
||||||
onSuccess(data) {
|
|
||||||
const schema = filterByACL(data?.data, ctx);
|
|
||||||
// url 为 `/admin` 的情况
|
|
||||||
if (isMatchAdmin) {
|
|
||||||
const s = findMenuItem(schema);
|
|
||||||
if (s) {
|
|
||||||
navigate(`/admin/${s['x-uid']}`);
|
|
||||||
setTitle(s.title);
|
|
||||||
} else {
|
|
||||||
navigate(`/admin/`);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// url 不为 `/admin/xxx` 的情况,不做处理
|
|
||||||
if (!isMatchAdminName) return;
|
|
||||||
|
|
||||||
// url 为 `admin/xxx` 的情况
|
|
||||||
const s = findByUid(schema, defaultSelectedUid);
|
|
||||||
if (s) {
|
|
||||||
setTitle(s.title);
|
|
||||||
} else {
|
|
||||||
const s = findMenuItem(schema);
|
|
||||||
if (s) {
|
|
||||||
navigate(`/admin/${s['x-uid']}`);
|
|
||||||
setTitle(s.title);
|
|
||||||
} else {
|
|
||||||
navigate(`/admin/`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const properties = Object.values(current?.root?.properties || {}).shift()?.['properties'] || data?.data?.properties;
|
|
||||||
if (properties && sideMenuRef.current) {
|
|
||||||
const pageType = Object.values(properties).find(
|
|
||||||
(item) => item['x-uid'] === params.name && item['x-component'] === 'Menu.Item',
|
|
||||||
);
|
|
||||||
if (pageType) {
|
|
||||||
sideMenuRef.current.style.display = 'none';
|
|
||||||
} else {
|
|
||||||
sideMenuRef.current.style.display = 'block';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [data?.data, params.name, sideMenuRef]);
|
|
||||||
|
|
||||||
const schema = useMemo(() => {
|
|
||||||
const s = filterByACL(data?.data, ctx);
|
|
||||||
if (s?.['x-component-props']) {
|
|
||||||
s['x-component-props']['useProps'] = useMenuProps;
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}, [data?.data]);
|
|
||||||
|
|
||||||
useRequest(
|
|
||||||
{
|
|
||||||
url: 'applicationPlugins:list',
|
|
||||||
params: {
|
|
||||||
sort: 'id',
|
|
||||||
paginate: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
onSuccess: ({ data }) => {
|
|
||||||
setHasNotice(true);
|
|
||||||
const errorPlugins = data.filter((item) => !item.isCompatible);
|
|
||||||
if (errorPlugins.length) {
|
|
||||||
notification.error({
|
|
||||||
message: 'Plugin dependencies check failed',
|
|
||||||
description: (
|
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
These plugins failed dependency checks. Please go to the{' '}
|
|
||||||
<Link to="/admin/pm/list/local/">plugin management page</Link> for more details.{' '}
|
|
||||||
</div>
|
|
||||||
<ul>
|
|
||||||
{errorPlugins.map((item) => (
|
|
||||||
<li key={item.id}>
|
|
||||||
{item.displayName} - {item.packageName}
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
manual: true,
|
|
||||||
// ready: !hasNotice,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return void 0;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<SchemaIdContext.Provider value={defaultSelectedUid}>
|
|
||||||
<SchemaComponent memoized scope={{ useMenuProps, onSelect, sideMenuRef, defaultSelectedUid }} schema={schema} />
|
|
||||||
</SchemaIdContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const InternalAdminLayout = () => {
|
|
||||||
const sideMenuRef = useRef<HTMLDivElement>();
|
|
||||||
const result = useSystemSettings();
|
|
||||||
const params = useParams<{ name?: string }>();
|
|
||||||
const app = useApp();
|
|
||||||
const { token } = useToken();
|
|
||||||
const pageStyle = usePageStyle();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Layout>
|
|
||||||
<Layout.Header
|
|
||||||
className={css`
|
|
||||||
.ant-menu.ant-menu-dark .ant-menu-item-selected,
|
|
||||||
.ant-menu-submenu-popup.ant-menu-dark .ant-menu-item-selected,
|
|
||||||
.ant-menu-submenu-horizontal.ant-menu-submenu-selected {
|
|
||||||
background-color: ${token.colorBgHeaderMenuActive};
|
|
||||||
color: ${token.colorTextHeaderMenuActive};
|
|
||||||
}
|
|
||||||
.ant-menu-dark.ant-menu-horizontal > .ant-menu-item:hover {
|
|
||||||
background-color: ${token.colorBgHeaderMenuHover};
|
|
||||||
color: ${token.colorTextHeaderMenuHover};
|
|
||||||
}
|
|
||||||
|
|
||||||
position: fixed;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: var(--tb-header-height);
|
|
||||||
line-height: var(--tb-header-height);
|
|
||||||
padding: 0;
|
|
||||||
z-index: 100;
|
|
||||||
background-color: ${token.colorBgHeader};
|
|
||||||
|
|
||||||
.ant-menu {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-menu-item {
|
|
||||||
color: ${token.colorTextHeaderMenu};
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={css`
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={css`
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
flex: 1 1 auto;
|
|
||||||
display: flex;
|
|
||||||
height: 100%;
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={css`
|
|
||||||
display: inline-flex;
|
|
||||||
flex-shrink: 0;
|
|
||||||
color: #fff;
|
|
||||||
padding: 0;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 12px 0 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
`}
|
|
||||||
onClick={() => {
|
|
||||||
location.href = app.adminUrl;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
className={css`
|
|
||||||
object-fit: contain;
|
|
||||||
height: 28px;
|
|
||||||
`}
|
|
||||||
src={result?.data?.data?.logo?.url}
|
|
||||||
/>
|
|
||||||
<h1
|
|
||||||
className={css`
|
|
||||||
color: #fff;
|
|
||||||
height: 32px;
|
|
||||||
margin: 0 0 0 12px;
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 18px;
|
|
||||||
line-height: 32px;
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
{result?.data?.data?.title}
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className={css`
|
|
||||||
flex: 1 1 auto;
|
|
||||||
width: 0;
|
|
||||||
display: flex;
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
<NoticeArea
|
|
||||||
className={css`
|
|
||||||
flex: 1;
|
|
||||||
`}
|
|
||||||
/>
|
|
||||||
<MenuEditor sideMenuRef={sideMenuRef} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className={css`
|
|
||||||
display: inline-flex;
|
|
||||||
position: relative;
|
|
||||||
flex-shrink: 0;
|
|
||||||
height: 100%;
|
|
||||||
z-index: 10;
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
<PinnedPluginList />
|
|
||||||
<MobileLink />
|
|
||||||
<Notifications />
|
|
||||||
<OnlineUserDropdown />
|
|
||||||
<CurrentUser />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Layout.Header>
|
|
||||||
{params.name && (
|
|
||||||
<Layout.Sider
|
|
||||||
theme={'light'}
|
|
||||||
ref={sideMenuRef}
|
|
||||||
style={{ height: '100vh' }}
|
|
||||||
className={css`
|
|
||||||
.ant-layout-sider-children {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
<header
|
|
||||||
className={css`
|
|
||||||
height: var(--tb-header-height);
|
|
||||||
width: 100%;
|
|
||||||
background: transparent;
|
|
||||||
pointer-events: none;
|
|
||||||
`}
|
|
||||||
></header>
|
|
||||||
</Layout.Sider>
|
|
||||||
)}
|
|
||||||
<Layout.Content
|
|
||||||
className={css`
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
position: relative;
|
|
||||||
overflow-y: auto;
|
|
||||||
height: 100vh;
|
|
||||||
max-height: 100vh;
|
|
||||||
> div {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.ant-layout-footer {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
text-align: center;
|
|
||||||
width: 100%;
|
|
||||||
z-index: 0;
|
|
||||||
padding: 0px 50px;
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
<header
|
|
||||||
className={css`
|
|
||||||
flex-shrink: 0;
|
|
||||||
height: var(--tb-header-height);
|
|
||||||
line-height: var(--tb-header-height);
|
|
||||||
background: transparent;
|
|
||||||
pointer-events: none;
|
|
||||||
`}
|
|
||||||
></header>
|
|
||||||
<>{params.name && pageStyle === 'tab' ? <PageTab /> : <Outlet />}</>
|
|
||||||
</Layout.Content>
|
|
||||||
</Layout>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const AdminLayout = (props) => {
|
|
||||||
const AdminComponent = (
|
|
||||||
<AdminProvider>
|
|
||||||
<InternalAdminLayout {...props} />
|
|
||||||
</AdminProvider>
|
|
||||||
);
|
|
||||||
return AdminComponent;
|
|
||||||
};
|
|
@ -1,4 +1,3 @@
|
|||||||
export * from './AdminLayout';
|
|
||||||
export * from './DetailsPage';
|
export * from './DetailsPage';
|
||||||
export * from '../features/outbound/OutboundPage';
|
export * from '../features/outbound/OutboundPage';
|
||||||
export * from './PageLayout';
|
export * from './PageLayout';
|
||||||
|
@ -3,11 +3,16 @@
|
|||||||
"version": "0.22.4",
|
"version": "0.22.4",
|
||||||
"main": "dist/server/index.js",
|
"main": "dist/server/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"antd": "5.19.4"
|
"@ant-design/icons": "~5.3.7",
|
||||||
|
"antd": "5.19.4",
|
||||||
|
"react-i18next": "^14.1.2",
|
||||||
|
"react-router-dom": "~6.25.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tachybase/client": "workspace:*",
|
"@tachybase/client": "workspace:*",
|
||||||
"@tachybase/database": "workspace:*",
|
"@tachybase/database": "workspace:*",
|
||||||
|
"@tachybase/plugin-workflow": "workspace:*",
|
||||||
|
"@tachybase/schema": "workspace:*",
|
||||||
"@tachybase/server": "workspace:*",
|
"@tachybase/server": "workspace:*",
|
||||||
"@tachybase/test": "workspace:*"
|
"@tachybase/test": "workspace:*"
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
createTableBlockSchema,
|
||||||
|
SchemaInitializerItem,
|
||||||
|
useSchemaInitializer,
|
||||||
|
useSchemaInitializerItem,
|
||||||
|
} from '@tachybase/client';
|
||||||
|
import { ISchema } from '@tachybase/schema';
|
||||||
|
|
||||||
|
import { BellOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
|
import { useTranslation } from './locale';
|
||||||
|
|
||||||
|
export const MessageBlockInitializer = () => {
|
||||||
|
const { insert } = useSchemaInitializer();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const itemConfig = useSchemaInitializerItem();
|
||||||
|
|
||||||
|
const schema = createTableBlockSchema({
|
||||||
|
collection: 'messages',
|
||||||
|
rowKey: 'id',
|
||||||
|
tableActionInitializers: 'MessageTable:configureActions',
|
||||||
|
tableColumnInitializers: 'MessageTable:configureColumns',
|
||||||
|
tableActionColumnInitializers: 'MessageTable:configureItemActions',
|
||||||
|
tableBlockProvider: 'MessageBlockProvider',
|
||||||
|
disableTemplate: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SchemaInitializerItem
|
||||||
|
icon={<BellOutlined />}
|
||||||
|
onClick={() => {
|
||||||
|
insert(schema as ISchema);
|
||||||
|
}}
|
||||||
|
title={t('Messages')}
|
||||||
|
{...itemConfig}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { ExtendCollectionsProvider, TableBlockProvider } from '@tachybase/client';
|
||||||
|
|
||||||
|
import { useAuditChangesCollection, useCollectionsCollection, useMessageCollection } from './collections';
|
||||||
|
|
||||||
|
export const MessageBlockProvider = ({ children, ...restProps }) => {
|
||||||
|
const auditChangesCollection = useAuditChangesCollection();
|
||||||
|
const MessageCollection = useMessageCollection();
|
||||||
|
const collectionsCollection = useCollectionsCollection();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ExtendCollectionsProvider collections={[MessageCollection, auditChangesCollection, collectionsCollection]}>
|
||||||
|
<TableBlockProvider name="message" {...restProps}>
|
||||||
|
{children}
|
||||||
|
</TableBlockProvider>
|
||||||
|
</ExtendCollectionsProvider>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,37 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { PinnedPluginListProvider, SchemaComponentOptions, useToken } from '@tachybase/client';
|
||||||
|
|
||||||
|
import { BellOutlined } from '@ant-design/icons';
|
||||||
|
import { Button, Tooltip } from 'antd';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { useTranslation } from './locale';
|
||||||
|
|
||||||
|
const NotificationLink = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { token } = useToken();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<Tooltip title={t('Messages')}>
|
||||||
|
<Button
|
||||||
|
icon={<BellOutlined style={{ color: token.colorTextHeaderMenu }} />}
|
||||||
|
title={t('Messages')}
|
||||||
|
onClick={() => {
|
||||||
|
navigate('/admin/messages');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MessageNotificationProvider = (props) => {
|
||||||
|
return (
|
||||||
|
<PinnedPluginListProvider
|
||||||
|
items={{
|
||||||
|
nl: { order: 220, component: 'NotificationLink', pin: true, isPublic: true },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SchemaComponentOptions components={{ NotificationLink }}>{props.children}</SchemaComponentOptions>
|
||||||
|
</PinnedPluginListProvider>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,86 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Page, SchemaComponent, SchemaComponentContext, SchemaComponentOptions } from '@tachybase/client';
|
||||||
|
|
||||||
|
export const MessagePage = () => {
|
||||||
|
return (
|
||||||
|
<SchemaComponentContext.Provider value={{ designable: false }}>
|
||||||
|
<SchemaComponent
|
||||||
|
schema={{
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Page',
|
||||||
|
properties: {
|
||||||
|
messages: {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'MessageBlockProvider',
|
||||||
|
'x-acl-action': 'messages:list',
|
||||||
|
'x-decorator-props': {
|
||||||
|
collection: 'messages',
|
||||||
|
action: 'list',
|
||||||
|
params: {
|
||||||
|
pageSize: 20,
|
||||||
|
},
|
||||||
|
rowKey: 'id',
|
||||||
|
showIndex: true,
|
||||||
|
dragSort: false,
|
||||||
|
disableTemplate: true,
|
||||||
|
},
|
||||||
|
'x-toolbar': 'BlockSchemaToolbar',
|
||||||
|
'x-settings': 'blockSettings:table',
|
||||||
|
'x-component': 'CardItem',
|
||||||
|
'x-filter-targets': [],
|
||||||
|
properties: {
|
||||||
|
actions: {
|
||||||
|
type: 'void',
|
||||||
|
'x-initializer': 'MessageTable:configureActions',
|
||||||
|
'x-component': 'ActionBar',
|
||||||
|
'x-component-props': {
|
||||||
|
style: {
|
||||||
|
marginBottom: 'var(--tb-spacing)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
smvxfjqmkl7: {
|
||||||
|
type: 'array',
|
||||||
|
'x-initializer': 'MessageTable:configureColumns',
|
||||||
|
'x-component': 'TableV2',
|
||||||
|
'x-use-component-props': 'useTableBlockProps',
|
||||||
|
'x-component-props': {
|
||||||
|
rowKey: 'id',
|
||||||
|
rowSelection: {
|
||||||
|
type: 'checkbox',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
actions: {
|
||||||
|
type: 'void',
|
||||||
|
title: '{{ t("Actions") }}',
|
||||||
|
'x-action-column': 'actions',
|
||||||
|
'x-decorator': 'TableV2.Column.ActionBar',
|
||||||
|
'x-component': 'TableV2.Column',
|
||||||
|
'x-component-props': {
|
||||||
|
width: 150,
|
||||||
|
fixed: 'right',
|
||||||
|
},
|
||||||
|
'x-designer': 'TableV2.ActionColumnDesigner',
|
||||||
|
'x-initializer': 'MessageTable:configureItemActions',
|
||||||
|
properties: {
|
||||||
|
he7o0zao83h: {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'DndContext',
|
||||||
|
'x-component': 'Space',
|
||||||
|
'x-component-props': {
|
||||||
|
split: '|',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
></SchemaComponent>
|
||||||
|
</SchemaComponentContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,24 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { SchemaComponentOptions } from '@tachybase/client';
|
||||||
|
|
||||||
|
import { MessageViewActionInitializer } from './components/MessageViewActionInitializer';
|
||||||
|
import { Message } from './deplicated/Message';
|
||||||
|
import { MessageTableActionColumnInitializer } from './initializers/MessageTableActionColumnInitializer';
|
||||||
|
import { MessageBlockInitializer } from './MessageBlockInitializer';
|
||||||
|
import { MessageBlockProvider } from './MessageBlockProvider';
|
||||||
|
|
||||||
|
export const MessageProvider = (props: any) => {
|
||||||
|
return (
|
||||||
|
<SchemaComponentOptions
|
||||||
|
components={{
|
||||||
|
Message,
|
||||||
|
MessageBlockProvider,
|
||||||
|
MessageBlockInitializer,
|
||||||
|
MessageViewActionInitializer,
|
||||||
|
MessageTableActionColumnInitializer,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</SchemaComponentOptions>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,151 @@
|
|||||||
|
import { tval } from './locale';
|
||||||
|
|
||||||
|
export const useMessageCollection = () => {
|
||||||
|
return {
|
||||||
|
name: 'messages',
|
||||||
|
title: tval('Messages'),
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'createdAt',
|
||||||
|
type: 'date',
|
||||||
|
interface: 'createdAt',
|
||||||
|
uiSchema: {
|
||||||
|
type: 'datetime',
|
||||||
|
title: tval('Created at'),
|
||||||
|
'x-component': 'DatePicker',
|
||||||
|
'x-component-props': {
|
||||||
|
showTime: true,
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
interface: 'input',
|
||||||
|
type: 'string',
|
||||||
|
name: 'title',
|
||||||
|
allowNull: false,
|
||||||
|
unique: false,
|
||||||
|
uiSchema: {
|
||||||
|
type: 'string',
|
||||||
|
title: '{{t("Title")}}',
|
||||||
|
'x-component': 'Input',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
interface: 'input',
|
||||||
|
type: 'string',
|
||||||
|
name: 'content',
|
||||||
|
allowNull: false,
|
||||||
|
unique: false,
|
||||||
|
uiSchema: {
|
||||||
|
type: 'string',
|
||||||
|
title: '{{t("Content")}}',
|
||||||
|
'x-component': 'Input',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'type',
|
||||||
|
type: 'string',
|
||||||
|
interface: 'select',
|
||||||
|
uiSchema: {
|
||||||
|
type: 'string',
|
||||||
|
title: tval('Action type'),
|
||||||
|
'x-component': 'Select',
|
||||||
|
'x-component-props': { ellipsis: true },
|
||||||
|
'x-read-pretty': true,
|
||||||
|
enum: [
|
||||||
|
{ label: tval('Create record'), value: 'create', color: 'lime' },
|
||||||
|
{ label: tval('Update record'), value: 'update', color: 'gold' },
|
||||||
|
{ label: tval('Delete record'), value: 'destroy', color: 'magenta' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'recordId',
|
||||||
|
type: 'string',
|
||||||
|
interface: 'input',
|
||||||
|
uiSchema: {
|
||||||
|
title: tval('Record ID'),
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-component-props': { ellipsis: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
collectionName: 'messages',
|
||||||
|
name: 'collection',
|
||||||
|
type: 'belongsTo',
|
||||||
|
interface: 'm2o',
|
||||||
|
target: 'collections',
|
||||||
|
targetKey: 'name',
|
||||||
|
sourceKey: 'id',
|
||||||
|
foreignKey: 'collectionName',
|
||||||
|
uiSchema: {
|
||||||
|
type: 'object',
|
||||||
|
title: tval('Collection'),
|
||||||
|
'x-component': 'AssociationField',
|
||||||
|
'x-component-props': { fieldNames: { value: 'name', label: 'title' }, ellipsis: true },
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'user',
|
||||||
|
collectionName: 'messages',
|
||||||
|
type: 'belongsTo',
|
||||||
|
interface: 'createdBy',
|
||||||
|
targetKey: 'id',
|
||||||
|
foreignKey: 'createdById',
|
||||||
|
target: 'users',
|
||||||
|
uiSchema: {
|
||||||
|
type: 'object',
|
||||||
|
title: tval('User'),
|
||||||
|
'x-component': 'AssociationField',
|
||||||
|
'x-component-props': { fieldNames: { value: 'id', label: 'nickname' }, ellipsis: true },
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAuditChangesCollection = () => {
|
||||||
|
return {
|
||||||
|
name: 'auditChanges',
|
||||||
|
title: tval('Audit Changes'),
|
||||||
|
fields: [],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useCollectionsCollection = () => {
|
||||||
|
return {
|
||||||
|
name: 'collections',
|
||||||
|
title: tval('Collections'),
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
type: 'string',
|
||||||
|
interface: 'input',
|
||||||
|
uiSchema: {
|
||||||
|
title: tval('Collection name'),
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-component-props': { ellipsis: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
type: 'string',
|
||||||
|
interface: 'input',
|
||||||
|
uiSchema: {
|
||||||
|
title: tval('Collection display name'),
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-component-props': { ellipsis: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
};
|
@ -0,0 +1,300 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { ActionInitializer } from '@tachybase/client';
|
||||||
|
import { uid } from '@tachybase/utils/client';
|
||||||
|
|
||||||
|
import { tval } from '../locale';
|
||||||
|
|
||||||
|
export const MessageViewActionInitializer = () => {
|
||||||
|
const changes = {
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'AssociationField',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-collection-field': 'Message.changes',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
'x-component-props': {
|
||||||
|
multiple: true,
|
||||||
|
fieldNames: {
|
||||||
|
value: 'id',
|
||||||
|
label: 'id',
|
||||||
|
},
|
||||||
|
mode: 'SubTable',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
block: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'AssociationField.SubTable',
|
||||||
|
properties: {
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'TableV2.Column.Decorator',
|
||||||
|
'x-component': 'TableV2.Column',
|
||||||
|
properties: {},
|
||||||
|
},
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'TableV2.Column.Decorator',
|
||||||
|
'x-component': 'TableV2.Column',
|
||||||
|
properties: {},
|
||||||
|
},
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'TableV2.Column.Decorator',
|
||||||
|
'x-component': 'TableV2.Column',
|
||||||
|
properties: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const schema = {
|
||||||
|
type: 'void',
|
||||||
|
title: tval('View'),
|
||||||
|
'x-action': 'view',
|
||||||
|
'x-toolbar': 'ActionSchemaToolbar',
|
||||||
|
'x-settings': 'actionSettings:view',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
openMode: 'drawer',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
drawer: {
|
||||||
|
type: 'void',
|
||||||
|
title: tval('View record'),
|
||||||
|
'x-component': 'Action.Container',
|
||||||
|
'x-component-props': {
|
||||||
|
className: 'nb-action-popup',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
tabs: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Tabs',
|
||||||
|
'x-component-props': {},
|
||||||
|
properties: {
|
||||||
|
tab1: {
|
||||||
|
type: 'void',
|
||||||
|
title: tval('Details'),
|
||||||
|
'x-component': 'Tabs.TabPane',
|
||||||
|
'x-designer': 'Tabs.Designer',
|
||||||
|
'x-component-props': {},
|
||||||
|
properties: {
|
||||||
|
grid: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
properties: {
|
||||||
|
'7ypqrxaqysp': {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Row',
|
||||||
|
properties: {
|
||||||
|
'1fc2l8dwe7m': {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Col',
|
||||||
|
properties: {
|
||||||
|
'5ley6xifrsb': {
|
||||||
|
type: 'void',
|
||||||
|
'x-acl-action': 'Message:get',
|
||||||
|
'x-decorator': 'FormBlockProvider',
|
||||||
|
'x-decorator-props': {
|
||||||
|
resource: 'messages',
|
||||||
|
collection: 'messages',
|
||||||
|
readPretty: true,
|
||||||
|
action: 'get',
|
||||||
|
params: {
|
||||||
|
appends: ['collection', 'user', 'changes'],
|
||||||
|
},
|
||||||
|
useParams: '{{ useParamsFromRecord }}',
|
||||||
|
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
||||||
|
},
|
||||||
|
'x-component': 'CardItem',
|
||||||
|
properties: {
|
||||||
|
bv710pbf9w6: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'FormV2',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
'x-component-props': {
|
||||||
|
useProps: '{{ useFormBlockProps }}',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
grid: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
properties: {
|
||||||
|
g4c24abnbd9: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Row',
|
||||||
|
properties: {
|
||||||
|
fkt9dj5lu1k: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Col',
|
||||||
|
properties: {
|
||||||
|
createdAt: {
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-collection-field': 'Message.createdAt',
|
||||||
|
'x-component-props': {},
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
l267bkv423v: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Row',
|
||||||
|
properties: {
|
||||||
|
tehaepm5xqy: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Col',
|
||||||
|
properties: {
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-collection-field': 'Message.type',
|
||||||
|
'x-component-props': {},
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
zhmn2tkzdh2: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Row',
|
||||||
|
properties: {
|
||||||
|
yuwyej17i64: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Col',
|
||||||
|
properties: {
|
||||||
|
recordId: {
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-collection-field': 'Message.recordId',
|
||||||
|
'x-component-props': {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'6q933ic06mj': {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Row',
|
||||||
|
properties: {
|
||||||
|
nwtbkqd99zl2: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Col',
|
||||||
|
properties: {
|
||||||
|
'collection.title': {
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-collection-field': 'Message.collection.title',
|
||||||
|
'x-component-props': {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
nwtbkqd99zl1: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Col',
|
||||||
|
properties: {
|
||||||
|
'collection.name': {
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-collection-field': 'Message.collection.name',
|
||||||
|
'x-component-props': {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
xd4kzulndqk: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Row',
|
||||||
|
properties: {
|
||||||
|
k3sd72n83zd: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Col',
|
||||||
|
properties: {
|
||||||
|
user: {
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-collection-field': 'Message.user',
|
||||||
|
'x-component-props': {},
|
||||||
|
'x-read-pretty': true,
|
||||||
|
properties: {
|
||||||
|
viewer: {
|
||||||
|
type: 'void',
|
||||||
|
title: tval('View record'),
|
||||||
|
'x-component': 'RecordPicker.Viewer',
|
||||||
|
'x-component-props': {
|
||||||
|
className: 'nb-action-popup',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
tabs: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Tabs',
|
||||||
|
'x-component-props': {},
|
||||||
|
'x-initializer': 'TabPaneInitializers',
|
||||||
|
properties: {
|
||||||
|
tab1: {
|
||||||
|
type: 'void',
|
||||||
|
title: tval('Details'),
|
||||||
|
'x-component': 'Tabs.TabPane',
|
||||||
|
'x-designer': 'Tabs.Designer',
|
||||||
|
'x-component-props': {},
|
||||||
|
properties: {
|
||||||
|
grid: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-initializer': 'popup:common:addBlock',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'3lghgclbtcg': {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Row',
|
||||||
|
properties: {
|
||||||
|
'8l1v1auwlx0': {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Col',
|
||||||
|
properties: {
|
||||||
|
changes,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return <ActionInitializer schema={schema} />;
|
||||||
|
};
|
@ -0,0 +1,513 @@
|
|||||||
|
import React, { createContext, useContext } from 'react';
|
||||||
|
import {
|
||||||
|
ExtendCollectionsProvider,
|
||||||
|
FormProvider,
|
||||||
|
SchemaComponent,
|
||||||
|
TableBlockProvider,
|
||||||
|
useCollection_deprecated,
|
||||||
|
useCompile,
|
||||||
|
useRecord,
|
||||||
|
} from '@tachybase/client';
|
||||||
|
import { ArrayTable } from '@tachybase/components';
|
||||||
|
import { observer, useField } from '@tachybase/schema';
|
||||||
|
|
||||||
|
import { tval } from '../locale';
|
||||||
|
import { MessageDesigner } from './MessageDesigner';
|
||||||
|
|
||||||
|
const collection = {
|
||||||
|
name: 'messages',
|
||||||
|
title: tval('Messages'),
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'createdAt',
|
||||||
|
type: 'date',
|
||||||
|
interface: 'createdAt',
|
||||||
|
uiSchema: {
|
||||||
|
type: 'datetime',
|
||||||
|
title: tval('Created at'),
|
||||||
|
'x-component': 'DatePicker',
|
||||||
|
'x-component-props': {
|
||||||
|
showTime: true,
|
||||||
|
},
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'type',
|
||||||
|
type: 'string',
|
||||||
|
interface: 'select',
|
||||||
|
uiSchema: {
|
||||||
|
type: 'string',
|
||||||
|
title: tval('Action type'),
|
||||||
|
'x-component': 'Select',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
enum: [
|
||||||
|
{ label: tval('Create record'), value: 'create', color: 'lime' },
|
||||||
|
{ label: tval('Update record'), value: 'update', color: 'gold' },
|
||||||
|
{ label: tval('Delete record'), value: 'destroy', color: 'magenta' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const Username = observer(
|
||||||
|
() => {
|
||||||
|
const field = useField<any>();
|
||||||
|
return <div>{field?.value?.nickname || field.value?.id}</div>;
|
||||||
|
},
|
||||||
|
{ displayName: 'Username' },
|
||||||
|
);
|
||||||
|
|
||||||
|
const Collection = observer(
|
||||||
|
() => {
|
||||||
|
const field = useField<any>();
|
||||||
|
const { title, name } = field.value || {};
|
||||||
|
const compile = useCompile();
|
||||||
|
return <div>{title ? compile(title) : name}</div>;
|
||||||
|
},
|
||||||
|
{ displayName: 'Collection' },
|
||||||
|
);
|
||||||
|
|
||||||
|
const Field = observer(
|
||||||
|
() => {
|
||||||
|
const field = useField<any>();
|
||||||
|
const compile = useCompile();
|
||||||
|
if (!field.value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return <div>{field.value?.uiSchema?.title ? compile(field.value?.uiSchema?.title) : field.value.name}</div>;
|
||||||
|
},
|
||||||
|
{ displayName: 'Field' },
|
||||||
|
);
|
||||||
|
|
||||||
|
const Value = observer(
|
||||||
|
() => {
|
||||||
|
const field = useField<any>();
|
||||||
|
const record = ArrayTable.useRecord();
|
||||||
|
if (record.field?.uiSchema) {
|
||||||
|
return (
|
||||||
|
<FormProvider>
|
||||||
|
<SchemaComponent
|
||||||
|
schema={{
|
||||||
|
name: record.field.name,
|
||||||
|
...record.field?.uiSchema,
|
||||||
|
default: field.value,
|
||||||
|
'x-read-pretty': true,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <div>{field.value ? JSON.stringify(field.value) : null}</div>;
|
||||||
|
},
|
||||||
|
{ displayName: 'Value' },
|
||||||
|
);
|
||||||
|
|
||||||
|
const IsAssociationBlock = createContext(null);
|
||||||
|
IsAssociationBlock.displayName = 'IsAssociationBlock';
|
||||||
|
|
||||||
|
export const Message: any = () => {
|
||||||
|
const isAssoc = useContext(IsAssociationBlock);
|
||||||
|
const ext = {};
|
||||||
|
if (!isAssoc) {
|
||||||
|
ext['column31'] = {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'TableV2.Column',
|
||||||
|
title: tval('Record ID'),
|
||||||
|
properties: {
|
||||||
|
recordId: {
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<SchemaComponent
|
||||||
|
memoized
|
||||||
|
components={{ ArrayTable, Username, Collection, Field, Value }}
|
||||||
|
schema={{
|
||||||
|
type: 'void',
|
||||||
|
name: 'lfm4trkw8j3',
|
||||||
|
'x-component': 'div',
|
||||||
|
properties: {
|
||||||
|
actions: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ActionBar',
|
||||||
|
'x-component-props': {
|
||||||
|
style: {
|
||||||
|
marginBottom: 16,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
filter: {
|
||||||
|
type: 'void',
|
||||||
|
title: tval('Filter'),
|
||||||
|
'x-action': 'filter',
|
||||||
|
'x-component': 'Filter.Action',
|
||||||
|
'x-component-props': {
|
||||||
|
icon: 'FilterOutlined',
|
||||||
|
useProps: '{{ useFilterActionProps }}',
|
||||||
|
},
|
||||||
|
'x-align': 'left',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
y84dlntcaup: {
|
||||||
|
type: 'array',
|
||||||
|
'x-component': 'TableV2',
|
||||||
|
'x-component-props': {
|
||||||
|
rowKey: 'id',
|
||||||
|
rowSelection: {
|
||||||
|
type: 'checkbox',
|
||||||
|
},
|
||||||
|
useProps: '{{ useTableBlockProps }}',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
actions: {
|
||||||
|
type: 'void',
|
||||||
|
title: tval('Actions'),
|
||||||
|
'x-action-column': 'actions',
|
||||||
|
'x-decorator': 'TableV2.Column.ActionBar',
|
||||||
|
'x-component': 'TableV2.Column',
|
||||||
|
'x-designer': 'TableV2.ActionColumnDesigner',
|
||||||
|
'x-initializer': 'table:configureItemActions',
|
||||||
|
properties: {
|
||||||
|
actions: {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'DndContext',
|
||||||
|
'x-component': 'Space',
|
||||||
|
'x-component-props': {
|
||||||
|
split: '|',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
o80rypwmeeg: {
|
||||||
|
type: 'void',
|
||||||
|
title: tval('View'),
|
||||||
|
'x-toolbar': 'ActionSchemaToolbar',
|
||||||
|
'x-settings': 'actionSettings:view',
|
||||||
|
'x-component': 'Action.Link',
|
||||||
|
'x-component-props': {
|
||||||
|
openMode: 'drawer',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
drawer: {
|
||||||
|
type: 'void',
|
||||||
|
title: tval('View record'),
|
||||||
|
'x-component': 'Action.Container',
|
||||||
|
'x-component-props': {
|
||||||
|
className: 'nb-action-popup',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
grid: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
properties: {
|
||||||
|
tdlav8o9o17: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Row',
|
||||||
|
properties: {
|
||||||
|
'7bsnaf47i6g': {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Col',
|
||||||
|
properties: {
|
||||||
|
'6s2qbihe3tu': {
|
||||||
|
type: 'void',
|
||||||
|
'x-acl-action': 'Message:get',
|
||||||
|
'x-decorator': 'FormBlockProvider',
|
||||||
|
'x-decorator-props': {
|
||||||
|
resource: 'messages',
|
||||||
|
collection: 'messages',
|
||||||
|
readPretty: true,
|
||||||
|
action: 'get',
|
||||||
|
params: {
|
||||||
|
appends: ['collection', 'user', 'changes'],
|
||||||
|
},
|
||||||
|
useParams: '{{ useParamsFromRecord }}',
|
||||||
|
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
||||||
|
},
|
||||||
|
'x-component': 'CardItem',
|
||||||
|
properties: {
|
||||||
|
mevpuonrda0: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'FormV2',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
'x-component-props': {
|
||||||
|
useProps: '{{ useFormBlockProps }}',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
grid: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
properties: {
|
||||||
|
row1: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Row',
|
||||||
|
properties: {
|
||||||
|
col11: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Col',
|
||||||
|
properties: {
|
||||||
|
collection: {
|
||||||
|
type: 'string',
|
||||||
|
title: tval('Collection display name'),
|
||||||
|
'x-component': 'Collection',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
row2: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Row',
|
||||||
|
properties: {
|
||||||
|
col21: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Col',
|
||||||
|
properties: {
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
row3: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Row',
|
||||||
|
properties: {
|
||||||
|
col31: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Col',
|
||||||
|
properties: {
|
||||||
|
createdAt: {
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
row4: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Row',
|
||||||
|
properties: {
|
||||||
|
col41: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Col',
|
||||||
|
properties: {
|
||||||
|
user: {
|
||||||
|
type: 'string',
|
||||||
|
title: tval('User'),
|
||||||
|
'x-component': 'Username',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
row5: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Row',
|
||||||
|
properties: {
|
||||||
|
col51: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Col',
|
||||||
|
properties: {
|
||||||
|
changes: {
|
||||||
|
type: 'array',
|
||||||
|
title: tval('Field value changes'),
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'ArrayTable',
|
||||||
|
'x-component-props': {
|
||||||
|
pagination: { pageSize: 10 },
|
||||||
|
scroll: { x: '100%' },
|
||||||
|
},
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
column3: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ArrayTable.Column',
|
||||||
|
'x-component-props': {
|
||||||
|
width: 200,
|
||||||
|
title: tval('Field'),
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
field: {
|
||||||
|
type: 'string',
|
||||||
|
//'x-decorator': 'Editable',
|
||||||
|
'x-component': 'Field',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
column4: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ArrayTable.Column',
|
||||||
|
'x-component-props': {
|
||||||
|
width: 200,
|
||||||
|
title: tval('Before change'),
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
before: {
|
||||||
|
type: 'string',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Value',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
column5: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ArrayTable.Column',
|
||||||
|
'x-component-props': {
|
||||||
|
width: 200,
|
||||||
|
title: tval('After change'),
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
after: {
|
||||||
|
type: 'string',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Value',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
column1: {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'TableV2.Column.Decorator',
|
||||||
|
'x-component': 'TableV2.Column',
|
||||||
|
properties: {
|
||||||
|
createdAt: {
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
column2: {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'TableV2.Column.Decorator',
|
||||||
|
'x-component': 'TableV2.Column',
|
||||||
|
properties: {
|
||||||
|
type: {
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
column3: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'TableV2.Column',
|
||||||
|
title: tval('Collection display name'),
|
||||||
|
properties: {
|
||||||
|
collection: {
|
||||||
|
'x-component': 'Collection',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...ext,
|
||||||
|
column4: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'TableV2.Column',
|
||||||
|
title: tval('User'),
|
||||||
|
properties: {
|
||||||
|
user: {
|
||||||
|
'x-component': 'Username',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Message.Decorator = observer(
|
||||||
|
(props: any) => {
|
||||||
|
const parent = useCollection_deprecated();
|
||||||
|
const record = useRecord();
|
||||||
|
let filter = props?.params?.filter;
|
||||||
|
if (parent.name) {
|
||||||
|
const filterByTk = record?.[parent.filterTargetKey || 'id'];
|
||||||
|
if (filter) {
|
||||||
|
filter = {
|
||||||
|
$and: [
|
||||||
|
filter,
|
||||||
|
{
|
||||||
|
collectionName: parent.name,
|
||||||
|
recordId: `${filterByTk}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
filter = {
|
||||||
|
collectionName: parent.name,
|
||||||
|
recordId: `${filterByTk}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const defaults = {
|
||||||
|
collection: 'messages',
|
||||||
|
resource: 'messages',
|
||||||
|
action: 'list',
|
||||||
|
params: {
|
||||||
|
pageSize: 20,
|
||||||
|
appends: ['collection', 'user'],
|
||||||
|
...props.params,
|
||||||
|
filter,
|
||||||
|
sort: '-createdAt',
|
||||||
|
},
|
||||||
|
rowKey: 'id',
|
||||||
|
showIndex: true,
|
||||||
|
dragSort: false,
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<IsAssociationBlock.Provider value={!!parent.name}>
|
||||||
|
<ExtendCollectionsProvider collections={[collection]}>
|
||||||
|
<TableBlockProvider {...defaults}>{props.children}</TableBlockProvider>
|
||||||
|
</ExtendCollectionsProvider>
|
||||||
|
</IsAssociationBlock.Provider>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'Message.Decorator' },
|
||||||
|
);
|
||||||
|
|
||||||
|
Message.Designer = MessageDesigner;
|
@ -0,0 +1,78 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
GeneralSchemaDesigner,
|
||||||
|
SchemaSettingsDataScope,
|
||||||
|
SchemaSettingsDivider,
|
||||||
|
SchemaSettingsRemove,
|
||||||
|
SchemaSettingsSelectItem,
|
||||||
|
useCollection_deprecated,
|
||||||
|
useDesignable,
|
||||||
|
useFormBlockContext,
|
||||||
|
useTableBlockContext,
|
||||||
|
} from '@tachybase/client';
|
||||||
|
import { useField, useFieldSchema } from '@tachybase/schema';
|
||||||
|
|
||||||
|
import { useTranslation } from '../locale';
|
||||||
|
|
||||||
|
export const MessageDesigner = () => {
|
||||||
|
const { name, title } = useCollection_deprecated();
|
||||||
|
const field = useField();
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const { form } = useFormBlockContext();
|
||||||
|
const { service } = useTableBlockContext();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { dn } = useDesignable();
|
||||||
|
return (
|
||||||
|
<GeneralSchemaDesigner title={title || name}>
|
||||||
|
<SchemaSettingsDataScope
|
||||||
|
collectionName={name}
|
||||||
|
defaultFilter={fieldSchema?.['x-decorator-props']?.params?.filter || {}}
|
||||||
|
form={form}
|
||||||
|
onSubmit={({ filter }) => {
|
||||||
|
const params = field.decoratorProps.params || {};
|
||||||
|
params.filter = filter;
|
||||||
|
field.decoratorProps.params = params;
|
||||||
|
fieldSchema['x-decorator-props']['params'] = params;
|
||||||
|
service.run({ ...service.params?.[0], filter, page: 1 });
|
||||||
|
dn.emit('patch', {
|
||||||
|
schema: {
|
||||||
|
['x-uid']: fieldSchema['x-uid'],
|
||||||
|
'x-decorator-props': fieldSchema['x-decorator-props'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<SchemaSettingsSelectItem
|
||||||
|
title={t('Records per page')}
|
||||||
|
value={field.decoratorProps?.params?.pageSize || 20}
|
||||||
|
options={[
|
||||||
|
{ label: '10', value: 10 },
|
||||||
|
{ label: '20', value: 20 },
|
||||||
|
{ label: '50', value: 50 },
|
||||||
|
{ label: '100', value: 100 },
|
||||||
|
{ label: '200', value: 200 },
|
||||||
|
]}
|
||||||
|
onChange={(pageSize) => {
|
||||||
|
const params = field.decoratorProps.params || {};
|
||||||
|
params.pageSize = pageSize;
|
||||||
|
field.decoratorProps.params = params;
|
||||||
|
fieldSchema['x-decorator-props']['params'] = params;
|
||||||
|
service.run({ ...service.params?.[0], pageSize, page: 1 });
|
||||||
|
dn.emit('patch', {
|
||||||
|
schema: {
|
||||||
|
['x-uid']: fieldSchema['x-uid'],
|
||||||
|
'x-decorator-props': fieldSchema['x-decorator-props'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<SchemaSettingsDivider />
|
||||||
|
<SchemaSettingsRemove
|
||||||
|
removeParentsIfNoChildren
|
||||||
|
breakRemoveOn={{
|
||||||
|
'x-component': 'Grid',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</GeneralSchemaDesigner>
|
||||||
|
);
|
||||||
|
};
|
@ -1,6 +1,14 @@
|
|||||||
import { Plugin } from '@tachybase/client';
|
import { Plugin, SchemaInitializerItemType } from '@tachybase/client';
|
||||||
|
import PluginWorkflow from '@tachybase/plugin-workflow/client';
|
||||||
|
|
||||||
import { lang } from './locale';
|
import { MessageTableColumnInitializers } from './initializers/MessageColumnInitializers';
|
||||||
|
import { MessageTableActionColumnInitializers } from './initializers/MessageTableActionColumnInitializers';
|
||||||
|
import { MessageTableActionInitializers } from './initializers/MessageTableActionInitializers';
|
||||||
|
import { MessageInstruction } from './instructions/MessageInstruction';
|
||||||
|
import { lang, tval } from './locale';
|
||||||
|
import { MessageNotificationProvider } from './MessageNotificationProvider';
|
||||||
|
import { MessagePage } from './MessagePage';
|
||||||
|
import { MessageProvider } from './MessageProvider';
|
||||||
import { SubscriptionManager } from './SubscriptionManager';
|
import { SubscriptionManager } from './SubscriptionManager';
|
||||||
|
|
||||||
interface MessageType {
|
interface MessageType {
|
||||||
@ -32,6 +40,31 @@ export class PluginMessagesClient extends Plugin {
|
|||||||
|
|
||||||
// You can get and modify the app instance here
|
// You can get and modify the app instance here
|
||||||
async load() {
|
async load() {
|
||||||
|
this.app.addComponents({
|
||||||
|
Messages: MessagePage,
|
||||||
|
});
|
||||||
|
this.app.use(MessageProvider);
|
||||||
|
this.app.schemaInitializerManager.add(MessageTableActionInitializers);
|
||||||
|
this.app.schemaInitializerManager.add(MessageTableActionColumnInitializers);
|
||||||
|
this.app.schemaInitializerManager.add(MessageTableColumnInitializers);
|
||||||
|
|
||||||
|
const workflowPlugin = this.pm.get(PluginWorkflow);
|
||||||
|
workflowPlugin.registerInstruction('message-instruction', MessageInstruction);
|
||||||
|
|
||||||
|
const blockInitializers = this.app.schemaInitializerManager.get('page:addBlock');
|
||||||
|
const recordBlockInitializers = this.app.schemaInitializerManager.get('popup:common:addBlock');
|
||||||
|
const messages: Omit<SchemaInitializerItemType, 'name'> = {
|
||||||
|
title: tval('messages'),
|
||||||
|
Component: 'MessageBlockInitializer',
|
||||||
|
};
|
||||||
|
blockInitializers.add('otherBlocks.messages', messages);
|
||||||
|
recordBlockInitializers.add('otherBlocks.messages', messages);
|
||||||
|
|
||||||
|
this.app.use(MessageNotificationProvider);
|
||||||
|
this.app.router.add('admin.messages', {
|
||||||
|
path: '/admin/messages',
|
||||||
|
Component: MessagePage,
|
||||||
|
});
|
||||||
this.userSettingsManager.add('sub-manager', {
|
this.userSettingsManager.add('sub-manager', {
|
||||||
title: lang('Subscription management'),
|
title: lang('Subscription management'),
|
||||||
icon: 'BellOutlined',
|
icon: 'BellOutlined',
|
||||||
|
@ -0,0 +1,91 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
SchemaInitializer,
|
||||||
|
SchemaInitializerChildren,
|
||||||
|
useAssociatedTableColumnInitializerFields,
|
||||||
|
useCompile,
|
||||||
|
useInheritsTableColumnInitializerFields,
|
||||||
|
useTableColumnInitializerFields,
|
||||||
|
} from '@tachybase/client';
|
||||||
|
|
||||||
|
import { tval, useTranslation } from '../locale';
|
||||||
|
|
||||||
|
// 表格列配置
|
||||||
|
const ParentCollectionFields = () => {
|
||||||
|
const inheritFields = useInheritsTableColumnInitializerFields();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const compile = useCompile();
|
||||||
|
if (!inheritFields?.length) return null;
|
||||||
|
const res = [];
|
||||||
|
inheritFields.forEach((inherit) => {
|
||||||
|
Object.values(inherit)[0].length &&
|
||||||
|
res.push({
|
||||||
|
type: 'itemGroup',
|
||||||
|
divider: true,
|
||||||
|
title: t(`Parent collection fields`) + '(' + compile(`${Object.keys(inherit)[0]}`) + ')',
|
||||||
|
children: Object.values(inherit)[0].filter((v: any) => !v?.field?.isForeignKey),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return <SchemaInitializerChildren>{res}</SchemaInitializerChildren>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const AssociatedFields = () => {
|
||||||
|
const associatedFields = useAssociatedTableColumnInitializerFields();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
if (!associatedFields?.length) return null;
|
||||||
|
const schema: any = [
|
||||||
|
{
|
||||||
|
type: 'itemGroup',
|
||||||
|
divider: true,
|
||||||
|
title: t('Display association fields'),
|
||||||
|
children: associatedFields,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return <SchemaInitializerChildren>{schema}</SchemaInitializerChildren>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MessageTableColumnInitializers = new SchemaInitializer({
|
||||||
|
name: 'MessageTable:configureColumns',
|
||||||
|
insertPosition: 'beforeEnd',
|
||||||
|
icon: 'SettingOutlined',
|
||||||
|
title: tval('Configure columns'),
|
||||||
|
wrap(s) {
|
||||||
|
if (s['x-action-column']) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: 'void',
|
||||||
|
'x-toolbar': 'TableColumnSchemaToolbar',
|
||||||
|
'x-settings': 'fieldSettings:TableColumn',
|
||||||
|
'x-decorator': 'TableV2.Column.Decorator',
|
||||||
|
'x-component': 'TableV2.Column',
|
||||||
|
properties: {
|
||||||
|
[s.name]: {
|
||||||
|
...s,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'displayFields',
|
||||||
|
type: 'itemGroup',
|
||||||
|
title: tval('Display fields'),
|
||||||
|
useChildren: useTableColumnInitializerFields,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'parentCollectionFields',
|
||||||
|
Component: ParentCollectionFields,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'associationFields',
|
||||||
|
Component: AssociatedFields,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'actionColumn',
|
||||||
|
title: tval('Action column'),
|
||||||
|
Component: 'MessageTableActionColumnInitializer',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
@ -0,0 +1,29 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { InitializerWithSwitch, useSchemaInitializerItem } from '@tachybase/client';
|
||||||
|
|
||||||
|
import { tval } from '../locale';
|
||||||
|
|
||||||
|
export const MessageTableActionColumnInitializer = () => {
|
||||||
|
const schema = {
|
||||||
|
type: 'void',
|
||||||
|
title: tval('Actions'),
|
||||||
|
'x-decorator': 'TableV2.Column.ActionBar',
|
||||||
|
'x-component': 'TableV2.Column',
|
||||||
|
'x-designer': 'TableV2.ActionColumnDesigner',
|
||||||
|
'x-initializer': 'MessageTable:configureItemActions',
|
||||||
|
'x-action-column': 'actions',
|
||||||
|
properties: {
|
||||||
|
actions: {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'DndContext',
|
||||||
|
'x-component': 'Space',
|
||||||
|
'x-component-props': {
|
||||||
|
split: '|',
|
||||||
|
},
|
||||||
|
properties: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const itemConfig = useSchemaInitializerItem();
|
||||||
|
return <InitializerWithSwitch {...itemConfig} schema={schema} item={itemConfig} type={'x-action-column'} />;
|
||||||
|
};
|
@ -0,0 +1,69 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { createDesignable, Resizable, SchemaInitializer, useAPIClient, useDesignable } from '@tachybase/client';
|
||||||
|
import { useFieldSchema } from '@tachybase/schema';
|
||||||
|
|
||||||
|
import { MenuOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
|
import { tval, useTranslation } from '../locale';
|
||||||
|
|
||||||
|
export const MessageTableActionColumnInitializers = new SchemaInitializer({
|
||||||
|
name: 'MessageTable:configureItemActions',
|
||||||
|
insertPosition: 'beforeEnd',
|
||||||
|
Component: (props: any) => <MenuOutlined {...props} style={{ cursor: 'pointer' }} />,
|
||||||
|
useInsert() {
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const api = useAPIClient();
|
||||||
|
const { refresh } = useDesignable();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (schema) => {
|
||||||
|
const spaceSchema = fieldSchema.reduceProperties((buf, schema) => {
|
||||||
|
if (schema['x-component'] === 'Space') {
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}, null);
|
||||||
|
if (!spaceSchema) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const dn = createDesignable({
|
||||||
|
t,
|
||||||
|
api,
|
||||||
|
refresh,
|
||||||
|
current: spaceSchema,
|
||||||
|
});
|
||||||
|
dn.loadAPIClientEvents();
|
||||||
|
dn.insertBeforeEnd(schema);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'enableActions',
|
||||||
|
type: 'itemGroup',
|
||||||
|
title: tval('Enable actions'),
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'view',
|
||||||
|
type: 'item',
|
||||||
|
title: tval('View'),
|
||||||
|
Component: 'MessageViewActionInitializer',
|
||||||
|
schema: {
|
||||||
|
'x-component': 'Action.Link',
|
||||||
|
'x-action': 'view',
|
||||||
|
'x-decorator': 'ACLActionProvider',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'divider',
|
||||||
|
type: 'divider',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'columnWidth',
|
||||||
|
type: 'item',
|
||||||
|
title: tval('Column width'),
|
||||||
|
Component: Resizable,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
@ -0,0 +1,39 @@
|
|||||||
|
import { SchemaInitializer } from '@tachybase/client';
|
||||||
|
|
||||||
|
import { tval } from '../locale';
|
||||||
|
|
||||||
|
export type { ButtonProps } from 'antd';
|
||||||
|
|
||||||
|
export const MessageTableActionInitializers = new SchemaInitializer({
|
||||||
|
name: 'MessageTable:configureActions',
|
||||||
|
title: tval('Configure actions'),
|
||||||
|
icon: 'SettingOutlined',
|
||||||
|
style: {
|
||||||
|
marginLeft: 8,
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
type: 'itemGroup',
|
||||||
|
title: tval('Enable actions'),
|
||||||
|
name: 'enableActions',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'filter',
|
||||||
|
title: tval('Filter'),
|
||||||
|
Component: 'FilterActionInitializer',
|
||||||
|
schema: {
|
||||||
|
'x-align': 'left',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'refresh',
|
||||||
|
title: tval('Refresh'),
|
||||||
|
Component: 'RefreshActionInitializer',
|
||||||
|
schema: {
|
||||||
|
'x-align': 'right',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
@ -0,0 +1,27 @@
|
|||||||
|
import { Instruction, VariableOption } from '@tachybase/plugin-workflow/client';
|
||||||
|
|
||||||
|
export class MessageInstruction extends Instruction {
|
||||||
|
title = 'Messages';
|
||||||
|
type = 'message-instruction';
|
||||||
|
group = 'extended';
|
||||||
|
fieldset = {
|
||||||
|
digit: {
|
||||||
|
type: 'number',
|
||||||
|
title: 'Digit',
|
||||||
|
name: 'digit',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'InputNumber',
|
||||||
|
'x-component-props': {
|
||||||
|
min: 1,
|
||||||
|
max: 10,
|
||||||
|
},
|
||||||
|
default: 6,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
useVariables(node, options): VariableOption {
|
||||||
|
return {
|
||||||
|
value: node.key,
|
||||||
|
label: node.title,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
import { i18n, tval as nTval } from '@tachybase/client';
|
||||||
|
import { useTranslation as useT } from 'react-i18next';
|
||||||
|
|
||||||
|
export const NAMESPACE = 'messages';
|
||||||
|
|
||||||
|
export function lang(key: string) {
|
||||||
|
return i18n.t(key, { ns: NAMESPACE });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useTranslation() {
|
||||||
|
return useT(NAMESPACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const tval = (key: string) => nTval(key, { ns: NAMESPACE });
|
@ -0,0 +1,20 @@
|
|||||||
|
import { FlowNodeModel, IJob, Instruction, InstructionResult, JOB_STATUS, Processor } from '@tachybase/plugin-workflow';
|
||||||
|
|
||||||
|
export class MessageInstruction extends Instruction {
|
||||||
|
async run(node: FlowNodeModel, input: any, processor: Processor): Promise<IJob> {
|
||||||
|
console.log('my instruction runs!');
|
||||||
|
|
||||||
|
const msgRepo = node.db.getRepository('messages');
|
||||||
|
|
||||||
|
await msgRepo.create({
|
||||||
|
values: {
|
||||||
|
title: 'testing',
|
||||||
|
content: 'testing',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: JOB_STATUS.RESOLVED,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,17 @@
|
|||||||
|
import { PluginWorkflow } from '@tachybase/plugin-workflow';
|
||||||
import { Plugin } from '@tachybase/server';
|
import { Plugin } from '@tachybase/server';
|
||||||
|
|
||||||
|
import { MessageInstruction } from './instructions/message-instruction';
|
||||||
|
|
||||||
export class PluginMessagesServer extends Plugin {
|
export class PluginMessagesServer extends Plugin {
|
||||||
async afterAdd() {}
|
async afterAdd() {}
|
||||||
|
|
||||||
async beforeLoad() {}
|
async beforeLoad() {}
|
||||||
|
|
||||||
async load() {}
|
async load() {
|
||||||
|
const workflowPlugin = this.app.getPlugin<PluginWorkflow>(PluginWorkflow);
|
||||||
|
workflowPlugin.registerInstruction('message-instruction', MessageInstruction);
|
||||||
|
}
|
||||||
|
|
||||||
async install() {}
|
async install() {}
|
||||||
|
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { css, PinnedPluginListProvider, SchemaComponentOptions, useToken } from '@tachybase/client';
|
||||||
|
|
||||||
|
import { MobileOutlined } from '@ant-design/icons';
|
||||||
|
import { Button, Tooltip } from 'antd';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { useTranslation } from './locale';
|
||||||
|
|
||||||
|
const MobileLink = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { token } = useToken();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<Tooltip title={t('Mobile UI')}>
|
||||||
|
<Button
|
||||||
|
icon={<MobileOutlined style={{ color: token.colorTextHeaderMenu }} />}
|
||||||
|
title={t('Mobile UI')}
|
||||||
|
onClick={() => {
|
||||||
|
navigate('/mobile');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MobileLinkProvider = (props) => {
|
||||||
|
return (
|
||||||
|
<PinnedPluginListProvider
|
||||||
|
items={{
|
||||||
|
me: { order: 210, component: 'MobileLink', pin: true, isPublic: true },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SchemaComponentOptions components={{ MobileLink }}>{props.children}</SchemaComponentOptions>
|
||||||
|
</PinnedPluginListProvider>
|
||||||
|
);
|
||||||
|
};
|
@ -17,6 +17,7 @@ import {
|
|||||||
import { NAMESPACE } from './locale';
|
import { NAMESPACE } from './locale';
|
||||||
import { MAppNotFound } from './MAppNotFound';
|
import { MAppNotFound } from './MAppNotFound';
|
||||||
import { MobileClientProvider } from './MobileClientProvider';
|
import { MobileClientProvider } from './MobileClientProvider';
|
||||||
|
import { MobileLinkProvider } from './MobileLinkProvider';
|
||||||
import MApplication from './router/Application';
|
import MApplication from './router/Application';
|
||||||
|
|
||||||
export class MobileClientPlugin extends Plugin {
|
export class MobileClientPlugin extends Plugin {
|
||||||
@ -26,6 +27,7 @@ export class MobileClientPlugin extends Plugin {
|
|||||||
this.addRoutes();
|
this.addRoutes();
|
||||||
this.addSettings();
|
this.addSettings();
|
||||||
this.app.use(MobileClientProvider);
|
this.app.use(MobileClientProvider);
|
||||||
|
this.app.use(MobileLinkProvider);
|
||||||
this.app.schemaInitializerManager.add(mBlockInitializers_deprecated);
|
this.app.schemaInitializerManager.add(mBlockInitializers_deprecated);
|
||||||
this.app.schemaInitializerManager.add(mBlockInitializers);
|
this.app.schemaInitializerManager.add(mBlockInitializers);
|
||||||
this.app.schemaInitializerManager.add(ImageSearchConfigureFields);
|
this.app.schemaInitializerManager.add(ImageSearchConfigureFields);
|
||||||
|
@ -47,5 +47,6 @@
|
|||||||
"AllProducts": "AllProducts",
|
"AllProducts": "AllProducts",
|
||||||
"sort":"sort",
|
"sort":"sort",
|
||||||
"Add new":"Add new",
|
"Add new":"Add new",
|
||||||
|
"Mobile UI": "Mobile UI",
|
||||||
"please enter the date": "please enter the date"
|
"please enter the date": "please enter the date"
|
||||||
}
|
}
|
||||||
|
@ -50,5 +50,6 @@
|
|||||||
"TabSearch":"表格搜索",
|
"TabSearch":"表格搜索",
|
||||||
"ImageSearch":"图片搜索",
|
"ImageSearch":"图片搜索",
|
||||||
"Add new":"添加",
|
"Add new":"添加",
|
||||||
|
"Mobile UI": "移动端页面",
|
||||||
"please enter the date": "请输入日期"
|
"please enter the date": "请输入日期"
|
||||||
}
|
}
|
||||||
|
@ -3355,12 +3355,21 @@ importers:
|
|||||||
|
|
||||||
packages/plugins/@tachybase/plugin-messages:
|
packages/plugins/@tachybase/plugin-messages:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@ant-design/icons':
|
||||||
|
specifier: ~5.3.7
|
||||||
|
version: 5.3.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
'@tachybase/client':
|
'@tachybase/client':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../../core/client
|
version: link:../../../core/client
|
||||||
'@tachybase/database':
|
'@tachybase/database':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../../core/database
|
version: link:../../../core/database
|
||||||
|
'@tachybase/plugin-workflow':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../plugin-workflow
|
||||||
|
'@tachybase/schema':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../../core/schema
|
||||||
'@tachybase/server':
|
'@tachybase/server':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../../core/server
|
version: link:../../../core/server
|
||||||
@ -3370,6 +3379,12 @@ importers:
|
|||||||
antd:
|
antd:
|
||||||
specifier: 5.19.4
|
specifier: 5.19.4
|
||||||
version: 5.19.4(date-fns@3.6.0)(luxon@3.5.0)(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
version: 5.19.4(date-fns@3.6.0)(luxon@3.5.0)(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
react-i18next:
|
||||||
|
specifier: ^14.1.2
|
||||||
|
version: 14.1.2(i18next@23.13.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
react-router-dom:
|
||||||
|
specifier: ~6.25.1
|
||||||
|
version: 6.25.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
|
||||||
packages/plugins/@tachybase/plugin-mobile-client:
|
packages/plugins/@tachybase/plugin-mobile-client:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
Loading…
Reference in New Issue
Block a user