feat: translations

This commit is contained in:
chenos 2022-04-22 23:58:19 +08:00
parent 5c6a39f2f4
commit 5c0184a397
35 changed files with 221 additions and 157 deletions

View File

@ -2,7 +2,7 @@
<html>
<head>
<meta charset="utf-8" />
<title>Your App</title>
<title>Loading...</title>
<script>
showLog=true;function log(m){if(window.console&&showLog){console.log(m)}}function css_browser_selector(u){var uaInfo={},screens=[320,480,640,768,1024,1152,1280,1440,1680,1920,2560],allScreens=screens.length,ua=u.toLowerCase(),is=function(t){return RegExp(t,'i').test(ua)},version=function(p,n){n=n.replace('.','_');var i=n.indexOf('_'),ver='';while(i>0){ver+=' '+p+n.substring(0,i);i=n.indexOf('_',i+1)}ver+=' '+p+n;return ver},g='gecko',w='webkit',c='chrome',f='firefox',s='safari',o='opera',m='mobile',a='android',bb='blackberry',lang='lang_',dv='device_',html=document.documentElement,b=[(!/opera|webtv/i.test(ua)&&/msie\s(\d+)/.test(ua))||/trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/.test(ua)?'ie ie'+(/trident\/4\.0/.test(ua)?'8':RegExp.$1=='11.0'?'11':RegExp.$1):is('firefox/')?g+' '+f+(/firefox\/((\d+)(\.(\d+))(\.\d+)*)/.test(ua)?' '+f+RegExp.$2+' '+f+RegExp.$2+'_'+RegExp.$4:''):is('gecko/')?g:is('opera')?o+(/version\/((\d+)(\.(\d+))(\.\d+)*)/.test(ua)?' '+o+RegExp.$2+' '+o+RegExp.$2+'_'+RegExp.$4:/opera(\s|\/)(\d+)\.(\d+)/.test(ua)?' '+o+RegExp.$2+' '+o+RegExp.$2+'_'+RegExp.$3:''):is('konqueror')?'konqueror':is('blackberry')?bb+(/Version\/(\d+)(\.(\d+)+)/i.test(ua)?' '+bb+RegExp.$1+' '+bb+RegExp.$1+RegExp.$2.replace('.','_'):/Blackberry ?(([0-9]+)([a-z]?))[\/|;]/gi.test(ua)?' '+bb+RegExp.$2+(RegExp.$3?' '+bb+RegExp.$2+RegExp.$3:''):''):is('android')?a+(/Version\/(\d+)(\.(\d+))+/i.test(ua)?' '+a+RegExp.$1+' '+a+RegExp.$1+RegExp.$2.replace('.','_'):'')+(/Android (.+); (.+) Build/i.test(ua)?' '+dv+RegExp.$2.replace(/ /g,'_').replace(/-/g,'_'):''):is('chrome')?w+' '+c+(/chrome\/((\d+)(\.(\d+))(\.\d+)*)/.test(ua)?' '+c+RegExp.$2+(RegExp.$4>0?' '+c+RegExp.$2+'_'+RegExp.$4:''):''):is('iron')?w+' iron':is('applewebkit/')?w+' '+s+(/version\/((\d+)(\.(\d+))(\.\d+)*)/.test(ua)?' '+s+RegExp.$2+' '+s+RegExp.$2+RegExp.$3.replace('.','_'):/ Safari\/(\d+)/i.test(ua)?RegExp.$1=='419'||RegExp.$1=='417'||RegExp.$1=='416'||RegExp.$1=='412'?' '+s+'2_0':RegExp.$1=='312'?' '+s+'1_3':RegExp.$1=='125'?' '+s+'1_2':RegExp.$1=='85'?' '+s+'1_0':'':''):is('mozilla/')?g:'',is('android|mobi|mobile|j2me|iphone|ipod|ipad|blackberry|playbook|kindle|silk',)?m:'',is('j2me')?'j2me':is('ipad|ipod|iphone')?(/CPU( iPhone)? OS (\d+[_|\.]\d+([_|\.]\d+)*)/i.test(ua)?'ios'+version('ios',RegExp.$2):'')+' '+(/(ip(ad|od|hone))/gi.test(ua)?RegExp.$1:''):is('playbook')?'playbook':is('kindle|silk')?'kindle':is('playbook')?'playbook':is('mac')?'mac'+(/mac os x ((\d+)[.|_](\d+))/.test(ua)?' mac'+RegExp.$2+' mac'+RegExp.$1.replace('.','_'):''):is('win')?'win'+(is('windows nt 6.2')?' win8':is('windows nt 6.1')?' win7':is('windows nt 6.0')?' vista':is('windows nt 5.2')||is('windows nt 5.1')?' win_xp':is('windows nt 5.0')?' win_2k':is('windows nt 4.0')||is('WinNT4.0')?' win_nt':''):is('freebsd')?'freebsd':is('x11|linux')?'linux':'',/[; |\[](([a-z]{2})(\-[a-z]{2})?)[)|;|\]]/i.test(ua)?(lang+RegExp.$2).replace('-','_')+(RegExp.$3!=''?(' '+lang+RegExp.$1).replace('-','_'):''):'',is('ipad|iphone|ipod')&&!is('safari')?'ipad_app':'',];console.debug(ua);function screenSize(){var w=window.outerWidth||html.clientWidth;var h=window.outerHeight||html.clientHeight;uaInfo.orientation=w<h?'portrait':'landscape';html.className=html.className.replace(/ ?orientation_\w+/g,'').replace(/ [min|max|cl]+[w|h]_\d+/g,'');for(var i=allScreens-1;i>=0;i--){if(w>=screens[i]){uaInfo.maxw=screens[i];break}}widthClasses='';for(var info in uaInfo){widthClasses+=' '+info+'_'+uaInfo[info]}html.className=html.className+widthClasses;return widthClasses}window.onresize=screenSize;screenSize();function retina(){var r=window.devicePixelRatio>1;if(r){html.className+=' retina'}else{html.className+=' non-retina'}}retina();var cssbs=b.join(' ')+' js ';html.className=(cssbs+html.className.replace(/\b(no[-|_]?)?js\b/g,'')).replace(/^ /,'').replace(/ +/g,' ');return cssbs}css_browser_selector(navigator.userAgent);
</script>

