) => {
+ for (const [key, value] of Object.entries(values)) {
+ setValueByName(key, value);
+ }
+ };
+
/** 视口滚动到指定字段 */
const scrollToField = (
name: string,
@@ -80,7 +91,7 @@ export default defineComponent({
const validateField = (fieldName: string, scrollToError = true) => {
const formData = getFilteredFormData();
const fieldData = formData[fieldName];
- const fieldSchema = props.schema.properties?.[fieldName];
+ const fieldSchema = schemaCore.value?.schema.properties?.[fieldName];
if (fieldData && fieldSchema) {
return validateSingle(fieldData, fieldSchema, fieldName).then((errors) => {
@@ -116,7 +127,7 @@ export default defineComponent({
const validate = (scrollToError = true) =>
validateAll({
formData: getFilteredFormData(),
- descriptor: props.schema.properties!,
+ descriptor: schemaCore.value?.schema.properties,
}).then((errors) => {
// scroll to error position
if (scrollToError && errors.length) {
@@ -179,7 +190,7 @@ export default defineComponent({
if (props.debug && process.env.NODE_ENV !== 'production') {
console.group('Action');
console.log('%cNext:', 'color: #47B04B; font-weight: 700;', value);
- console.log('%cConfig:', 'color: #1E80FF; font-weight: 700;', fieldConfigList.value);
+ console.log('%cConfig:', 'color: #1E80FF; font-weight: 700;', schemaRenderer.value);
console.groupEnd();
}
});
@@ -192,12 +203,12 @@ export default defineComponent({
return () => (
- {fieldConfigList.value.map((config) => (
+ {schemaRenderer.value?.map((scoped) => (
))}
diff --git a/packages/vue3-schema-form/src/types/basic.ts b/packages/vue3-schema-form/src/types/basic.ts
index 73c71f3..65f6eb7 100644
--- a/packages/vue3-schema-form/src/types/basic.ts
+++ b/packages/vue3-schema-form/src/types/basic.ts
@@ -9,3 +9,5 @@ export type ErrorMessage = {
name: string;
error: string[];
};
+
+export type Deps = Record;
diff --git a/packages/vue3-schema-form/src/types/props.ts b/packages/vue3-schema-form/src/types/props.ts
index 4109701..ebf83e6 100644
--- a/packages/vue3-schema-form/src/types/props.ts
+++ b/packages/vue3-schema-form/src/types/props.ts
@@ -17,6 +17,11 @@ export const schemaFormProps = {
type: Object as PropType,
default: () => ({}),
},
+ /** 依赖的外部状态,用于插值表达式中的 $deps */
+ deps: {
+ type: Object,
+ default: () => ({}),
+ },
/** 只读模式 */
readonly: Boolean,
/** 禁用模式 */
diff --git a/packages/vue3-schema-form/src/types/schema.ts b/packages/vue3-schema-form/src/types/schema.ts
index e52ec4f..a9bf74e 100644
--- a/packages/vue3-schema-form/src/types/schema.ts
+++ b/packages/vue3-schema-form/src/types/schema.ts
@@ -1,26 +1,21 @@
import { RuleItem } from 'async-validator';
-import { FormData } from '.';
export type ValueType = 'string' | 'object' | 'array' | 'number' | 'boolean' | 'date' | string;
-export type PayloadBoolean = boolean | ((data: FormData) => boolean);
-
-export type PayloadString = string | ((data: FormData) => string);
-
export interface SchemaBase {
type: ValueType;
title: string;
/** 是否必填,支持函数表达式 (formData)=> boolean */
- required: PayloadBoolean;
+ required: boolean;
placeholder: string;
/** 改变字段绑定值 用户并不希望纯展示的字段也出现在表单中,此时,使用 bind: false 可避免字段在提交时出现 */
// bind: false | string | string[];
/** 是否禁用,支持函数表达式 (formData)=> boolean */
- disabled: PayloadBoolean;
+ disabled: boolean;
/** 是否只读,支持函数表达式 (formData)=> boolean */
- readonly: PayloadBoolean;
+ readonly: boolean;
/** 是否隐藏,隐藏的字段不会在 formData 里透出,支持函数表达式 (formData)=> boolean */
- hidden: PayloadBoolean;
+ hidden: boolean;
/** Label 与 Field 的展示关系,row 表示并排展示,column 表示两排展示 */
displayType: 'row' | 'column' | string;
/** label宽度 !暂时不支持,如果有场景需要,可以考虑支持 */
@@ -40,4 +35,8 @@ export interface SchemaBase {
border: boolean;
}
+export type SchemaSegments = {
+ [P in keyof T]?: T[P] extends boolean | number ? T[P] | string : T[P];
+};
+
export type Schema = Partial;
diff --git a/packages/vue3-schema-form/src/utils/index.ts b/packages/vue3-schema-form/src/utils/index.ts
index 14eb305..e237f87 100644
--- a/packages/vue3-schema-form/src/utils/index.ts
+++ b/packages/vue3-schema-form/src/utils/index.ts
@@ -3,3 +3,4 @@ export * from './validate';
export * from './basic';
export * from './crate';
export * from './widget';
+export * from './resolver';
diff --git a/packages/vue3-schema-form/src/utils/resolver.ts b/packages/vue3-schema-form/src/utils/resolver.ts
new file mode 100644
index 0000000..d819f95
--- /dev/null
+++ b/packages/vue3-schema-form/src/utils/resolver.ts
@@ -0,0 +1,76 @@
+import { FormData, Deps, Schema } from '../types';
+import { isJsonSchema, isSegment, isObject } from './validate';
+
+/* eslint-disable no-new-func */
+export const safeEval = (code: string) => {
+ return Function(`"use strict"; ${code}`)();
+};
+
+/**
+ * 解析表达式计算结果
+ */
+export const evaluateSegment = (
+ segment: string,
+ selfValue: unknown,
+ formData: FormData,
+ deps: Deps
+) => {
+ try {
+ return safeEval(`
+ const $selfValue =${JSON.stringify(selfValue)};
+ const $values = ${JSON.stringify(formData)};
+ const $deps = ${JSON.stringify(deps)};
+ return (${segment})
+ `);
+ } catch (error) {
+ console.error(error, 'expression:', segment, 'formData:', formData);
+ return null;
+ }
+};
+
+export const resolvePropertiesSegment = (
+ properties: Record,
+ formData: FormData,
+ deps: Deps
+): Record => {
+ const resolvedProperties = {};
+
+ Object.keys(properties).forEach((key) => {
+ const val = properties[key];
+ if (Array.isArray(val)) {
+ resolvedProperties[key] = val;
+ } else if (isObject(val)) {
+ resolvedProperties[key] = resolvePropertiesSegment(val, formData, deps);
+ } else if (isSegment(val)) {
+ resolvedProperties[key] = evaluateSegment(val.slice(2, -2), formData[key], formData, deps);
+ } else {
+ resolvedProperties[key] = val;
+ }
+ });
+
+ return resolvedProperties;
+};
+
+export const resolveSchemaWithSegments = (
+ schema: Schema,
+ formData: FormData,
+ deps: Deps
+): Schema => {
+ if (!isJsonSchema(schema)) {
+ return schema;
+ }
+
+ const { properties = {}, ...rest } = schema;
+
+ const resolvedProperties = Object.fromEntries(
+ Object.entries(properties).map(([field, property]) => [
+ field,
+ resolvePropertiesSegment(property, formData, deps),
+ ])
+ );
+
+ return {
+ ...rest,
+ properties: resolvedProperties,
+ };
+};
diff --git a/packages/vue3-schema-form/src/utils/validate.ts b/packages/vue3-schema-form/src/utils/validate.ts
index d740ea6..e561a8e 100644
--- a/packages/vue3-schema-form/src/utils/validate.ts
+++ b/packages/vue3-schema-form/src/utils/validate.ts
@@ -1,4 +1,5 @@
import { Numeric } from './basic';
+import { Schema } from '../types';
export const isDef = (val: T): val is NonNullable => val !== undefined && val !== null;
@@ -32,6 +33,18 @@ export const isEmptyValue = (value: unknown) => {
return !value;
};
+export const isJsonSchema = (schema: Record): schema is Schema =>
+ schema.properties !== undefined && schema.type === 'object';
+
+export const isSegment = (segment: string): segment is string => {
+ const pattern = /^{{(.+)}}$/;
+ if (typeof segment === 'string' && pattern.test(segment)) {
+ return true;
+ }
+
+ return false;
+};
+
export const isJSON = (str: string) => {
try {
if (typeof JSON.parse(str) === 'object') {