diff --git a/packages/command-services/lib/locale/locale.service.ts b/packages/command-services/lib/locale/locale.service.ts index 14ef65efc35745c3a6050c2885c89ff17729353e..aa0828bd2584ee2f18f6d6e0b45e1f6da225123c 100644 --- a/packages/command-services/lib/locale/locale.service.ts +++ b/packages/command-services/lib/locale/locale.service.ts @@ -9,12 +9,12 @@ export class LocaleService { public static async setup(config: LocaleConfig) { LocaleService.config = { ...DEFAULT_LOCALE_CONFIG, ...config }; const locale = LocaleService.config.locale; - const uri = `${LocaleService.config.uri}/${locale}.json`; + const uri = `${LocaleService.config.uri}/${locale}.json?version=${new Date().valueOf()}`; try { const resources = await LocaleService.loadResource(uri); LocaleService.localeResources[locale] = resources; } catch (error) { - const uri = `${LocaleService.config.uri}/${DEFAULT_LOCALE}.json`; + const uri = `${LocaleService.config.uri}/${DEFAULT_LOCALE}.json?version=${new Date().valueOf()}`; const resources = await LocaleService.loadResource(uri); LocaleService.localeResources[locale] = resources; } diff --git a/packages/command-services/lib/locale/locales/en.json b/packages/command-services/lib/locale/locales/en.json index 20d66f856d107c691eaefceffc1829bc3dd67573..51a2bb2f2e3ecbbd93b56ac778d6cfa40545ed31 100644 --- a/packages/command-services/lib/locale/locales/en.json +++ b/packages/command-services/lib/locale/locales/en.json @@ -3,24 +3,24 @@ "no": "No", "confirm": "Confirm", "cancel": "Cancel", - "saveSuccess": "Successfully saved!", + "saveSuccess": "Successfully saved.", "saveFailed": "Save failed!", - "deleteSuccess": "Successfully deleted!", - "deleteFaild": "Failed to delete!", + "deleteSuccess": "Successfully deleted.", + "deleteFaild": "Failed to delete.", "confirmDeletion": "Confirm deletion?", "confirmClosing": "There is unsaved data. Do you want to continue closing?", "confirmCancel": "Exist unsaved change,Confirm to cancel?", "unauthorized": "Your login has expired, please login again.", - "noDataExist": "Data does not exist to access the edit state!", - "pleaseSelectDeleteData": "Please select the data to delete!", - "pleaseSelectParentNode": "Please select parent node!", - "deleteChildFirst": "Please delete the child nodes first!", - "pleaseSelectDetailFormData": "Please select a detail form data first!", - "pleaseSelectEditData": "Please select the data you want to edit!", - "pleaseSelectViewData": "Please select the data you want to view!", + "noDataExist": "Data does not exist to access the edit state.", + "pleaseSelectDeleteData": "Please select the data to delete.", + "pleaseSelectParentNode": "Please select parent node.", + "deleteChildFirst": "Please delete the child nodes first.", + "pleaseSelectDetailFormData": "Please select a detail form data first.", + "pleaseSelectEditData": "Please select the data you want to edit.", + "pleaseSelectViewData": "Please select the data you want to view.", "hasChangeCheckFaild": "An error occurred while checking the page for unsaved changes! Please make sure the data has been saved. Do you want to continue closing?", - "pleaseSelectCopyData": "Please select the data you want to copy!", - "unallowEmptyBizBillId": "Please select the data you want to print!", + "pleaseSelectCopyData": "Please select the data you want to copy.", + "unallowEmptyBizBillId": "Please select the data you want to print.", "required": "Please input '$property'", "maxLength": "'$property' should not longer than $constraint1", "minLength": "'$property' should not shorter than $constraint1 charactor`", @@ -32,10 +32,13 @@ "errorTypeVertifyMessages": "Error", "emptyTypeVertifyMessages": "Empty", "verifyMessageWithRowIndex": "row $constraint1", - "pleaseSelectUpdateRow":"Please select the row where you want to update the attachment!", - "pleaseUploadFirst":"Please upload attachment first!", - "pleaseSelectDownloadAttachment":"Please select the attachment you want to download!", - "noDownloadAttachment":"There are no attachments to download!", - "noAttachment":"There are no attachments to preview.", - "noParentData":"Please select the parent data!" + "pleaseSelectUpdateRow": "Please select the row where you want to update the attachment.", + "pleaseUploadFirst": "Please upload attachment first.", + "pleaseSelectDownloadAttachment": "Please select the attachment you want to download.", + "noDownloadAttachment": "There are no attachments to download.", + "noAttachment": "There are no attachments to preview.", + "noParentData": "Please select the parent data.", + "appOrFuncIdRequired": "No menu or application parameters are configured, please configure them in the designer.", + "validate": "'$property' calibration failed", + "dataPicking": "Failed to verify the expression before help" } \ No newline at end of file diff --git a/packages/command-services/lib/locale/locales/zh-CHS.json b/packages/command-services/lib/locale/locales/zh-CHS.json index 4f178def69d82f79186503270e6e8a55cd7b646a..5476841d7c24f6d898c09c241e7c42015bfc1000 100644 --- a/packages/command-services/lib/locale/locales/zh-CHS.json +++ b/packages/command-services/lib/locale/locales/zh-CHS.json @@ -37,5 +37,8 @@ "pleaseSelectDownloadAttachment":"请选择要下载的附件!", "noDownloadAttachment":"找不到要下载的附件!", "noAttachment":"请选择上级数据!", - "noParentData":"请选择上级数据!" + "noParentData":"请选择上级数据!", + "appOrFuncIdRequired":"未配置菜单或应用参数,请在设计器中配置。", + "validate": "'$property'校验不通过", + "dataPicking": "帮助前表达式校验不通过" } \ No newline at end of file diff --git a/packages/command-services/lib/locale/locales/zh-CHT.json b/packages/command-services/lib/locale/locales/zh-CHT.json index 1812c8beb59cb8595c9225dcfdd424a891ec8117..8f049c184937c5f2b1bffb528432f42cdb3448ec 100644 --- a/packages/command-services/lib/locale/locales/zh-CHT.json +++ b/packages/command-services/lib/locale/locales/zh-CHT.json @@ -37,5 +37,8 @@ "pleaseSelectDownloadAttachment":"請選擇要下載的附件!", "noDownloadAttachment":"找不到要下載的附件!", "noAttachment":"沒有可以預覽的附件。", - "noParentData":"請選擇上級數據!" + "noParentData":"請選擇上級數據!", + "appOrFuncIdRequired":"未配置菜單或應用參數,請在設計器中配置。", + "validate": "'$property'校驗不通過", + "dataPicking": "幫助前表達式校驗不通過" } \ No newline at end of file diff --git a/packages/command-services/lib/navigation.service.ts b/packages/command-services/lib/navigation.service.ts index 4d88e3b2ea704017d2bf52fd97c97f87ac4dc455..6b5067d54a2330ca899e81485a6ab04dfc16a737 100644 --- a/packages/command-services/lib/navigation.service.ts +++ b/packages/command-services/lib/navigation.service.ts @@ -5,6 +5,8 @@ import { AppType, BuildFrameworkTabIdOptions } from './types'; import lodash from 'lodash'; import { NavigationEventService } from './navigation-event.service'; import { NavigationHistoryService } from './navigation-history.service'; +import { FormNotifyService } from './form-notify.service'; +import { LocaleService } from './locale'; /** * 导航服务 */ @@ -19,7 +21,8 @@ export class NavigationService { private viewModel: ViewModel, private injector: Injector, private navigationEventService: NavigationEventService, - private navigationHistoryService: NavigationHistoryService + private navigationHistoryService: NavigationHistoryService, + private formNotifyService: FormNotifyService ) { } public set context(commandContext: CommandContext) { @@ -46,6 +49,10 @@ export class NavigationService { * @param destructuring 是否解构参数 */ public openMenu(tabId: string, funcId: string, params: any, reload?: boolean, enableRefresh?: any, tabName?: string, destructuring?: any) { + if (!funcId) { + this.formNotifyService.warning(LocaleService.translate('appOrFuncIdRequired')); + return Promise.reject(); + } let queryStringParams = this.buildParamMap(params); destructuring = this.convertToBoolean(destructuring, false); if (destructuring === true) { @@ -82,6 +89,10 @@ export class NavigationService { * @param destructuring 解构参数 */ public openMenu$(tabId: string, funcId: string, params: any, reload?: boolean, enableRefresh?: any, tabName?: string, destructuring?: any) { + if (!funcId) { + this.formNotifyService.warning(LocaleService.translate('appOrFuncIdRequired')); + return Promise.reject(); + } let queryStringParams = this.buildParamMap(params); destructuring = this.convertToBoolean(destructuring, false); if (destructuring === true) { @@ -119,6 +130,10 @@ export class NavigationService { * @param destructuring 解构参数 */ public openMenuWithDimension(tabId: string, funcId: string, params: any, enableRefresh?: any, dim1?: any, dim2?: any, tabName?: string, metadataId?: string, destructuring?: any) { + if (!funcId) { + this.formNotifyService.warning(LocaleService.translate('appOrFuncIdRequired')); + return Promise.reject(); + } if (metadataId === undefined || metadataId === null) { metadataId = ''; } @@ -171,6 +186,10 @@ export class NavigationService { */ public openApp(tabId: string, appId: string, appEntrance: string, params: any, reload?: boolean, tabName?: string, enableRefresh?: any, destructuring?: any) { + if (!appId) { + this.formNotifyService.warning(LocaleService.translate('appOrFuncIdRequired')); + return Promise.reject(); + } let queryStringParams = this.buildParamMap(params); destructuring = this.convertToBoolean(destructuring, false); if (destructuring === true) { @@ -205,6 +224,10 @@ export class NavigationService { * @param destructuring 解构参数 */ public openApp$(tabId: string, appId: string, appEntrance: string, params: any, reload?: boolean, tabName?: string, enableRefresh?: any, destructuring?: any) { + if (!appId) { + this.formNotifyService.warning(LocaleService.translate('appOrFuncIdRequired')); + return Promise.reject(); + } let queryStringParams = this.buildParamMap(params); destructuring = this.convertToBoolean(destructuring, false); if (destructuring === true) { diff --git a/packages/command-services/lib/providers.ts b/packages/command-services/lib/providers.ts index 011ff089a84d4f633b9de6d90a529a6ce7a5c215..f1653cd52098a12abb4f730d396a32c0ef405ee7 100644 --- a/packages/command-services/lib/providers.ts +++ b/packages/command-services/lib/providers.ts @@ -119,7 +119,7 @@ const commandServiceProviders: StaticProvider[] = [ { provide: TemplateService, useClass: TemplateService, deps: [ViewModel] }, { provide: PrintService, useClass: PrintService, deps: [CloudPrintService, FormNotifyService] }, // 导航相关服务 - { provide: NavigationService, useClass: NavigationService, deps: [RuntimeFrameworkService, QuerystringService, ViewModel, Injector, NavigationEventService, NavigationHistoryService] }, + { provide: NavigationService, useClass: NavigationService, deps: [RuntimeFrameworkService, QuerystringService, ViewModel, Injector, NavigationEventService, NavigationHistoryService, FormNotifyService] }, // 参数解析服务 { provide: ParamService, useClass: ParamService, deps: [QuerystringService, RuntimeFrameworkService] }, { provide: ApplicationParamService, useClass: ApplicationParamService, deps: [ParamService, RuntimeFrameworkService, ViewModel] }, diff --git a/packages/devkit/lib/expression/expression-registry.ts b/packages/devkit/lib/expression/expression-registry.ts index f729dcf71a74c98f66456e623f5fc2a917c8a32b..4aec4402831c55697c5845e51bb9a8c82bcffd19 100644 --- a/packages/devkit/lib/expression/expression-registry.ts +++ b/packages/devkit/lib/expression/expression-registry.ts @@ -1,9 +1,13 @@ +import { Injector, Translate, TRANSLATE_TOKEN } from "../common"; +import { Locale } from "../i18n"; import { ViewModel, ViewModelState } from "../viewmodel/index"; import { DEFAULT_EXPRESSION_VALIDATE_MESSAGE, ExpressionConfig, ExpressionObject, ExpressionRule, ExpressionType } from "./types"; export class ExpressionRegistry { private expressionObjects: ExpressionObject[] | null = null; - constructor(private expressionConfigs: ExpressionConfig[], private viewModel: ViewModel) { + private translate: Translate; + constructor(private expressionConfigs: ExpressionConfig[], private viewModel: ViewModel, private injector: Injector) { + this.translate = this.injector.get(TRANSLATE_TOKEN); } public get expressions(): ExpressionObject[] { const expressions: ExpressionConfig[] = this.expressionConfigs; @@ -54,6 +58,7 @@ export class ExpressionRegistry { if (!(expressionType === ExpressionType.Validate || expressionType === ExpressionType.Required || expressionType === ExpressionType.DataPicking)) { return null; } - return DEFAULT_EXPRESSION_VALIDATE_MESSAGE['zh-CHS'][expressionType]; + const localeId = Locale.getLocaleId(); + return this.translate.transform(expressionType, DEFAULT_EXPRESSION_VALIDATE_MESSAGE[localeId][expressionType]); } } diff --git a/packages/devkit/lib/expression/providers.ts b/packages/devkit/lib/expression/providers.ts index d8c1ee58b9075ecae504b7b06189ab1ddb62beea..7015b7e0f7f79f6c57f385ba094a67dc7e4c85bc 100644 --- a/packages/devkit/lib/expression/providers.ts +++ b/packages/devkit/lib/expression/providers.ts @@ -16,6 +16,6 @@ export const expressionProviders: StaticProvider[] = [ { provide: ExpressionEventEmitter, useClass: ExpressionEventEmitter, deps: [ChangeObserverRegistry] }, { provide: ExpressionEngineProxy, useClass: ExpressionEngineProxy, deps: [CHANGE_HANDLERS_TOKEN, ExpressionEventEmitter, ExpressionRegistry, ExpressionDependencyResolverRegistry, ExpressionDependencyResolver, ExpressionExecutor, ExpressionResult, forwardRef(() => ViewModel)] }, { provide: ExpressionExecutor, useClass: ExpressionExecutor, deps: [] }, - { provide: ExpressionRegistry, useClass: ExpressionRegistry, deps: [EXPRESSIONS_TOKEN, forwardRef(() => ViewModel)] }, + { provide: ExpressionRegistry, useClass: ExpressionRegistry, deps: [EXPRESSIONS_TOKEN, forwardRef(() => ViewModel), Injector] }, { provide: ExpressionEvaluator, useClass: ExpressionEvaluator, deps: [forwardRef(() => ViewModel), ExpressionExecutor, ExpressionRegistry] } ]; diff --git a/packages/devkit/lib/store/entity-store/entity-data-loader.ts b/packages/devkit/lib/store/entity-store/entity-data-loader.ts index 292e6694750685a93626cb86d3d5df7bb0224d33..1701159e60040ea818391ab9004aad6a3755e5c7 100644 --- a/packages/devkit/lib/store/entity-store/entity-data-loader.ts +++ b/packages/devkit/lib/store/entity-store/entity-data-loader.ts @@ -45,11 +45,7 @@ class EntityDataLoader { const multiLanguage = fieldSchema.multiLanguage; const dataField = multiLanguage ? `${fieldSchema.name}_MULTILANGUAGE` : fieldSchema.name; const propName = fieldSchema.name; - if (multiLanguage) { - entity[propName] = entityData[dataField] || {}; - } else { - entity[propName] = entityData[dataField]; - } + entity[propName] = entityData[dataField]; }); } diff --git a/packages/devkit/lib/store/form/validation/types.ts b/packages/devkit/lib/store/form/validation/types.ts index 5de3c04ff27dc302f08d39d947e9ec8ba8f33b65..f989651567c9b777ba6a2bcada406a6f03bb3d38 100644 --- a/packages/devkit/lib/store/form/validation/types.ts +++ b/packages/devkit/lib/store/form/validation/types.ts @@ -5,6 +5,7 @@ interface ValidationRule { name: string; message?: string; + multiLanguage?: boolean; [key: string]: any; } diff --git a/packages/devkit/lib/store/form/validation/validators/required-validator.ts b/packages/devkit/lib/store/form/validation/validators/required-validator.ts index 3f3c4b4524e940e7a8abebf778f5fdd02fa636cb..0490b0de66e9385613bb6b51900a6e03df7787c0 100644 --- a/packages/devkit/lib/store/form/validation/validators/required-validator.ts +++ b/packages/devkit/lib/store/form/validation/validators/required-validator.ts @@ -20,7 +20,7 @@ class RequiredValidator extends BaseValidator { return null; } - if (ValidatorUtil.isEmptyInput(value)) { + if (ValidatorUtil.isEmptyInput(value, rule.multiLanguage)) { const name = rule.name; const message = rule.message || '输入的值不能为空'; const error = { name, message, actualValue: value }; diff --git a/packages/devkit/lib/store/form/validation/validators/validator-util.ts b/packages/devkit/lib/store/form/validation/validators/validator-util.ts index 4f18568ba644005fa3e3bf1afdd7e23fc8db8c8b..3b2f19d10aefa79bbef431e96513c02a265fb521 100644 --- a/packages/devkit/lib/store/form/validation/validators/validator-util.ts +++ b/packages/devkit/lib/store/form/validation/validators/validator-util.ts @@ -1,3 +1,4 @@ +import { Locale } from "../../../../i18n"; /** * 验证器工具类 @@ -14,11 +15,16 @@ class ValidatorUtil { /** * 是否为空输入 */ - public static isEmptyInput(value: any): boolean { + public static isEmptyInput(value: any, multiLanguage = false): boolean { if (value === null || value === undefined || value === '') { return true; } - + if (multiLanguage && typeof value === 'object') { + const currentLanguageValue = value[Locale.getLocaleId()]; + if (currentLanguageValue === null || currentLanguageValue === undefined || currentLanguageValue === '') { + return true; + } + } return false; } @@ -29,7 +35,7 @@ class ValidatorUtil { // 排除不是字符串、数字、布尔的值 if (typeof value !== 'string' && typeof value !== 'number' && typeof value !== 'boolean') { - return false + return false; } // 排除NaN diff --git a/packages/renderer/src/app.vue b/packages/renderer/src/app.vue index fe4ecc7b947392fd17aad29d23fafee2e4ec4bf5..67f7cadd19220372d230824ba7aba301d96d7b99 100644 --- a/packages/renderer/src/app.vue +++ b/packages/renderer/src/app.vue @@ -28,7 +28,7 @@ function resolveQuery(query: LocationQuery) { router.beforeResolve(async (to: RouteLocationNormalizedGeneric) => { const router = to.name; const localeQuery = devkitInjector.get(LocaleQuery, undefined); - LocaleService.setup({ locale: localeQuery.locale }); + await LocaleService.setup({ locale: localeQuery.locale }); const metadataLoader = devkitInjector.get(MetadataLoader, undefined); const languageListLoader = devkitInjector.get(LanguageListLoader); const resourceLoader = devkitInjector.get(ResourceLoader); diff --git a/packages/renderer/src/component-config-resolver/data-grid-component-config-resolver.ts b/packages/renderer/src/component-config-resolver/data-grid-component-config-resolver.ts index 1ec23c04bb55cbe704d3bfef562c774d92f9b41f..806d46622f1befbbb9ab282d88ea0899ae2ccbe6 100644 --- a/packages/renderer/src/component-config-resolver/data-grid-component-config-resolver.ts +++ b/packages/renderer/src/component-config-resolver/data-grid-component-config-resolver.ts @@ -4,7 +4,7 @@ import { ConfigResolver } from "../config"; import { isNil } from "lodash-es"; import { TemplateTransformService } from "../template-transformer"; import { ViewModel, ViewModelState } from "@farris/devkit-vue"; -import { GlobalTranslate } from "../i18n"; +import { GlobalTranslate, LanguageListManager } from "../i18n"; export class DataGridComponentConfigResolver extends ComponentConfigResolver { public type: string = 'data-grid'; @@ -14,7 +14,9 @@ export class DataGridComponentConfigResolver extends ComponentConfigResolver { private configResolver: ConfigResolver, private templateTransformService: TemplateTransformService, private viewModel: ViewModel, - private translate: GlobalTranslate, private formMetadataId: string + private translate: GlobalTranslate, + private formMetadataId: string, + private languageListManager: LanguageListManager ) { super(); } @@ -54,9 +56,11 @@ export class DataGridComponentConfigResolver extends ComponentConfigResolver { if (!isNil(visible)) { column.visible = this.configResolver.resolve(visible, id); } + this.resolveLoolupColumn(metadata, column); if (!editor) { return; } + this.resolveLanguageTextboxEditor(editor); const { readonly, required, disabled } = editor; editor.readonly = this.configResolver.resolve(readonly, id); editor.readonlyConfig = readonly; @@ -72,4 +76,28 @@ export class DataGridComponentConfigResolver extends ComponentConfigResolver { } return relatedComponent.id; } + private resolveLanguageTextboxEditor(editor: Record) { + const { type } = editor; + if (type === 'language-textbox') { + const languageList: any[] = []; + const languages = this.languageListManager.getLanguageList() || []; + languageList.push(...languages); + if (!languageList || languageList.length < 1) { + languageList.push(...[{ code: 'en', name: 'English' }, { code: 'zh-CHS', name: '中文简体' }]); + } + editor.languages = languageList; + } + } + private resolveLoolupColumn(viewSchema: Record, column: Record) { + const { editor, dictPicking } = column; + if (!editor || editor.type !== 'lookup') { + return; + } + if (dictPicking) { + const { id } = viewSchema; + const relatedComponent = this.formMetadataService.getRelatedComponent(id); + const viewModel = this.viewModel.getModule().getViewModel(relatedComponent.id); + editor.dictPicking = (payload: any) => (viewModel as any)[dictPicking]({ payload, schema: viewSchema }); + } + } } diff --git a/packages/renderer/src/component-config-resolver/providers.ts b/packages/renderer/src/component-config-resolver/providers.ts index 066cc35a767d6d903b50a87287c7a998432eb284..5d9107e3714a48cebe7d7760d8e53435b8fa6031 100644 --- a/packages/renderer/src/component-config-resolver/providers.ts +++ b/packages/renderer/src/component-config-resolver/providers.ts @@ -20,7 +20,7 @@ import { LanguageListManager, GlobalTranslate } from "../i18n"; export const componentConfigResolverProviders: StaticProvider[] = [ { provide: COMPONENT_CONFIG_RESOLVER_TOKEN, useClass: FormGroupComponentConfigResolver, deps: [FormMetadataService, ConfigResolver, LanguageListManager, GlobalTranslate, FORM_METADATA_ID_TOKEN], multi: true }, - { provide: COMPONENT_CONFIG_RESOLVER_TOKEN, useClass: DataGridComponentConfigResolver, deps: [FormMetadataService, ConfigResolver, TemplateTransformService, ViewModel, GlobalTranslate, FORM_METADATA_ID_TOKEN], multi: true }, + { provide: COMPONENT_CONFIG_RESOLVER_TOKEN, useClass: DataGridComponentConfigResolver, deps: [FormMetadataService, ConfigResolver, TemplateTransformService, ViewModel, GlobalTranslate, FORM_METADATA_ID_TOKEN, LanguageListManager], multi: true }, { provide: COMPONENT_CONFIG_RESOLVER_TOKEN, useClass: PageHeaderComponentConfigResolver, deps: [FormMetadataService, ConfigResolver, GlobalTranslate, FORM_METADATA_ID_TOKEN], multi: true }, { provide: COMPONENT_CONFIG_RESOLVER_TOKEN, useClass: TabPageComponentConfigResolver, deps: [FormMetadataService, ConfigResolver, GlobalTranslate, FORM_METADATA_ID_TOKEN], multi: true }, { provide: COMPONENT_CONFIG_RESOLVER_TOKEN, useClass: ResponseToolbarConfigResolver, deps: [FormMetadataService, ConfigResolver, GlobalTranslate, FORM_METADATA_ID_TOKEN], multi: true }, diff --git a/packages/renderer/src/component-effectors/form-group-required-effector.ts b/packages/renderer/src/component-effectors/form-group-required-effector.ts index c595493493414cbe66ca728b5ad9a18c80e715cc..428fc18faaa473aaaa25b991def2db5a8684797e 100644 --- a/packages/renderer/src/component-effectors/form-group-required-effector.ts +++ b/packages/renderer/src/component-effectors/form-group-required-effector.ts @@ -105,11 +105,12 @@ export class FormGroupRequiredEffector { const { name: controlId, validationRules } = controlConfig; const rules = validationRules.filter((rule: any) => rule.name !== 'required'); - const { required } = viewSchema.editor; + const { required, type } = viewSchema.editor; const isRequired = this.configResolver.resolve(required, viewSchema.id); if (isRequired) { - rules.push({ name: 'required', message: this.buildRequiredMessage(controlConfig) }); + const multiLanguage = type === 'language-textbox'; + rules.push({ name: 'required', message: this.buildRequiredMessage(controlConfig), multiLanguage }); } viewModel.formStore!.setControlRules(controlId, rules); diff --git a/packages/renderer/src/config-builders/form/validation-rule-creator.ts b/packages/renderer/src/config-builders/form/validation-rule-creator.ts index e13d2bfb452be8ee44a01f18d9b152e8a70edbfd..6758912aed97af351dcc36be0f1567b126e3cb54 100644 --- a/packages/renderer/src/config-builders/form/validation-rule-creator.ts +++ b/packages/renderer/src/config-builders/form/validation-rule-creator.ts @@ -37,7 +37,7 @@ class ValidationRuleCreator { * 必填 */ public required(controlNode: any): RequiredValidationRule | undefined { - const { required } = controlNode.editor; + const { required, type } = controlNode.editor; if (!this.isValidValue(required)) { return; } @@ -49,7 +49,7 @@ class ValidationRuleCreator { const displayName = this.getDisplayName(controlNode); const originalMessage = LocaleService.translate('required'); const message = originalMessage.replace(/\$property/g, displayName); - return { name: 'required', message }; + return { name: 'required', message, multiLanguage: 'language-textbox' === type }; } /** diff --git a/packages/renderer/src/expression-effectors/expression-required-effector.ts b/packages/renderer/src/expression-effectors/expression-required-effector.ts index 9cca4b4569ff93d7ca2cce9ee7005c9d0ef63533..2742f2f47200af7f9474b5e1b65b593535fab620 100644 --- a/packages/renderer/src/expression-effectors/expression-required-effector.ts +++ b/packages/renderer/src/expression-effectors/expression-required-effector.ts @@ -2,6 +2,7 @@ import { EffectOption, Effector, ENTITY_TEMPLATE, ExpressionObject, FormControlC import { ComponentConfigRegistry } from "../config"; import { FormMetadataService } from "../service"; import { compile, createVNode } from "vue"; +import { FieldResolver } from "../resolvers"; export class ExpressionRequiredEffector implements Effector { constructor( @@ -141,11 +142,14 @@ export class ExpressionRequiredEffector implements Effector { return propertyName && message && message.replace(/\$property/g, propertyName) || undefined; } private buildValidationRules(result: boolean, validationRules: ValidationRule[], expressionObject: ExpressionObject, controlConfig?: FormControlConfig | null): ValidationRule[] { + const entitySchema = this.formMetadataService.getEntity(); + const resolvedField = FieldResolver.resolve(entitySchema, expressionObject.fieldId); validationRules = validationRules.filter((rule) => rule.name !== 'required'); if (result === true) { validationRules?.splice(0, 0, { name: 'required', message: this.formatMessage(expressionObject.message, controlConfig?.displayName) || undefined, + multiLanguage: resolvedField?.multiLanguage }); } return validationRules; diff --git a/packages/renderer/src/i18n/resource-loader.ts b/packages/renderer/src/i18n/resource-loader.ts index 32f1bcdd5ff4cd1e7e085afe7de908ba820a410f..0924bbd253239cec6966ff31f2cb6634f723808d 100644 --- a/packages/renderer/src/i18n/resource-loader.ts +++ b/packages/renderer/src/i18n/resource-loader.ts @@ -3,6 +3,7 @@ import { MetadataManager, ResourceMetadataDataService } from "../metadata"; import { LanguageListManager } from "./language-list-manager"; import { Metadata } from "../types"; import { ResourceManager } from "./resource-manager"; +import { FormMetadataQuery } from "../service"; export class ResourceLoader { private metadataManager: MetadataManager; @@ -17,7 +18,7 @@ export class ResourceLoader { this.httpClient = this.injector.get(HttpClient); this.resourceManager = this.injector.get(ResourceManager); } - public loadByProjectPath(formMetadataId: string, projectPath: string) { + public async loadByProjectPath(formMetadataId: string, projectPath: string) { const metadata = this.metadataManager.getMetadataCache(formMetadataId); const formMetadata = metadata.form; const resourceMetadatas = formMetadata.refs && formMetadata.refs.filter((ref: any) => { @@ -26,12 +27,13 @@ export class ResourceLoader { const resourceMetadataPromise = resourceMetadatas.map((id: string) => { return this.loadResourceMetadataByPath(projectPath, id); }); + await this.loadExternalMetadataByPath(formMetadata); return Promise.all(resourceMetadataPromise).then((resourceMetadatas) => { if (!resourceMetadatas || resourceMetadatas.length < 1) { return; } const resourceMetadata = resourceMetadatas.pop(); - const resources: any[] = resourceMetadata.content.StringResources; + const resources: any[] = resourceMetadata?.content?.StringResources; if (!resources) { return; } @@ -43,7 +45,7 @@ export class ResourceLoader { this.resourceManager.setResourceCache(formMetadataId, resourceObject); }); } - public loadByMetadataId(formMetadataId: string) { + public async loadByMetadataId(formMetadataId: string) { const metadata = this.metadataManager.getMetadataCache(formMetadataId); const formMetadata = metadata.form; const { relativePath, code } = formMetadata; @@ -52,14 +54,37 @@ export class ResourceLoader { const su = paths[1]; const projectName = paths[3]; const basePath = `/apps/${app}/${su}/web/${projectName}/${code}/i18n`.toLowerCase(); - const path = `${basePath}/${this.languageListManager.getLanguageCode()}.json`; + const path = `${basePath}/${this.languageListManager.getLanguageCode()}.json?version=${new Date().valueOf()}`; + await this.loadExternalMetadataById(formMetadata); return this.httpClient.get(path, {}).then((data: any) => { this.resourceManager.setResourceCache(formMetadataId, data); return data; - }).catch(()=>{ + }).catch(() => { console.warn('国际化资源加载失败!'); }); } + private async loadExternalMetadataByPath(formMetadata: any) { + const formMetadataQuery = new FormMetadataQuery(formMetadata); + const externalFormInfos = formMetadataQuery.getExternalFormInfos(); + + for (const externalFormInfo of externalFormInfos) { + await this.loadByProjectPath(externalFormInfo.id, externalFormInfo.projectPath); + this.correctExternalFormCode(externalFormInfo); + } + } + private async loadExternalMetadataById(formMetadata: any){ + const formMetadataQuery = new FormMetadataQuery(formMetadata); + const externalFormInfos = formMetadataQuery.getExternalFormInfos(); + + for (const externalFormInfo of externalFormInfos) { + await this.loadByMetadataId(externalFormInfo.id); + this.correctExternalFormCode(externalFormInfo); + } + } + private correctExternalFormCode(externalFormInfo: any) { + const externalMetadata = this.metadataManager.getMetadataCache(externalFormInfo.id); + externalMetadata.form.content.module.externalContainerId = externalFormInfo.externalContainerId; + } private loadResourceMetadataByPath(projectPath: string, metadataId: string) { const resourceMetadata = this.resourceMetadataDataService.loadByProjectPath(projectPath, metadataId); const languageCode = this.languageListManager.getLanguageCode(); diff --git a/packages/renderer/src/i18n/transformer/data-grid-i18n-transformer.ts b/packages/renderer/src/i18n/transformer/data-grid-i18n-transformer.ts index b22fc8feb0b87c8be7dea4a9997b1ec7d36b70d7..cae00cfccbd5c4af4bc9dc8875d2abd0ea3396e3 100644 --- a/packages/renderer/src/i18n/transformer/data-grid-i18n-transformer.ts +++ b/packages/renderer/src/i18n/transformer/data-grid-i18n-transformer.ts @@ -16,30 +16,50 @@ export class DataGridI18nTransformer { return; } columns.forEach((column: Record) => { - const { editor, visible, id: columnId, title, dataType, formatter } = column; + const { id: columnId, title } = column; column.title = this.translate.transform(this.formMetadataId, columnId, title); this.resolveEnumColumn(id, column); this.resolveBooleanColumn(id, column); + this.resolveEditorPlaceholder(column); }); } private resolveEnumColumn(schemaId: string, column: Record) { - const { title, dataType, formatter } = column; - if (dataType === 'enum' && formatter) { + const { dataType, formatter, id: columnId, editor } = column; + if (dataType !== 'enum') { + return; + } + if (formatter) { // eslint-disable-next-line prefer-destructuring const data: any[] = formatter.data; if (data && data.length > 0) { data.forEach((item: any) => { - item.name = this.translate.transform(this.formMetadataId, `${schemaId}/editor/data/${item.value}`, item.name); + item.name = this.translate.transform(this.formMetadataId, `${columnId}/editor/data/${item.value}`, item.name); + }); + } + } + if (editor) { + const { data } = editor; + if (data && data.length > 0) { + data.forEach((item: any) => { + item.name = this.translate.transform(this.formMetadataId, `${columnId}/editor/data/${item.value}`, item.name); }); } } } private resolveBooleanColumn(schemaId: string, column: Record) { - const { title, dataType, formatter } = column; + const { dataType, formatter } = column; if (dataType === 'boolean' && formatter) { const { trueText, falseText } = formatter; formatter.trueText = this.translate.transform(this.formMetadataId, `${schemaId}/formatter/trueText`, trueText); formatter.falseText = this.translate.transform(this.formMetadataId, `${schemaId}/formatter/falseText`, falseText); } } + private resolveEditorPlaceholder(column: Record) { + const { editor, id: columnId } = column; + if (!editor) { + return; + } + const { placeholder } = editor; + editor.placeholder = this.translate.transform(this.formMetadataId, `${columnId}/placeholder`, placeholder); + } } diff --git a/packages/renderer/src/i18n/transformer/form-group-i18n-transformer.ts b/packages/renderer/src/i18n/transformer/form-group-i18n-transformer.ts index 980b7592cebecd5553f2b55b70759f06df4618a5..1fc23fc01f0de2d87ef0508d4c579820c3f4c248 100644 --- a/packages/renderer/src/i18n/transformer/form-group-i18n-transformer.ts +++ b/packages/renderer/src/i18n/transformer/form-group-i18n-transformer.ts @@ -3,7 +3,51 @@ import { GlobalTranslate } from "../global-translate"; export class FormGroupI18nTransformer { constructor(private metadata: Record, private translate: GlobalTranslate, private formMetadataId: string) { } public transform() { - const { visible, editor = null, id, label } = this.metadata; + const { editor = null, id, label } = this.metadata; this.metadata.label = this.translate.transform(this.formMetadataId, id, label); + this.resolveEditorPlaceholder(this.metadata); + this.resolveEditorData(this.metadata); + this.resolveValidationMessage(this.metadata); + this.resolveLookupTitle(this.metadata); + } + private resolveEditorData(viewSchema: Record) { + const { editor, id } = viewSchema; + const { data } = editor || {}; + if (!Array.isArray(data) || data.length < 1) { + return; + } + data.forEach((item) => { + const { value, name } = item; + item.name = this.translate.transform(this.formMetadataId, `${id}/editor/data/${value}`, name); + }); + } + private resolveValidationMessage(viewSchema: Record) { + const { editor, id } = viewSchema; + const { formatValidation } = editor || {}; + if (!formatValidation) { + return; + } + const { message } = formatValidation; + formatValidation.message = this.translate.transform(this.formMetadataId, `${id}/formatValidation/message`, message); + } + private resolveEditorPlaceholder(viewSchema: Record) { + const { editor, id } = viewSchema; + if (!editor) { + return; + } + const { placeholder } = editor; + editor.placeholder = this.translate.transform(this.formMetadataId, `${id}/editor/placeholder`, placeholder); + } + private resolveLookupTitle(viewSchema: Record) { + const { editor, id } = viewSchema; + if (!editor) { + return; + } + const { type, dialog } = editor; + if (type !== 'lookup' || !dialog) { + return; + } + const { title } = dialog; + dialog.title = this.translate.transform(this.formMetadataId, `${id}/dialog/title`, title); } } diff --git a/packages/renderer/src/main.ts b/packages/renderer/src/main.ts index 92b4b5ba28098b4715aea93734b84883de4f632a..47a1e9b9fe75e9554d4d302ec3bc97672a33b92b 100644 --- a/packages/renderer/src/main.ts +++ b/packages/renderer/src/main.ts @@ -8,20 +8,23 @@ import router from './router'; import App from './app.vue'; import { i18nProviders } from './i18n'; +const localeId = localStorage.getItem('languageCode') || 'zh-CHS'; + const devkit = createDevkit({ providers: [ ...commandServiceDevkitProviders, ...metadataProviders, ...communicationProviders, ...i18nProviders - ] + ], + localeId }); const app = createApp(App); app.use(router); app.use(devkit); app.use(FarrisVue, { - locale: localStorage.getItem('languageCode') || 'zh-CHS', + locale: localeId, uri: '/platform/common/web/@farris/i18n/ui' }); app.mount('#app'); diff --git a/packages/renderer/src/validator/expression-validator.ts b/packages/renderer/src/validator/expression-validator.ts index a48afc8c226a77d85839ca60e852d56d7620cfa5..9d5b4884757dbf014f78f0feee73af4a4a67a416 100644 --- a/packages/renderer/src/validator/expression-validator.ts +++ b/packages/renderer/src/validator/expression-validator.ts @@ -1,7 +1,9 @@ import { Entity, ExpressionBindingType, ExpressionEvaluator, ExpressionObject, ExpressionRegistry, ExpressionType, FormControlConfig, ValidationRule, FormValidator, ViewModel, ViewModelState } from "@farris/devkit-vue"; +import { FieldResolver } from "../resolvers"; +import { FormMetadataService } from "../service"; -export class ExpressionValidator implements FormValidator{ - constructor(private viewModel: ViewModel){} +export class ExpressionValidator implements FormValidator { + constructor(private viewModel: ViewModel, private formMetadataService: FormMetadataService) { } validate(entities: Entity[]): void { const expressions = this.getValidationExpressions(); if (!expressions?.length) { @@ -69,12 +71,15 @@ export class ExpressionValidator implements FormValidator{ return viewModel.formStore ? viewModel.formStore.getControlRules(controlName) : viewModel.formArrayStore?.getControlRules(id, controlName); } private buildValidationRules(result: boolean, validationRules: ValidationRule[], expressionObject: ExpressionObject, controlConfig?: FormControlConfig | null): ValidationRule[] { + const entitySchema = this.formMetadataService.getEntity(); + const resolvedField = FieldResolver.resolve(entitySchema, expressionObject.fieldId); if (expressionObject.type === ExpressionType.Required) { validationRules = validationRules.filter((rule) => rule.name !== 'required'); if (result === true) { validationRules?.splice(0, 0, { name: 'required', message: this.formatMessage(expressionObject.message, controlConfig?.displayName) || undefined, + multiLanguage: resolvedField?.multiLanguage }); } return validationRules; diff --git a/packages/renderer/src/validator/providers.ts b/packages/renderer/src/validator/providers.ts index 14a8f8358426ece5e83ab360a0a78a02e4221dc8..0cbe31061fdb4c46414d22c4f3ceae2dc28dee98 100644 --- a/packages/renderer/src/validator/providers.ts +++ b/packages/renderer/src/validator/providers.ts @@ -7,5 +7,5 @@ import { ExpressionValidator } from "./expression-validator"; export const validatorProviders: StaticProvider[] = [ { provide: RESPONSE_REQUIRED_VALIDATOR_TOKEN, useClass: ResponseRequiredValidator, deps: [ComponentConfigRegistry, ComponentConfigDependencyResolveService, FormMetadataService, ConfigResolver, ViewModel] }, - { provide: EXPRESSION_VALIDATOR_TOKEN, useClass: ExpressionValidator, deps: [ViewModel] }, + { provide: EXPRESSION_VALIDATOR_TOKEN, useClass: ExpressionValidator, deps: [ViewModel, FormMetadataService] }, ]; diff --git a/packages/renderer/src/validator/response-required-validator.ts b/packages/renderer/src/validator/response-required-validator.ts index 942db935c17d9bec73ed9d6efc935f4f95e84761..760b154fb5ef835a94b8724e73b9163e3d77bd74 100644 --- a/packages/renderer/src/validator/response-required-validator.ts +++ b/packages/renderer/src/validator/response-required-validator.ts @@ -70,7 +70,7 @@ export class ResponseRequiredValidator implements FormValidator { if (rules?.some(rule => rule.type === 'required')) { continue; } - + const requiredRule = rules?.find((rule: any) => rule.name === 'required'); const newRules = rules?.filter(rule => rule.type !== 'required') || []; const controlConfig = viewModel.formArrayStore?.getConfigManager().getControlConfig(fieldId); if (!controlConfig) { @@ -78,7 +78,8 @@ export class ResponseRequiredValidator implements FormValidator { } newRules?.splice(0, 0, { name: 'required', - message: this.buildRequiredMessage(controlConfig) + message: this.buildRequiredMessage(controlConfig), + multiLanguage: requiredRule?.multiLanguage }); viewModel.formArrayStore?.setControlRules( entity.idValue, diff --git a/packages/ui-vue/components/locale/src/lib/locale.service.ts b/packages/ui-vue/components/locale/src/lib/locale.service.ts index 626ddae9a47e96005adb633608b83b40ca3cff8d..fb9f0ce45e44ed7a6e1cce379edecce03118800f 100644 --- a/packages/ui-vue/components/locale/src/lib/locale.service.ts +++ b/packages/ui-vue/components/locale/src/lib/locale.service.ts @@ -76,7 +76,7 @@ export class LocaleService { const { loadResource } = useResourceLoader(); // 优先加载远程资源 if (LocaleService.config.uri) { - const url = `${LocaleService.config.uri}/${locale}.json`; + const url = `${LocaleService.config.uri}/${locale}.json?version=${new Date().valueOf()}`; resources = await loadResource(url); } // 合并本地资源