View File

@ -1,7 +1,9 @@
import { Checkbox, message, Table } from 'antd';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useAPIClient, useRequest } from '../../api-client';
import { useRecord } from '../../record-provider';
import { useCompile } from '../../schema-component';
import { useMenuItems } from './RoleTable';
const findUids = (items) => {
@ -20,6 +22,8 @@ export const MenuConfigure = () => {
const record = useRecord();
const api = useAPIClient();
const items = useMenuItems();
const compile = useCompile();
const { t } = useTranslation();
const allUids = findUids(items);
const [uids, setUids] = useState([]);
const { loading, refresh } = useRequest(
@ -50,7 +54,7 @@ export const MenuConfigure = () => {
columns={[
{
dataIndex: 'title',
title: '菜单项',
title: t('Menu item title'),
},
{
dataIndex: 'accessible',
@ -69,10 +73,10 @@ export const MenuConfigure = () => {
});
}
refresh();
message.success('保存成功');
message.success(t('Saved successfully'));
}}
/>{' '}
访
{t('Accessible')}
</>
),
render: (_, schema) => {
@ -93,7 +97,7 @@ export const MenuConfigure = () => {
tk: schema.uid,
},
});
message.success('保存成功');
message.success(t('Saved successfully'));
}}
/>
);

View File

@ -85,14 +85,14 @@ export const RolesResourcesActions = connect((props) => {
<div>
<RoleResourceCollectionContext.Provider value={collection}>
<FormLayout layout={'vertical'}>
<FormItem label={'操作权限'}>
<FormItem label={t('Action permission')}>
<Table
size={'small'}
pagination={false}
columns={[
{
dataIndex: 'displayName',
title: '操作',
title: t('Action display name'),
render: (value) => compile(value),
},
{
@ -148,14 +148,14 @@ export const RolesResourcesActions = connect((props) => {
})}
/>
</FormItem>
<FormItem label={'字段权限'}>
<FormItem label={t('Field permission')}>
<Table
pagination={false}
dataSource={fieldPermissions}
columns={[
{
dataIndex: ['uiSchema', 'title'],
title: '字段名称',
title: t('Field display name'),
render: (value) => compile(value),
},
...availableActionsWithFields.map((action) => {

View File

@ -12,34 +12,34 @@ const collection = {
name: 'title',
interface: 'input',
uiSchema: {
title: '数据表名称',
title: '{{t("Collection display name")}}',
type: 'number',
'x-component': 'Input',
required: true,
} as ISchema,
},
{
type: 'string',
name: 'name',
interface: 'input',
uiSchema: {
title: '数据表标识',
type: 'string',
'x-component': 'Input',
description: '使用英文',
} as ISchema,
},
// {
// type: 'string',
// name: 'name',
// interface: 'input',
// uiSchema: {
// title: '数据表标识',
// type: 'string',
// 'x-component': 'Input',
// description: '使用英文',
// } as ISchema,
// },
{
type: 'string',
name: 'usingConfig',
interface: 'input',
uiSchema: {
title: '权限策略',
title: '{{t("Permission policy")}}',
type: 'string',
'x-component': 'Select',
enum: [
{ label: '单独配置', value: 'resourceAction', color: 'orange' },
{ label: '通用配置', value: 'strategy', color: 'default' },
{ label: '{{t("Individual")}}', value: 'resourceAction', color: 'orange' },
{ label: '{{t("General")}}', value: 'strategy', color: 'default' },
],
} as ISchema,
},
@ -127,7 +127,7 @@ export const roleCollectionsSchema: ISchema = {
},
column4: {
type: 'void',
title: 'Actions',
title: '{{t("Actions")}}',
'x-component': 'Table.Column',
properties: {
actions: {
@ -139,7 +139,7 @@ export const roleCollectionsSchema: ISchema = {
properties: {
configure: {
type: 'void',
title: '单独配置权限',
title: '{{t("Configure")}}',
'x-component': 'Action.Link',
'x-component-props': {
type: 'primary',
@ -152,15 +152,16 @@ export const roleCollectionsSchema: ISchema = {
'x-decorator-props': {
useValues: useRoleResourceValues,
},
title: '配置权限',
title: '{{t("Configure permission")}}',
properties: {
usingActionsConfig: {
title: '{{t("Permission policy")}}',
'x-component': 'Radio.Group',
'x-decorator': 'FormItem',
default: false,
enum: [
{ value: false, label: '使用通用权限' },
{ value: true, label: '单独配置权限' },
{ value: false, label: '{{t("General")}}' },
{ value: true, label: '{{t("Individual")}}' },
],
'x-reactions': {
target: 'actions',
@ -179,15 +180,15 @@ export const roleCollectionsSchema: ISchema = {
type: 'void',
'x-component': 'Action.Drawer.Footer',
properties: {
action1: {
title: 'Cancel',
cancel: {
title: '{{t("Cancel")}}',
'x-component': 'Action',
'x-component-props': {
useAction: '{{ cm.useCancelAction }}',
},
},
action2: {
title: 'Submit',
submit: {
title: '{{t("Submit")}}',
'x-component': 'Action',
'x-component-props': {
type: 'primary',

View File

@ -14,7 +14,7 @@ const collection = {
name: 'title',
interface: 'input',
uiSchema: {
title: '角色名称',
title: '{{t("Role title")}}',
type: 'number',
'x-component': 'Input',
required: true,
@ -25,7 +25,7 @@ const collection = {
name: 'name',
interface: 'input',
uiSchema: {
title: '角色标识',
title: '{{t("Role name")}}',
type: 'string',
'x-component': 'Input',
} as ISchema,
@ -35,7 +35,7 @@ const collection = {
name: 'default',
interface: 'boolean',
uiSchema: {
title: '默认角色',
title: '{{t("Default role")}}',
type: 'boolean',
'x-component': 'Checkbox',
} as ISchema,
@ -82,12 +82,19 @@ export const roleSchema: ISchema = {
properties: {
delete: {
type: 'void',
title: '删除',
title: '{{ t("Delete") }}',
'x-component': 'Action',
'x-component-props': {
useAction: '{{ cm.useBulkDestroyAction }}',
confirm: {
title: "{{t('Delete role')}}",
content: "{{t('Are you sure you want to delete it?')}}",
},
},
},
create: {
type: 'void',
title: '添加角色',
title: '{{t("Add role")}}',
'x-component': 'Action',
'x-component-props': {
type: 'primary',
@ -111,7 +118,7 @@ export const roleSchema: ISchema = {
);
},
},
title: '添加角色',
title: '{{t("Add role")}}',
properties: {
title: {
'x-component': 'CollectionField',
@ -120,25 +127,28 @@ export const roleSchema: ISchema = {
name: {
'x-component': 'CollectionField',
'x-decorator': 'FormItem',
description: '{{t("Randomly generated and can be modified. Support letters, numbers and underscores, must start with an letter.")}}',
description:
'{{t("Randomly generated and can be modified. Support letters, numbers and underscores, must start with an letter.")}}',
},
default: {
'x-component': 'CollectionField',
'x-decorator': 'FormItem',
title: '',
'x-content': '{{t("Default role")}}',
},
footer: {
type: 'void',
'x-component': 'Action.Drawer.Footer',
properties: {
action1: {
title: 'Cancel',
cancel: {
title: '{{t("Cancel")}}',
'x-component': 'Action',
'x-component-props': {
useAction: '{{ cm.useCancelAction }}',
},
},
action2: {
title: 'Submit',
submit: {
title: '{{t("Submit")}}',
'x-component': 'Action',
'x-component-props': {
type: 'primary',
@ -215,14 +225,14 @@ export const roleSchema: ISchema = {
properties: {
view: {
type: 'void',
title: '配置权限',
title: '{{t("Configure")}}',
'x-component': 'Action.Link',
'x-component-props': {},
properties: {
drawer: {
type: 'void',
'x-component': 'Action.Drawer',
title: '配置权限',
title: '{{t("Configure permissions")}}',
properties: {
tabs1: {
type: 'void',
@ -231,7 +241,7 @@ export const roleSchema: ISchema = {
properties: {
tab1: {
type: 'void',
title: '系统全局配置',
title: '{{t("Global permissions")}}',
'x-component': 'Tabs.TabPane',
'x-component-props': {},
properties: {
@ -242,7 +252,7 @@ export const roleSchema: ISchema = {
},
tab2: {
type: 'void',
title: '数据权限',
title: '{{t("Action permissions")}}',
'x-component': 'Tabs.TabPane',
'x-component-props': {},
properties: {
@ -251,7 +261,7 @@ export const roleSchema: ISchema = {
},
tab3: {
type: 'void',
title: '菜单访问权限',
title: '{{t("Menu permissions")}}',
'x-component': 'Tabs.TabPane',
'x-component-props': {},
properties: {
@ -268,7 +278,7 @@ export const roleSchema: ISchema = {
},
update: {
type: 'void',
title: '编辑',
title: '{{t("Edit")}}',
'x-component': 'Action.Link',
'x-component-props': {
type: 'primary',
@ -281,7 +291,7 @@ export const roleSchema: ISchema = {
'x-decorator-props': {
useValues: '{{ cm.useValuesFromRecord }}',
},
title: '编辑角色',
title: '{{t("Edit role")}}',
properties: {
title: {
'x-component': 'CollectionField',
@ -293,22 +303,24 @@ export const roleSchema: ISchema = {
'x-disabled': true,
},
default: {
title: '',
'x-component': 'CollectionField',
'x-decorator': 'FormItem',
'x-content': '{{t("Default role")}}',
},
footer: {
type: 'void',
'x-component': 'Action.Drawer.Footer',
properties: {
action1: {
title: 'Cancel',
cancel: {
title: '{{t("Cancel")}}',
'x-component': 'Action',
'x-component-props': {
useAction: '{{ cm.useCancelAction }}',
},
},
action2: {
title: 'Submit',
submit: {
title: '{{t("Submit")}}',
'x-component': 'Action',
'x-component-props': {
type: 'primary',
@ -323,10 +335,14 @@ export const roleSchema: ISchema = {
},
delete: {
type: 'void',
title: '删除',
title: '{{ t("Delete") }}',
'x-component': 'Action.Link',
'x-component-props': {
useAction: '{{ cm.useDestroyAction }}',
confirm: {
title: "{{t('Delete role')}}",
content: "{{t('Are you sure you want to delete it?')}}",
},
useAction: '{{cm.useDestroyAction}}',
},
},
},

View File

@ -12,7 +12,7 @@ export const rolesResourcesScopesCollection = {
name: 'name',
interface: 'input',
uiSchema: {
title: '名称',
title: '{{t("Scope name")}}',
type: 'string',
'x-component': 'Input',
required: true,

View File

@ -3,7 +3,7 @@ import { Modal } from 'antd';
import { useTranslation } from 'react-i18next';
import { useHistory } from 'react-router-dom';
import { useCollection } from '../../collection-manager';
import { useActionContext } from '../../schema-component';
import { useActionContext, useCompile } from '../../schema-component';
import { useBlockRequestContext, useFilterByTk } from '../BlockProvider';
import { useDetailsBlockContext } from '../DetailsBlockProvider';
import { TableFieldResource } from '../TableFieldProvider';
@ -60,6 +60,7 @@ export const useCreateActionProps = () => {
const { t } = useTranslation();
const actionSchema = useFieldSchema();
const { fields, getField } = useCollection();
const compile = useCompile();
return {
async onClick() {
const fieldNames = fields.map((field) => field.name);
@ -112,7 +113,7 @@ export const useCreateActionProps = () => {
return;
}
Modal.success({
title: onSuccess?.successMessage,
title: compile(onSuccess?.successMessage),
onOk: async () => {
await form.reset();
if (onSuccess?.redirecting && onSuccess?.redirectTo) {
@ -136,6 +137,7 @@ export const useUpdateActionProps = () => {
const actionSchema = useFieldSchema();
const history = useHistory();
const { fields, getField } = useCollection();
const compile = useCompile();
return {
async onClick() {
const skipValidator = actionSchema?.['x-action-settings']?.skipValidator;
@ -197,7 +199,7 @@ export const useUpdateActionProps = () => {
const onSuccess = actionSchema?.['x-action-settings']?.onSuccess;
if (onSuccess?.successMessage) {
Modal.success({
title: onSuccess?.successMessage,
title: compile(onSuccess?.successMessage),
onOk: async () => {
await form.reset();
if (onSuccess?.redirecting && onSuccess?.redirectTo) {

View File

@ -78,10 +78,10 @@ export const chinaRegion: IField = {
children: [
{
name: 'name',
title: '名称',
title: '{{t("Filename")}}',
operators: operators.string,
schema: {
title: '名称',
title: '{{t("Filename")}}',
type: 'string',
'x-component': 'Input',
},

View File

@ -288,5 +288,8 @@ export default {
"Block type": "区块类型",
"Action column": "操作列",
"Records per page": "每页显示数量",
"(Fields only)": "(仅字段)"
"(Fields only)": "(仅字段)",
"Button title": "按钮标题",
"Button icon": "按钮图标",
"Submitted successfully": "提交成功",
}

View File

@ -13,16 +13,16 @@ export const ActionDesigner = (props) => {
return (
<GeneralSchemaDesigner {...props}>
<SchemaSettings.ModalItem
title={'编辑'}
title={t('Edit button')}
schema={
{
type: 'object',
title: '编辑按钮',
title: t('Edit button'),
properties: {
title: {
'x-decorator': 'FormItem',
'x-component': 'Input',
title: '按钮标题',
title: t('Button title'),
default: fieldSchema.title,
'x-component-props': {},
// description: `原字段标题:${collectionField?.uiSchema?.title}`,
@ -30,7 +30,7 @@ export const ActionDesigner = (props) => {
icon: {
'x-decorator': 'FormItem',
'x-component': 'IconPicker',
title: '按钮图标',
title: t('Button icon'),
default: fieldSchema?.['x-component-props']?.icon,
'x-component-props': {},
// description: `原字段标题:${collectionField?.uiSchema?.title}`,
@ -60,10 +60,10 @@ export const ActionDesigner = (props) => {
/>
{isPopupAction && (
<SchemaSettings.SelectItem
title={'打开方式'}
title={t('Open mode')}
options={[
{ label: '抽屉', value: 'drawer' },
{ label: '对话框', value: 'modal' },
{ label: t('Drawer'), value: 'drawer' },
{ label: t('Dialog'), value: 'modal' },
]}
value={field.componentProps.openMode}
onChange={(value) => {
@ -81,7 +81,7 @@ export const ActionDesigner = (props) => {
)}
{fieldSchema?.['x-action-settings'] && (
<SchemaSettings.SwitchItem
title={'跳过必填校验'}
title={t('Skip required validation')}
checked={!!fieldSchema?.['x-action-settings']?.skipValidator}
onChange={(value) => {
fieldSchema['x-action-settings'].skipValidator = value;
@ -98,13 +98,13 @@ export const ActionDesigner = (props) => {
)}
{fieldSchema?.['x-action-settings'] && (
<SchemaSettings.ModalItem
title={'表单值'}
title={t('Form values')}
schema={
{
type: 'object',
properties: {
overwriteValues: {
title: '以下字段提交时,保存值为',
title: t('When submitting the following fields, the saved values are'),
'x-decorator': 'FormItem',
'x-component': 'Input.TextArea',
default: JSON.stringify(fieldSchema?.['x-action-settings']?.overwriteValues),
@ -190,6 +190,9 @@ export const ActionDesigner = (props) => {
breakRemoveOn={(s) => {
return s['x-component'] === 'Space' || s['x-component'].endsWith('ActionBar');
}}
confirm={{
title: t('Delete action')
}}
/>
</GeneralSchemaDesigner>
);

View File

@ -13,7 +13,6 @@ export const BlockItem: React.FC<any> = (props) => {
props.className,
css`
position: relative;
overflow: hidden;
&:hover {
> .general-schema-designer {
display: block;

View File

@ -65,27 +65,25 @@ export const FilterActionDesigner = (props) => {
</SchemaSettings.ItemGroup>
<SchemaSettings.Divider />
<SchemaSettings.ModalItem
title={'编辑'}
title={t('Edit button')}
schema={
{
type: 'object',
title: '编辑按钮',
title: t('Edit button'),
properties: {
title: {
'x-decorator': 'FormItem',
'x-component': 'Input',
title: '按钮标题',
title: t('Button title'),
default: fieldSchema.title,
'x-component-props': {},
// description: `原字段标题:${collectionField?.uiSchema?.title}`,
},
icon: {
'x-decorator': 'FormItem',
'x-component': 'IconPicker',
title: '按钮图标',
title: t('Button icon'),
default: fieldSchema?.['x-component-props']?.icon,
'x-component-props': {},
// description: `原字段标题:${collectionField?.uiSchema?.title}`,
},
},
} as ISchema

View File

@ -174,7 +174,7 @@ FormItem.Designer = () => {
)}
{collectionField?.target && (
<SchemaSettings.SelectItem
title={'标题字段'}
title={t('Title field')}
options={options}
value={field?.componentProps?.fieldNames?.label}
onChange={(label) => {
@ -201,6 +201,9 @@ FormItem.Designer = () => {
{collectionField && <SchemaSettings.Divider />}
<SchemaSettings.Remove
removeParentsIfNoChildren
confirm={{
title: t('Delete field')
}}
breakRemoveOn={{
'x-component': 'Grid',
}}

View File

@ -80,6 +80,7 @@ export const KanbanCardDesigner = (props: any) => {
return;
}
const dn = createDesignable({
t,
api,
refresh,
current: gridSchema,

View File

@ -79,6 +79,7 @@ export const KanbanCardDesigner = (props: any) => {
return;
}
const dn = createDesignable({
t,
api,
refresh,
current: gridSchema,

View File

@ -1,13 +1,15 @@
import { useField } from '@formily/react';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
export const MarkdownVoidDesigner = () => {
const field = useField();
const { t } = useTranslation();
return (
<GeneralSchemaDesigner>
<SchemaSettings.Item
title={'编辑'}
title={t('Edit markdown')}
onClick={() => {
field.editable = true;
}}

View File

@ -308,6 +308,7 @@ export const MenuDesigner = () => {
}
const current = findByUid(menuSchema, uid);
const dn = createDesignable({
t,
api,
refresh,
current,

View File

@ -10,6 +10,7 @@ import {
import { Menu as AntdMenu } from 'antd';
import React, { createContext, useContext, useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
import { useTranslation } from 'react-i18next';
import { createDesignable, DndContext, SortableItem, useDesignable, useDesigner } from '../..';
import { Icon, useAPIClient, useSchemaInitializer } from '../../../';
import { MenuDesigner } from './Menu.Designer';
@ -144,6 +145,7 @@ export const Menu: ComposedMenu = observer((props) => {
defaultOpenKeys: dOpenKeys,
...others
} = props;
const { t } = useTranslation();
const Designer = useDesigner();
const schema = useFieldSchema();
const { refresh } = useDesignable();
@ -300,8 +302,8 @@ export const Menu: ComposedMenu = observer((props) => {
{render({
style: { margin: 8 },
insert: (s) => {
console.log('createDesignable', s);
const dn = createDesignable({
t,
api,
refresh,
current: sideMenuSchema,

View File

@ -34,16 +34,16 @@ export const TableColumnDesigner = (props) => {
return (
<GeneralSchemaDesigner>
<SchemaSettings.ModalItem
title={t('Edit column title')}
title={t('Custom column title')}
schema={
{
type: 'object',
title: t('Edit column title'),
title: t('Custom column title'),
properties: {
title: {
title: t('Column title'),
// title: t('Column title'),
default: columnSchema?.title,
description: `${t('Original field title: ')}${collectionField?.uiSchema?.title}`,
description: `${t('Original title: ')}${collectionField?.uiSchema?.title}`,
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-component-props': {},
@ -97,6 +97,9 @@ export const TableColumnDesigner = (props) => {
breakRemoveOn={{
'x-component': 'Grid',
}}
confirm={{
title: t('Delete table column')
}}
/>
</GeneralSchemaDesigner>
);

View File

@ -2,12 +2,14 @@ import { DndContext as DndKitContext, DragEndEvent, DragOverlay, rectIntersectio
import { Props } from '@dnd-kit/core/dist/components/DndContext/DndContext';
import { observer } from '@formily/react';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useAPIClient } from '../../../';
import { createDesignable, useDesignable } from '../../hooks';
const useDragEnd = (props?: any) => {
const { refresh } = useDesignable();
const api = useAPIClient();
const { t } = useTranslation();
return (event: DragEndEvent) => {
const { active, over } = event;
@ -28,6 +30,7 @@ const useDragEnd = (props?: any) => {
}
const dn = createDesignable({
t,
api,
refresh,
current: overSchema,

View File

@ -1,10 +1,12 @@
import { HighlightOutlined } from '@ant-design/icons';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useDesignable } from '..';
import { PluginManager } from '../../plugin-manager';
export const DesignableSwitch = () => {
const { designable, setDesignable } = useDesignable();
const { t } = useTranslation();
const style = {};
if (designable) {
style['backgroundColor'] = '#f18b62';
@ -13,7 +15,7 @@ export const DesignableSwitch = () => {
<PluginManager.Toolbar.Item
selected={designable}
icon={<HighlightOutlined />}
title={'界面配置'}
title={t('UI Editor')}
style={style}
onClick={() => {
setDesignable(!designable);

View File

@ -4,6 +4,7 @@ import { message } from 'antd';
import get from 'lodash/get';
import set from 'lodash/set';
import React, { useContext } from 'react';
import { useTranslation } from 'react-i18next';
import { APIClient, useAPIClient } from '../../api-client';
import { SchemaComponentContext } from '../context';
@ -12,6 +13,8 @@ interface CreateDesignableProps {
api?: APIClient;
refresh?: () => void;
onSuccess?: any;
i18n?: any;
t?: any;
}
export function createDesignable(options: CreateDesignableProps) {
@ -90,6 +93,8 @@ export const splitWrapSchema = (wrapped: Schema, schema: ISchema) => {
return [schema1, schema2];
};
const translate = (v?: any) => v;
export class Designable {
current: Schema;
options: CreateDesignableProps;
@ -101,7 +106,7 @@ export class Designable {
}
loadAPIClientEvents() {
const { refresh, api } = this.options;
const { refresh, api, t = translate } = this.options;
if (!api) {
return;
}
@ -125,7 +130,7 @@ export class Designable {
});
}
onSuccess?.();
message.success('配置保存成功!', 0.2);
message.success(t('Saved successfully'), 0.2);
});
this.on('patch', async ({ schema }) => {
refresh();
@ -139,7 +144,7 @@ export class Designable {
...schema,
},
});
message.success('配置保存成功!', 0.2);
message.success(t('Saved successfully'), 0.2);
});
this.on('remove', async ({ removed }) => {
refresh();
@ -150,7 +155,7 @@ export class Designable {
url: `/uiSchemas:remove/${removed['x-uid']}`,
method: 'post',
});
message.success('配置保存成功!', 0.2);
message.success(t('Saved successfully'), 0.2);
});
}
@ -508,7 +513,8 @@ export function useDesignable() {
const field = useField();
const fieldSchema = useFieldSchema();
const api = useAPIClient();
const dn = createDesignable({ api, refresh, current: fieldSchema });
const { t } = useTranslation();
const dn = createDesignable({ t, api, refresh, current: fieldSchema });
dn.loadAPIClientEvents();
return {
dn,

View File

@ -14,42 +14,42 @@ export const BlockInitializers = {
{
key: 'table',
type: 'item',
title: 'Table',
title: '{{t("Table")}}',
component: 'TableBlockInitializer',
},
{
key: 'form',
type: 'item',
title: 'Form',
title: '{{t("Form")}}',
component: 'FormBlockInitializer',
},
{
key: 'details',
type: 'item',
title: 'Details',
title: '{{t("Details")}}',
component: 'DetailsBlockInitializer',
},
{
key: 'calendar',
type: 'item',
title: 'Calendar',
title: '{{t("Calendar")}}',
component: 'CalendarBlockInitializer',
},
{
key: 'kanban',
type: 'item',
title: 'Kanban',
title: '{{t("Kanban")}}',
component: 'KanbanBlockInitializer',
},
],
},
{
type: 'itemGroup',
title: 'Media',
title: '{{t("Media")}}',
children: [
{
type: 'item',
title: 'Markdown',
title: '{{t("Markdown")}}',
component: 'MarkdownBlockInitializer',
},
],

View File

@ -1,19 +1,19 @@
// 日历的操作配置
export const CalendarActionInitializers = {
title: 'Configure actions',
title: '{{t("Configure actions")}}',
icon: 'SettingOutlined',
style: { marginLeft: 8 },
items: [
{
type: 'itemGroup',
title: 'Enable actions',
title: '{{t("Enable actions")}}',
children: [
{
type: 'item',
title: 'Today',
title: '{{t("Today")}}',
component: 'ActionInitializer',
schema: {
title: 'Today',
title: '{{t("Today")}}',
'x-component': 'CalendarV2.Today',
'x-action': `calendar:today`,
'x-align': 'left',
@ -21,10 +21,10 @@ export const CalendarActionInitializers = {
},
{
type: 'item',
title: 'Turn pages',
title: '{{t("Turn pages")}}',
component: 'ActionInitializer',
schema: {
title: 'Turn pages',
title: '{{t("Turn pages")}}',
'x-component': 'CalendarV2.Nav',
'x-action': `calendar:nav`,
'x-align': 'left',
@ -32,10 +32,10 @@ export const CalendarActionInitializers = {
},
{
type: 'item',
title: 'Title',
title: '{{t("Title")}}',
component: 'ActionInitializer',
schema: {
title: 'Title',
title: '{{t("Title")}}',
'x-component': 'CalendarV2.Title',
'x-action': `calendar:title`,
'x-align': 'left',
@ -43,10 +43,10 @@ export const CalendarActionInitializers = {
},
{
type: 'item',
title: 'Select view',
title: '{{t("Select view")}}',
component: 'ActionInitializer',
schema: {
title: 'Select view',
title: '{{t("Select view")}}',
'x-component': 'CalendarV2.ViewSelect',
'x-action': `calendar:viewSelect`,
'x-align': 'right',

View File

@ -13,22 +13,22 @@ export const CreateFormBlockInitializers = (props: any) => {
items={[
{
type: 'itemGroup',
title: '{{ t("Data blocks") }}',
title: '{{t("Data blocks")}}',
children: [
{
type: 'item',
title: '{{ t("Form") }}',
title: '{{t("Form")}}',
component: 'CreateFormBlockInitializer',
},
],
},
{
type: 'itemGroup',
title: 'Media',
title: '{{t("Media")}}',
children: [
{
type: 'item',
title: 'Markdown',
title: '{{t("Markdown")}}',
component: 'MarkdownBlockInitializer',
},
],

View File

@ -1,3 +1,4 @@
// 表单的操作配置
export const FormActionInitializers = {
title: '{{t("Configure actions")}}',
@ -15,7 +16,7 @@ export const FormActionInitializers = {
'x-action-settings': {
initialValues: {},
onSuccess: {
successMessage: 'Submitted successfully!',
successMessage: '{{t("Submitted successfully")}}',
},
},
},
@ -40,7 +41,7 @@ export const FormActionInitializers = {
'x-action-settings': {
initialValues: {},
onSuccess: {
successMessage: 'Submitted successfully!',
successMessage: '{{t("Submitted successfully")}}',
},
},
'x-component-props': {

View File

@ -28,35 +28,35 @@ export const RecordBlockInitializers = (props: any) => {
items={[
{
type: 'itemGroup',
title: '当前数据区块',
title: '{{t("Current record blocks")}}',
children: [
{
key: 'details',
type: 'item',
title: 'Details',
title: '{{t("Details")}}',
component: 'RecordReadPrettyFormBlockInitializer',
},
{
key: 'form',
type: 'item',
title: 'Form',
title: '{{t("Form")}}',
component: 'RecordFormBlockInitializer',
},
],
},
{
type: 'itemGroup',
title: '关系数据区块',
title: '{{t("Relationship blocks")}}',
children: useRelationFields(),
},
{
type: 'itemGroup',
title: 'Media',
title: '{{t("Media")}}',
children: [
{
key: 'markdown',
type: 'item',
title: 'Markdown',
title: '{{t("Markdown")}}',
component: 'MarkdownBlockInitializer',
},
],

View File

@ -24,11 +24,11 @@ export const RecordFormBlockInitializers = (props: any) => {
},
{
type: 'itemGroup',
title: 'Media',
title: '{{t("Media")}}',
children: [
{
type: 'item',
title: 'Markdown',
title: '{{t("Markdown")}}',
component: 'MarkdownBlockInitializer',
},
],

View File

@ -25,6 +25,7 @@ export const TableActionColumnInitializers = (props: any) => {
return;
}
const dn = createDesignable({
t,
api,
refresh,
current: spaceSchema,

View File

@ -25,7 +25,7 @@ export const TableSelectorInitializers = (props: any) => {
},
{
type: 'itemGroup',
title: 'Media',
title: t('Media'),
children: [
{
type: 'item',

View File

@ -145,6 +145,7 @@ export const DetailsBlockInitializer = (props) => {
export const CalendarBlockInitializer = (props) => {
const { insert } = props;
const { t } = useTranslation();
const { getCollection } = useCollectionManager();
const options = useContext(SchemaOptionsContext);
return (
@ -170,7 +171,7 @@ export const CalendarBlockInitializer = (props) => {
value: field.name,
};
});
const values = await FormDialog('创建日历区块', () => {
const values = await FormDialog(t('Create calendar block'), () => {
return (
<SchemaComponentOptions scope={options.scope} components={{ ...options.components }}>
<FormLayout layout={'vertical'}>
@ -178,14 +179,14 @@ export const CalendarBlockInitializer = (props) => {
schema={{
properties: {
title: {
title: '标题字段',
title: t('Title field'),
enum: stringFields,
required: true,
'x-component': 'Select',
'x-decorator': 'FormItem',
},
start: {
title: '开始日期字段',
title: t('Start date field'),
enum: dateFields,
required: true,
default: 'createdAt',
@ -193,7 +194,7 @@ export const CalendarBlockInitializer = (props) => {
'x-decorator': 'FormItem',
},
end: {
title: '结束日期字段',
title: t('End date field'),
enum: dateFields,
'x-component': 'Select',
'x-decorator': 'FormItem',
@ -222,6 +223,7 @@ export const CalendarBlockInitializer = (props) => {
export const KanbanBlockInitializer = (props) => {
const { insert } = props;
const { t } = useTranslation();
const { getCollection } = useCollectionManager();
const options = useContext(SchemaOptionsContext);
const api = useAPIClient();
@ -244,7 +246,7 @@ export const KanbanBlockInitializer = (props) => {
},
};
});
const values = await FormDialog('创建看板区块', () => {
const values = await FormDialog(t('Create kanban block'), () => {
return (
<SchemaComponentOptions scope={options.scope} components={{ ...options.components }}>
<FormLayout layout={'vertical'}>
@ -252,7 +254,7 @@ export const KanbanBlockInitializer = (props) => {
schema={{
properties: {
groupField: {
title: '分组字段',
title: t('Group field'),
enum: fields,
required: true,
'x-component': 'Select',

View File

@ -164,7 +164,7 @@ export const useRecordCollectionDataSourceItems = (componentName) => {
{
type: 'item',
name: collection.name,
title: '空白区块',
title: t('Blank block'),
},
{
type: 'divider',
@ -173,7 +173,7 @@ export const useRecordCollectionDataSourceItems = (componentName) => {
key: `${componentName}_table_subMenu_${index}_copy`,
type: 'subMenu',
name: 'copy',
title: '复制模板',
title: t('Duplicate template'),
children: templates.map((template) => {
const templateName =
template?.componentName === 'ReadPrettyFormItem' ? `${template?.name} ${t('(Fields only)')}` : template?.name;
@ -182,7 +182,7 @@ export const useRecordCollectionDataSourceItems = (componentName) => {
mode: 'copy',
name: collection.name,
template,
title: templateName || '未命名',
title: templateName || t('Untitled'),
};
}),
},
@ -190,7 +190,7 @@ export const useRecordCollectionDataSourceItems = (componentName) => {
key: `${componentName}_table_subMenu_${index}_ref`,
type: 'subMenu',
name: 'ref',
title: '引用模板',
title: t('Reference template'),
children: templates.map((template) => {
const templateName =
template?.componentName === 'ReadPrettyFormItem' ? `${template?.name} ${t('(Fields only)')}` : template?.name;
@ -199,7 +199,7 @@ export const useRecordCollectionDataSourceItems = (componentName) => {
mode: 'reference',
name: collection.name,
template,
title: templateName || '未命名',
title: templateName || t('Untitled'),
};
}),
},
@ -214,7 +214,7 @@ export const useCollectionDataSourceItems = (componentName) => {
{
key: 'tableBlock',
type: 'itemGroup',
title: t('Select data source'),
title: t('Select collection'),
children: collections
?.filter((item) => !item.inherit)
?.map((item, index) => {
@ -237,7 +237,7 @@ export const useCollectionDataSourceItems = (componentName) => {
{
type: 'item',
name: item.name,
title: '空白区块',
title: t('Blank block'),
},
{
type: 'divider',
@ -246,7 +246,7 @@ export const useCollectionDataSourceItems = (componentName) => {
key: `${componentName}_table_subMenu_${index}_copy`,
type: 'subMenu',
name: 'copy',
title: '复制模板',
title: t('Duplicate template'),
children: templates.map((template) => {
const templateName =
template?.componentName === 'FormItem' ? `${template?.name} ${t('(Fields only)')}` : template?.name;
@ -255,7 +255,7 @@ export const useCollectionDataSourceItems = (componentName) => {
mode: 'copy',
name: item.name,
template,
title: templateName || '未命名',
title: templateName || t('Untitled'),
};
}),
},
@ -263,7 +263,7 @@ export const useCollectionDataSourceItems = (componentName) => {
key: `${componentName}_table_subMenu_${index}_ref`,
type: 'subMenu',
name: 'ref',
title: '引用模板',
title: t('Reference template'),
children: templates.map((template) => {
const templateName =
template?.componentName === 'FormItem' ? `${template?.name} ${t('(Fields only)')}` : template?.name;
@ -272,7 +272,7 @@ export const useCollectionDataSourceItems = (componentName) => {
mode: 'reference',
name: item.name,
template,
title: templateName || '未命名',
title: templateName || t('Untitled'),
};
}),
},
@ -657,7 +657,7 @@ export const createCalendarBlockSchema = (options) => {
properties: {
tab1: {
type: 'void',
title: '详情',
title: '{{t("Details")}}',
'x-component': 'Tabs.TabPane',
'x-designer': 'Tabs.Designer',
'x-component-props': {},
@ -725,7 +725,7 @@ export const createKanbanBlockSchema = (options) => {
},
},
cardAdder: {
title: '添加卡片',
title: '{{t("Add new")}}',
type: 'void',
'x-designer': 'Action.Designer',
'x-designer-props': {
@ -780,7 +780,7 @@ export const createKanbanBlockSchema = (options) => {
properties: {
tab1: {
type: 'void',
title: '详情',
title: '{{t("Details")}}',
'x-component': 'Tabs.TabPane',
'x-designer': 'Tabs.Designer',
'x-component-props': {},

View File

@ -52,7 +52,7 @@ export const GeneralSchemaDesigner = (props: any) => {
<div className={classNames('general-schema-designer-title', titleCss)}>
<Space size={2}>
<span className={'title-tag'}>{compile(title)}</span>
{template && <span className={'title-tag'}>: {templateName || '未命名'}</span>}
{template && <span className={'title-tag'}>{t('Reference template')}: {templateName || t('Untitled')}</span>}
</Space>
</div>
)}

View File

@ -1,8 +1,10 @@
import { css } from '@emotion/css';
import { FormDialog, FormItem, FormLayout, Input } from '@formily/antd';
import { GeneralField } from '@formily/core';
import { ISchema, Schema, SchemaOptionsContext } from '@formily/react';
import { uid } from '@formily/shared';
import { Dropdown, Menu, MenuItemProps, Modal, Select, Switch } from 'antd';
import classNames from 'classnames';
import React, { createContext, useContext, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
@ -87,6 +89,15 @@ export const SchemaSettings: React.FC<SchemaSettingsProps> & SchemaSettingsNeste
setVisible(visible);
}}
overlay={<Menu>{props.children}</Menu>}
overlayClassName={classNames(
'nb-schema-initializer-button-overlay',
css`
.ant-dropdown-menu-item-group-list {
max-height: 40vh;
overflow: auto;
}
`,
)}
>
{typeof title === 'string' ? <span>{title}</span> : title}
</Dropdown>
@ -134,7 +145,7 @@ SchemaSettings.Template = (props) => {
<SchemaSettings.Item
onClick={async () => {
setVisible(false);
const values = await FormDialog('Save as template', () => {
const values = await FormDialog(t('Save as template'), () => {
return (
<FormLayout layout={'vertical'}>
<SchemaComponent
@ -143,7 +154,7 @@ SchemaSettings.Template = (props) => {
type: 'object',
properties: {
name: {
title: '模板名称',
title: t('Template name'),
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
@ -155,6 +166,7 @@ SchemaSettings.Template = (props) => {
);
}).open({});
const sdn = createDesignable({
t,
api,
refresh: dn.refresh.bind(dn),
current: fieldSchema.parent,
@ -231,6 +243,7 @@ SchemaSettings.FormItemTemplate = (props) => {
const schema = await copyTemplateSchema(template);
const templateSchema = findBlockTemplateSchema(fieldSchema);
const sdn = createDesignable({
t,
api,
refresh: dn.refresh.bind(dn),
current: templateSchema.parent,
@ -275,7 +288,7 @@ SchemaSettings.FormItemTemplate = (props) => {
type: 'object',
properties: {
name: {
title: '模板名称',
title: t('Template name'),
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
@ -287,6 +300,7 @@ SchemaSettings.FormItemTemplate = (props) => {
);
}).open({});
const sdn = createDesignable({
t,
api,
refresh: dn.refresh.bind(dn),
current: gridSchema.parent,

View File

@ -256,27 +256,23 @@ export class PluginACL extends Plugin {
records: [
{
name: 'root',
title: 'Root',
title: '{{t("Root")}}',
hidden: true,
},
{
name: 'admin',
title: 'Admin',
title: '{{t("Admin")}}',
allowConfigure: true,
allowNewMenu: true,
strategy: { actions: ['create', 'export', 'view', 'update', 'destroy'] },
},
{
name: 'member',
title: 'Member',
title: '{{t("Member")}}',
allowNewMenu: true,
strategy: { actions: ['view', 'update:own', 'destroy:own', 'create'] },
default: true,
},
{
name: 'anonymous',
title: 'Anonymous',
},
],
});
const rolesResourcesScopes = this.app.db.getRepository('rolesResourcesScopes');