feat(db): field value parser
This commit is contained in:
parent
0ac351bbd2
commit
5805b69455
@ -15,7 +15,7 @@ import {
|
|||||||
Sequelize,
|
Sequelize,
|
||||||
SyncOptions,
|
SyncOptions,
|
||||||
Transactionable,
|
Transactionable,
|
||||||
Utils,
|
Utils
|
||||||
} from 'sequelize';
|
} from 'sequelize';
|
||||||
import { SequelizeStorage, Umzug } from 'umzug';
|
import { SequelizeStorage, Umzug } from 'umzug';
|
||||||
import { Collection, CollectionOptions, RepositoryType } from './collection';
|
import { Collection, CollectionOptions, RepositoryType } from './collection';
|
||||||
@ -58,11 +58,12 @@ import {
|
|||||||
SyncListener,
|
SyncListener,
|
||||||
UpdateListener,
|
UpdateListener,
|
||||||
UpdateWithAssociationsListener,
|
UpdateWithAssociationsListener,
|
||||||
ValidateListener,
|
ValidateListener
|
||||||
} from './types';
|
} from './types';
|
||||||
import { patchSequelizeQueryInterface, snakeCase } from './utils';
|
import { patchSequelizeQueryInterface, snakeCase } from './utils';
|
||||||
|
|
||||||
import DatabaseUtils from './database-utils';
|
import DatabaseUtils from './database-utils';
|
||||||
|
import { BaseValueParser, registerFieldValueParsers } from './value-parsers';
|
||||||
|
|
||||||
export interface MergeOptions extends merge.Options {}
|
export interface MergeOptions extends merge.Options {}
|
||||||
|
|
||||||
@ -150,6 +151,7 @@ export class Database extends EventEmitter implements AsyncEmitter {
|
|||||||
migrator: Umzug;
|
migrator: Umzug;
|
||||||
migrations: Migrations;
|
migrations: Migrations;
|
||||||
fieldTypes = new Map();
|
fieldTypes = new Map();
|
||||||
|
fieldValueParsers = new Map();
|
||||||
options: IDatabaseOptions;
|
options: IDatabaseOptions;
|
||||||
models = new Map<string, ModelStatic<Model>>();
|
models = new Map<string, ModelStatic<Model>>();
|
||||||
repositories = new Map<string, RepositoryType>();
|
repositories = new Map<string, RepositoryType>();
|
||||||
@ -227,6 +229,8 @@ export class Database extends EventEmitter implements AsyncEmitter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerFieldValueParsers(this);
|
||||||
|
|
||||||
this.initOperators();
|
this.initOperators();
|
||||||
|
|
||||||
const migratorOptions: any = this.options.migrator || {};
|
const migratorOptions: any = this.options.migrator || {};
|
||||||
@ -473,6 +477,20 @@ export class Database extends EventEmitter implements AsyncEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerFieldValueParsers(parsers: MapOf<any>) {
|
||||||
|
for (const [type, parser] of Object.entries(parsers)) {
|
||||||
|
this.fieldValueParsers.set(type, parser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildFieldValueParser<T extends BaseValueParser>(field: Field, ctx: any) {
|
||||||
|
const Parser = this.fieldValueParsers.has(field.type)
|
||||||
|
? this.fieldValueParsers.get(field.type)
|
||||||
|
: this.fieldValueParsers.get('default');
|
||||||
|
const parser = new Parser(field, ctx);
|
||||||
|
return parser as T;
|
||||||
|
}
|
||||||
|
|
||||||
registerModels(models: MapOf<ModelStatic<any>>) {
|
registerModels(models: MapOf<ModelStatic<any>>) {
|
||||||
for (const [type, schemaType] of Object.entries(models)) {
|
for (const [type, schemaType] of Object.entries(models)) {
|
||||||
this.models.set(type, schemaType);
|
this.models.set(type, schemaType);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { DataTypes } from 'sequelize';
|
import { DataTypes } from 'sequelize';
|
||||||
import { BaseColumnFieldOptions, Field, ValueParser } from './field';
|
import { BaseColumnFieldOptions, Field } from './field';
|
||||||
|
|
||||||
export class ArrayField extends Field {
|
export class ArrayField extends Field {
|
||||||
get dataType() {
|
get dataType() {
|
||||||
@ -28,51 +28,6 @@ export class ArrayField extends Field {
|
|||||||
super.unbind();
|
super.unbind();
|
||||||
this.off('beforeSave', this.sortValue);
|
this.off('beforeSave', this.sortValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
buildValueParser(ctx: any) {
|
|
||||||
return new ArrayValueParser(this, ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ArrayValueParser extends ValueParser {
|
|
||||||
async setValue(value: any) {
|
|
||||||
const { map, set } = this.getOptions();
|
|
||||||
const values = this.toArr(value);
|
|
||||||
if (set.size > 0) {
|
|
||||||
const filtered = values.map((v) => (map.has(v) ? map.get(v) : v)).filter((v) => set.has(v));
|
|
||||||
if (values.length === filtered.length) {
|
|
||||||
this.value = filtered;
|
|
||||||
} else {
|
|
||||||
this.errors.push('No matching option found');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.value = values;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getOptions() {
|
|
||||||
const options = this.field.options?.['uiSchema']?.enum || [];
|
|
||||||
const map = new Map();
|
|
||||||
const set = new Set();
|
|
||||||
for (const option of options) {
|
|
||||||
set.add(option.value);
|
|
||||||
set.add(option.label);
|
|
||||||
map.set(option.label, option.value);
|
|
||||||
}
|
|
||||||
return { map, set };
|
|
||||||
}
|
|
||||||
|
|
||||||
toArr(value) {
|
|
||||||
let values = [];
|
|
||||||
if (!value) {
|
|
||||||
values = [];
|
|
||||||
} else if (typeof value === 'string') {
|
|
||||||
values = value.split(',');
|
|
||||||
} else if (Array.isArray(value)) {
|
|
||||||
values = value;
|
|
||||||
}
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ArrayFieldOptions extends BaseColumnFieldOptions {
|
export interface ArrayFieldOptions extends BaseColumnFieldOptions {
|
||||||
|
@ -2,7 +2,6 @@ import { omit } from 'lodash';
|
|||||||
import { BelongsToOptions as SequelizeBelongsToOptions, Utils } from 'sequelize';
|
import { BelongsToOptions as SequelizeBelongsToOptions, Utils } from 'sequelize';
|
||||||
import { Reference } from '../features/ReferencesMap';
|
import { Reference } from '../features/ReferencesMap';
|
||||||
import { checkIdentifier } from '../utils';
|
import { checkIdentifier } from '../utils';
|
||||||
import { ToOneValueParser } from './has-one-field';
|
|
||||||
import { BaseRelationFieldOptions, RelationField } from './relation-field';
|
import { BaseRelationFieldOptions, RelationField } from './relation-field';
|
||||||
|
|
||||||
export class BelongsToField extends RelationField {
|
export class BelongsToField extends RelationField {
|
||||||
@ -116,10 +115,6 @@ export class BelongsToField extends RelationField {
|
|||||||
collection.model.refreshAttributes();
|
collection.model.refreshAttributes();
|
||||||
// this.collection.removeIndex([this.options.foreignKey]);
|
// this.collection.removeIndex([this.options.foreignKey]);
|
||||||
}
|
}
|
||||||
|
|
||||||
buildValueParser(ctx: any) {
|
|
||||||
return new ToOneValueParser(this, ctx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BelongsToFieldOptions extends BaseRelationFieldOptions, SequelizeBelongsToOptions {
|
export interface BelongsToFieldOptions extends BaseRelationFieldOptions, SequelizeBelongsToOptions {
|
||||||
|
@ -2,10 +2,8 @@ import { omit } from 'lodash';
|
|||||||
import { BelongsToManyOptions as SequelizeBelongsToManyOptions, Utils } from 'sequelize';
|
import { BelongsToManyOptions as SequelizeBelongsToManyOptions, Utils } from 'sequelize';
|
||||||
import { Collection } from '../collection';
|
import { Collection } from '../collection';
|
||||||
import { Reference } from '../features/ReferencesMap';
|
import { Reference } from '../features/ReferencesMap';
|
||||||
import { Repository } from '../repository';
|
|
||||||
import { checkIdentifier } from '../utils';
|
import { checkIdentifier } from '../utils';
|
||||||
import { BelongsToField } from './belongs-to-field';
|
import { BelongsToField } from './belongs-to-field';
|
||||||
import { ValueParser } from './field';
|
|
||||||
import { MultipleRelationFieldOptions, RelationField } from './relation-field';
|
import { MultipleRelationFieldOptions, RelationField } from './relation-field';
|
||||||
|
|
||||||
export class BelongsToManyField extends RelationField {
|
export class BelongsToManyField extends RelationField {
|
||||||
@ -125,48 +123,6 @@ export class BelongsToManyField extends RelationField {
|
|||||||
this.clearAccessors();
|
this.clearAccessors();
|
||||||
delete collection.model.associations[this.name];
|
delete collection.model.associations[this.name];
|
||||||
}
|
}
|
||||||
|
|
||||||
buildValueParser(ctx: any) {
|
|
||||||
return new ToManyValueParser(this, ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ToManyValueParser extends ValueParser {
|
|
||||||
async setValue(value: any) {
|
|
||||||
const fieldNames = this.getFileNames();
|
|
||||||
console.log('fieldNames', fieldNames, this.ctx.column);
|
|
||||||
if (this.isInterface('chinaRegion')) {
|
|
||||||
const repository = this.field.database.getRepository(this.field.target) as Repository;
|
|
||||||
this.value = await Promise.all(
|
|
||||||
value.split('/').map(async (v) => {
|
|
||||||
const instance = await repository.findOne({ filter: { [fieldNames.label]: v.trim() } });
|
|
||||||
return instance ? instance.get(fieldNames.value) : v;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const dataIndex = this.ctx?.column?.dataIndex || [];
|
|
||||||
if (Array.isArray(dataIndex) && dataIndex.length < 2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const field = this.ctx.column.dataIndex[1];
|
|
||||||
const repository = this.field.database.getRepository(this.field.target) as Repository;
|
|
||||||
this.value = await Promise.all(
|
|
||||||
value.split(',').map(async (v) => {
|
|
||||||
const instance = await repository.findOne({ filter: { [field]: v.trim() } });
|
|
||||||
return instance ? instance.get(fieldNames.value) : v;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getFileNames() {
|
|
||||||
const fieldNames = this.field.options?.uiSchema?.['x-component-props']?.['fieldNames'] || {};
|
|
||||||
return { label: 'id', value: 'id', ...fieldNames };
|
|
||||||
}
|
|
||||||
|
|
||||||
isInterface(name) {
|
|
||||||
return this.field.options.interface === name;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BelongsToManyFieldOptions
|
export interface BelongsToManyFieldOptions
|
||||||
|
@ -1,42 +1,10 @@
|
|||||||
import { DataTypes } from 'sequelize';
|
import { DataTypes } from 'sequelize';
|
||||||
import { BaseColumnFieldOptions, Field, ValueParser } from './field';
|
import { BaseColumnFieldOptions, Field } from './field';
|
||||||
|
|
||||||
export class BooleanField extends Field {
|
export class BooleanField extends Field {
|
||||||
get dataType() {
|
get dataType() {
|
||||||
return DataTypes.BOOLEAN;
|
return DataTypes.BOOLEAN;
|
||||||
}
|
}
|
||||||
|
|
||||||
buildValueParser(ctx: any) {
|
|
||||||
return new BooleanValueParser(this, ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class BooleanValueParser extends ValueParser {
|
|
||||||
async setValue(value: any) {
|
|
||||||
// Boolean
|
|
||||||
if (typeof value === 'boolean') {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
// Number
|
|
||||||
else if (typeof value === 'number' && [0, 1].includes(value)) {
|
|
||||||
this.value = value === 1;
|
|
||||||
}
|
|
||||||
// String
|
|
||||||
else if (typeof value === 'string') {
|
|
||||||
if (!value) {
|
|
||||||
this.value = null;
|
|
||||||
}
|
|
||||||
if (['1', 'y', 'yes', 'true', '是'].includes(value.toLowerCase())) {
|
|
||||||
this.value = true;
|
|
||||||
} else if (['0', 'n', 'no', 'false', '否'].includes(value.toLowerCase())) {
|
|
||||||
this.value = false;
|
|
||||||
} else {
|
|
||||||
this.errors.push(`${JSON.stringify(this.value)} value invalid`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.errors.push(`${JSON.stringify(this.value)} value invalid`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BooleanFieldOptions extends BaseColumnFieldOptions {
|
export interface BooleanFieldOptions extends BaseColumnFieldOptions {
|
||||||
|
@ -1,38 +1,10 @@
|
|||||||
import { moment2str } from '@nocobase/utils';
|
|
||||||
import moment, { isDate, isMoment } from 'moment';
|
|
||||||
import { DataTypes } from 'sequelize';
|
import { DataTypes } from 'sequelize';
|
||||||
import { BaseColumnFieldOptions, Field, ValueParser } from './field';
|
import { BaseColumnFieldOptions, Field } from './field';
|
||||||
|
|
||||||
export class DateField extends Field {
|
export class DateField extends Field {
|
||||||
get dataType() {
|
get dataType() {
|
||||||
return DataTypes.DATE(3);
|
return DataTypes.DATE(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
buildValueParser(ctx: any) {
|
|
||||||
return new DateValueParser(this, ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DateValueParser extends ValueParser {
|
|
||||||
async setValue(value: any) {
|
|
||||||
if (isMoment(value)) {
|
|
||||||
this.value = value;
|
|
||||||
} else if (isDate(value)) {
|
|
||||||
this.value = value;
|
|
||||||
} else if (typeof value === 'string') {
|
|
||||||
const props = this.getProps();
|
|
||||||
const m = moment(value);
|
|
||||||
if (m.isValid()) {
|
|
||||||
this.value = moment2str(m, props);
|
|
||||||
} else {
|
|
||||||
this.errors.push('Invalid date');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getProps() {
|
|
||||||
return this.field.options?.uiSchema?.['x-component-props'] || {};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DateFieldOptions extends BaseColumnFieldOptions {
|
export interface DateFieldOptions extends BaseColumnFieldOptions {
|
||||||
|
@ -238,33 +238,4 @@ export abstract class Field {
|
|||||||
typeToString() {
|
typeToString() {
|
||||||
return this.dataType.toString();
|
return this.dataType.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
buildValueParser(ctx: any) {
|
|
||||||
return new ValueParser(this, ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ValueParser {
|
|
||||||
ctx: any;
|
|
||||||
field: any;
|
|
||||||
value: any;
|
|
||||||
errors = [];
|
|
||||||
|
|
||||||
constructor(field: any, ctx: any) {
|
|
||||||
this.field = field;
|
|
||||||
this.ctx = ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
toString() {
|
|
||||||
return this.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
getValue() {
|
|
||||||
return this.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
async setValue(value: any) {
|
|
||||||
console.log(this.field.name, value);
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import {
|
|||||||
import { Collection } from '../collection';
|
import { Collection } from '../collection';
|
||||||
import { Reference } from '../features/ReferencesMap';
|
import { Reference } from '../features/ReferencesMap';
|
||||||
import { checkIdentifier } from '../utils';
|
import { checkIdentifier } from '../utils';
|
||||||
import { ToManyValueParser } from './belongs-to-many-field';
|
|
||||||
import { MultipleRelationFieldOptions, RelationField } from './relation-field';
|
import { MultipleRelationFieldOptions, RelationField } from './relation-field';
|
||||||
|
|
||||||
export interface HasManyFieldOptions extends HasManyOptions {
|
export interface HasManyFieldOptions extends HasManyOptions {
|
||||||
@ -186,10 +185,6 @@ export class HasManyField extends RelationField {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
collection.model.refreshAttributes();
|
collection.model.refreshAttributes();
|
||||||
}
|
}
|
||||||
|
|
||||||
buildValueParser(ctx: any) {
|
|
||||||
return new ToManyValueParser(this, ctx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HasManyFieldOptions extends MultipleRelationFieldOptions, SequelizeHasManyOptions {
|
export interface HasManyFieldOptions extends MultipleRelationFieldOptions, SequelizeHasManyOptions {
|
||||||
|
@ -9,9 +9,7 @@ import {
|
|||||||
} from 'sequelize';
|
} from 'sequelize';
|
||||||
import { Collection } from '../collection';
|
import { Collection } from '../collection';
|
||||||
import { Reference } from '../features/ReferencesMap';
|
import { Reference } from '../features/ReferencesMap';
|
||||||
import { Repository } from '../repository';
|
|
||||||
import { checkIdentifier } from '../utils';
|
import { checkIdentifier } from '../utils';
|
||||||
import { ValueParser } from './field';
|
|
||||||
import { BaseRelationFieldOptions, RelationField } from './relation-field';
|
import { BaseRelationFieldOptions, RelationField } from './relation-field';
|
||||||
|
|
||||||
export interface HasOneFieldOptions extends HasOneOptions {
|
export interface HasOneFieldOptions extends HasOneOptions {
|
||||||
@ -193,36 +191,6 @@ export class HasOneField extends RelationField {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
collection.model.refreshAttributes();
|
collection.model.refreshAttributes();
|
||||||
}
|
}
|
||||||
|
|
||||||
buildValueParser(ctx: any) {
|
|
||||||
return new ToOneValueParser(this, ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ToOneValueParser extends ValueParser {
|
|
||||||
async setValue(value: any) {
|
|
||||||
const fieldNames = this.getFileNames();
|
|
||||||
console.log('fieldNames', fieldNames, this.ctx.column);
|
|
||||||
const dataIndex = this.ctx?.column?.dataIndex || [];
|
|
||||||
if (Array.isArray(dataIndex) && dataIndex.length < 2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const field = this.ctx.column.dataIndex[1];
|
|
||||||
const repository = this.field.database.getRepository(this.field.target) as Repository;
|
|
||||||
const instance = await repository.findOne({ filter: { [field]: value.trim() } });
|
|
||||||
if (instance) {
|
|
||||||
this.value = instance.get(fieldNames.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getFileNames() {
|
|
||||||
const fieldNames = this.field.options?.uiSchema?.['x-component-props']?.['fieldNames'] || {};
|
|
||||||
return { label: 'id', value: 'id', ...fieldNames };
|
|
||||||
}
|
|
||||||
|
|
||||||
isInterface(name) {
|
|
||||||
return this.field.options.interface === name;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HasOneFieldOptions extends BaseRelationFieldOptions, SequelizeHasOneOptions {
|
export interface HasOneFieldOptions extends BaseRelationFieldOptions, SequelizeHasOneOptions {
|
||||||
|
@ -1,41 +1,8 @@
|
|||||||
import { DataTypes } from 'sequelize';
|
import { DataTypes } from 'sequelize';
|
||||||
import { BaseColumnFieldOptions, Field, ValueParser } from './field';
|
import { BaseColumnFieldOptions, Field } from './field';
|
||||||
|
|
||||||
abstract class NumberField extends Field {
|
abstract class NumberField extends Field {
|
||||||
buildValueParser(ctx: any) {
|
|
||||||
return new NumberValueParser(this, ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function percent2float(value: string) {
|
|
||||||
const index = value.indexOf('.');
|
|
||||||
if (index === -1) {
|
|
||||||
return parseFloat(value) / 100;
|
|
||||||
}
|
|
||||||
const repeat = value.length - index - 2;
|
|
||||||
const v = parseInt('1' + '0'.repeat(repeat));
|
|
||||||
return (parseFloat(value) * v) / (100 * v);
|
|
||||||
}
|
|
||||||
|
|
||||||
export class NumberValueParser extends ValueParser {
|
|
||||||
async setValue(value: any) {
|
|
||||||
if (value === null || value === undefined || typeof value === 'number') {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
if (typeof value === 'string') {
|
|
||||||
if (!value) {
|
|
||||||
this.value = null;
|
|
||||||
} else if (['n/a', '-'].includes(value.toLowerCase())) {
|
|
||||||
this.value = null;
|
|
||||||
} else if (value.endsWith('%')) {
|
|
||||||
this.value = percent2float(value);
|
|
||||||
console.log(value, this.value);
|
|
||||||
} else {
|
|
||||||
const val = +value;
|
|
||||||
this.value = isNaN(val) ? null : val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class IntegerField extends NumberField {
|
export class IntegerField extends NumberField {
|
||||||
|
@ -1,44 +1,10 @@
|
|||||||
import { DataTypes } from 'sequelize';
|
import { DataTypes } from 'sequelize';
|
||||||
import { BaseColumnFieldOptions, Field, ValueParser } from './field';
|
import { BaseColumnFieldOptions, Field } from './field';
|
||||||
|
|
||||||
export class StringField extends Field {
|
export class StringField extends Field {
|
||||||
get dataType() {
|
get dataType() {
|
||||||
return DataTypes.STRING;
|
return DataTypes.STRING;
|
||||||
}
|
}
|
||||||
|
|
||||||
buildValueParser(ctx: any) {
|
|
||||||
return new StringValueParser(this, ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class StringValueParser extends ValueParser {
|
|
||||||
async setValue(value: any) {
|
|
||||||
const { map, set } = this.getOptions();
|
|
||||||
if (set.size > 0) {
|
|
||||||
if (map.has(value)) {
|
|
||||||
value = map.get(value);
|
|
||||||
}
|
|
||||||
if (set.has(value)) {
|
|
||||||
this.value = value;
|
|
||||||
} else {
|
|
||||||
this.errors.push('No matching option found');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getOptions() {
|
|
||||||
const options = this.field.options?.['uiSchema']?.enum || [];
|
|
||||||
const map = new Map();
|
|
||||||
const set = new Set();
|
|
||||||
for (const option of options) {
|
|
||||||
set.add(option.value);
|
|
||||||
set.add(option.label);
|
|
||||||
map.set(option.label, option.value);
|
|
||||||
}
|
|
||||||
return { map, set };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StringFieldOptions extends BaseColumnFieldOptions {
|
export interface StringFieldOptions extends BaseColumnFieldOptions {
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
export { DataTypes, ModelStatic, Op, SyncOptions } from 'sequelize';
|
export { DataTypes, ModelStatic, Op, SyncOptions } from 'sequelize';
|
||||||
export * from './collection';
|
export * from './collection';
|
||||||
export * from './inherited-collection';
|
export * from './collection-importer';
|
||||||
export * from './database';
|
export * from './database';
|
||||||
export { Database as default } from './database';
|
export { Database as default } from './database';
|
||||||
|
export * from './field-repository/array-field-repository';
|
||||||
export * from './fields';
|
export * from './fields';
|
||||||
|
export * from './filter-match';
|
||||||
|
export * from './inherited-collection';
|
||||||
export * from './magic-attribute-model';
|
export * from './magic-attribute-model';
|
||||||
export * from './migration';
|
export * from './migration';
|
||||||
export * from './mock-database';
|
export * from './mock-database';
|
||||||
@ -15,7 +18,6 @@ export * from './relation-repository/multiple-relation-repository';
|
|||||||
export * from './relation-repository/single-relation-repository';
|
export * from './relation-repository/single-relation-repository';
|
||||||
export * from './repository';
|
export * from './repository';
|
||||||
export * from './update-associations';
|
export * from './update-associations';
|
||||||
export * from './collection-importer';
|
|
||||||
export * from './filter-match';
|
|
||||||
export * from './field-repository/array-field-repository';
|
|
||||||
export { snakeCase } from './utils';
|
export { snakeCase } from './utils';
|
||||||
|
export * from './value-parsers';
|
||||||
|
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
|
import Database from './database';
|
||||||
import { IdentifierError } from './errors/identifier-error';
|
import { IdentifierError } from './errors/identifier-error';
|
||||||
import { Model } from './model';
|
import { Model } from './model';
|
||||||
import lodash from 'lodash';
|
|
||||||
import Database from './database';
|
|
||||||
|
|
||||||
type HandleAppendsQueryOptions = {
|
type HandleAppendsQueryOptions = {
|
||||||
templateModel: any;
|
templateModel: any;
|
||||||
@ -116,3 +115,13 @@ export function patchSequelizeQueryInterface(db: Database) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function percent2float(value: string) {
|
||||||
|
const index = value.indexOf('.');
|
||||||
|
if (index === -1) {
|
||||||
|
return parseFloat(value) / 100;
|
||||||
|
}
|
||||||
|
const repeat = value.length - index - 2;
|
||||||
|
const v = parseInt('1' + '0'.repeat(repeat));
|
||||||
|
return (parseFloat(value) * v) / (100 * v);
|
||||||
|
}
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
import { BaseValueParser } from './base-value-parser';
|
||||||
|
|
||||||
|
export class ArrayValueParser extends BaseValueParser {
|
||||||
|
async setValue(value: any) {
|
||||||
|
const { map, set } = this.getOptions();
|
||||||
|
const values = this.toArr(value);
|
||||||
|
if (set.size > 0) {
|
||||||
|
const filtered = values.map((v) => (map.has(v) ? map.get(v) : v)).filter((v) => set.has(v));
|
||||||
|
if (values.length === filtered.length) {
|
||||||
|
this.value = filtered;
|
||||||
|
} else {
|
||||||
|
this.errors.push(`No matching option found - ${JSON.stringify(value)}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.value = values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getOptions() {
|
||||||
|
const options = this.field.options?.['uiSchema']?.enum || [];
|
||||||
|
const map = new Map();
|
||||||
|
const set = new Set();
|
||||||
|
for (const option of options) {
|
||||||
|
set.add(option.value);
|
||||||
|
set.add(option.label);
|
||||||
|
map.set(option.label, option.value);
|
||||||
|
}
|
||||||
|
return { map, set };
|
||||||
|
}
|
||||||
|
|
||||||
|
toArr(value) {
|
||||||
|
let values: string[] = [];
|
||||||
|
if (!value) {
|
||||||
|
values = [];
|
||||||
|
} else if (typeof value === 'string') {
|
||||||
|
values = value.split(',');
|
||||||
|
} else if (Array.isArray(value)) {
|
||||||
|
values = value;
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
export class BaseValueParser {
|
||||||
|
ctx: any;
|
||||||
|
field: any;
|
||||||
|
value: any;
|
||||||
|
errors: string[] = [];
|
||||||
|
|
||||||
|
constructor(field: any, ctx: any) {
|
||||||
|
this.field = field;
|
||||||
|
this.ctx = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
getValue() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
async setValue(value: any) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
import { BaseValueParser } from './base-value-parser';
|
||||||
|
|
||||||
|
export class BooleanValueParser extends BaseValueParser {
|
||||||
|
async setValue(value: any) {
|
||||||
|
// Boolean
|
||||||
|
if (typeof value === 'boolean') {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
// Number
|
||||||
|
else if (typeof value === 'number' && [0, 1].includes(value)) {
|
||||||
|
this.value = value === 1;
|
||||||
|
}
|
||||||
|
// String
|
||||||
|
else if (typeof value === 'string') {
|
||||||
|
if (!value) {
|
||||||
|
this.value = null;
|
||||||
|
}
|
||||||
|
if (['1', 'y', 'yes', 'true', '是'].includes(value.toLowerCase())) {
|
||||||
|
this.value = true;
|
||||||
|
} else if (['0', 'n', 'no', 'false', '否'].includes(value.toLowerCase())) {
|
||||||
|
this.value = false;
|
||||||
|
} else {
|
||||||
|
this.errors.push(`Value invalid - ${JSON.stringify(this.value)}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.errors.push(`Value invalid - ${JSON.stringify(this.value)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
import { moment2str } from '@nocobase/utils';
|
||||||
|
import moment, { isDate, isMoment } from 'moment';
|
||||||
|
import { BaseValueParser } from "./base-value-parser";
|
||||||
|
|
||||||
|
export class DateValueParser extends BaseValueParser {
|
||||||
|
async setValue(value: any) {
|
||||||
|
if (isMoment(value)) {
|
||||||
|
this.value = value;
|
||||||
|
} else if (isDate(value)) {
|
||||||
|
this.value = value;
|
||||||
|
} else if (typeof value === 'string') {
|
||||||
|
const props = this.getProps();
|
||||||
|
const m = moment(value);
|
||||||
|
if (m.isValid()) {
|
||||||
|
this.value = moment2str(m, props);
|
||||||
|
} else {
|
||||||
|
this.errors.push('Invalid date');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getProps() {
|
||||||
|
return this.field.options?.uiSchema?.['x-component-props'] || {};
|
||||||
|
}
|
||||||
|
}
|
46
packages/core/database/src/value-parsers/index.ts
Normal file
46
packages/core/database/src/value-parsers/index.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { Database } from '../database';
|
||||||
|
import { ArrayValueParser } from './array-value-parser';
|
||||||
|
import { BaseValueParser } from './base-value-parser';
|
||||||
|
import { BooleanValueParser } from './boolean-value-parser';
|
||||||
|
import { DateValueParser } from './date-value-parser';
|
||||||
|
import { JsonValueParser } from './json-value-parser';
|
||||||
|
import { NumberValueParser } from './number-value-parser';
|
||||||
|
import { StringValueParser } from './string-value-parser';
|
||||||
|
import { ToManyValueParser } from './to-many-value-parser';
|
||||||
|
import { ToOneValueParser } from './to-one-value-parser';
|
||||||
|
|
||||||
|
export function registerFieldValueParsers(db: Database) {
|
||||||
|
db.registerFieldValueParsers({
|
||||||
|
default: BaseValueParser,
|
||||||
|
array: ArrayValueParser,
|
||||||
|
set: ArrayValueParser,
|
||||||
|
boolean: BooleanValueParser,
|
||||||
|
date: DateValueParser,
|
||||||
|
json: JsonValueParser,
|
||||||
|
jsonb: JsonValueParser,
|
||||||
|
number: NumberValueParser,
|
||||||
|
integer: NumberValueParser,
|
||||||
|
bigInt: NumberValueParser,
|
||||||
|
float: NumberValueParser,
|
||||||
|
double: NumberValueParser,
|
||||||
|
real: NumberValueParser,
|
||||||
|
decimal: NumberValueParser,
|
||||||
|
string: StringValueParser,
|
||||||
|
hasOne: ToOneValueParser,
|
||||||
|
hasMany: ToManyValueParser,
|
||||||
|
belongsTo: ToOneValueParser,
|
||||||
|
belongsToMany: ToManyValueParser,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
ArrayValueParser,
|
||||||
|
BaseValueParser,
|
||||||
|
BooleanValueParser,
|
||||||
|
DateValueParser,
|
||||||
|
JsonValueParser,
|
||||||
|
NumberValueParser,
|
||||||
|
StringValueParser,
|
||||||
|
ToManyValueParser,
|
||||||
|
ToOneValueParser,
|
||||||
|
};
|
@ -0,0 +1,7 @@
|
|||||||
|
import { BaseValueParser } from './base-value-parser';
|
||||||
|
|
||||||
|
export class JsonValueParser extends BaseValueParser {
|
||||||
|
async setValue(value: any) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
import { percent2float } from '../utils';
|
||||||
|
import { BaseValueParser } from './base-value-parser';
|
||||||
|
|
||||||
|
export class NumberValueParser extends BaseValueParser {
|
||||||
|
async setValue(value: any) {
|
||||||
|
if (value === null || value === undefined || typeof value === 'number') {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
if (!value) {
|
||||||
|
this.value = null;
|
||||||
|
} else if (['n/a', '-'].includes(value.toLowerCase())) {
|
||||||
|
this.value = null;
|
||||||
|
} else if (value.endsWith('%')) {
|
||||||
|
this.value = percent2float(value);
|
||||||
|
console.log(value, this.value);
|
||||||
|
} else {
|
||||||
|
const val = +value;
|
||||||
|
this.value = isNaN(val) ? null : val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
import { BaseValueParser } from './base-value-parser';
|
||||||
|
|
||||||
|
export class StringValueParser extends BaseValueParser {
|
||||||
|
async setValue(value: any) {
|
||||||
|
const { map, set } = this.getOptions();
|
||||||
|
if (set.size > 0) {
|
||||||
|
if (map.has(value)) {
|
||||||
|
value = map.get(value);
|
||||||
|
}
|
||||||
|
if (set.has(value)) {
|
||||||
|
this.value = value;
|
||||||
|
} else {
|
||||||
|
this.errors.push(`No matching option found - ${JSON.stringify(value)}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getOptions() {
|
||||||
|
const options = this.field.options?.['uiSchema']?.enum || [];
|
||||||
|
const map = new Map();
|
||||||
|
const set = new Set();
|
||||||
|
for (const option of options) {
|
||||||
|
set.add(option.value);
|
||||||
|
set.add(option.label);
|
||||||
|
map.set(option.label, option.value);
|
||||||
|
}
|
||||||
|
return { map, set };
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
import { Repository } from '../repository';
|
||||||
|
import { BaseValueParser } from './base-value-parser';
|
||||||
|
|
||||||
|
export class ToManyValueParser extends BaseValueParser {
|
||||||
|
async setValue(value: any) {
|
||||||
|
const fieldNames = this.getFileNames();
|
||||||
|
if (this.isInterface('chinaRegion')) {
|
||||||
|
const repository = this.field.database.getRepository(this.field.target) as Repository;
|
||||||
|
try {
|
||||||
|
this.value = await Promise.all(
|
||||||
|
value.split('/').map(async (v) => {
|
||||||
|
const instance = await repository.findOne({ filter: { [fieldNames.label]: v.trim() } });
|
||||||
|
if (!instance) {
|
||||||
|
throw new Error(`"${v}" does not exist`);
|
||||||
|
}
|
||||||
|
return instance.get(fieldNames.value);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
this.errors.push(error.message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const dataIndex = this.ctx?.column?.dataIndex || [];
|
||||||
|
if (Array.isArray(dataIndex) && dataIndex.length < 2) {
|
||||||
|
this.errors.push(`data index invalid`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const key = this.ctx.column.dataIndex[1];
|
||||||
|
const repository = this.field.database.getRepository(this.field.target) as Repository;
|
||||||
|
try {
|
||||||
|
this.value = await Promise.all(
|
||||||
|
value.split(',').map(async (v) => {
|
||||||
|
const instance = await repository.findOne({ filter: { [key]: v.trim() } });
|
||||||
|
if (!instance) {
|
||||||
|
throw new Error(`"${v}" does not exist`);
|
||||||
|
}
|
||||||
|
return instance.get(fieldNames.value);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
this.errors.push(error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getFileNames() {
|
||||||
|
const fieldNames = this.field.options?.uiSchema?.['x-component-props']?.['fieldNames'] || {};
|
||||||
|
return { label: 'id', value: 'id', ...fieldNames };
|
||||||
|
}
|
||||||
|
|
||||||
|
isInterface(name) {
|
||||||
|
return this.field.options.interface === name;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
import { Repository } from '../repository';
|
||||||
|
import { BaseValueParser } from './base-value-parser';
|
||||||
|
|
||||||
|
export class ToOneValueParser extends BaseValueParser {
|
||||||
|
async setValue(value: any) {
|
||||||
|
const fieldNames = this.getFileNames();
|
||||||
|
const dataIndex = this.ctx?.column?.dataIndex || [];
|
||||||
|
if (Array.isArray(dataIndex) && dataIndex.length < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const field = this.ctx.column.dataIndex[1];
|
||||||
|
const repository = this.field.database.getRepository(this.field.target) as Repository;
|
||||||
|
const instance = await repository.findOne({ filter: { [field]: value.trim() } });
|
||||||
|
if (instance) {
|
||||||
|
this.value = instance.get(fieldNames.value);
|
||||||
|
} else {
|
||||||
|
this.errors.push(`"${value}" does not exist`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getFileNames() {
|
||||||
|
const fieldNames = this.field.options?.uiSchema?.['x-component-props']?.['fieldNames'] || {};
|
||||||
|
return { label: 'id', value: 'id', ...fieldNames };
|
||||||
|
}
|
||||||
|
|
||||||
|
isInterface(name) {
|
||||||
|
return this.field.options.interface === name;
|
||||||
|
}
|
||||||
|
}
|
@ -40,9 +40,6 @@ export const useDownloadXlsxTemplateAction = () => {
|
|||||||
if (!field) {
|
if (!field) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (field.interface === 'chinaRegion') {
|
|
||||||
column.dataIndex.push('name');
|
|
||||||
}
|
|
||||||
column.defaultTitle = compile(field?.uiSchema?.title) || field.name;
|
column.defaultTitle = compile(field?.uiSchema?.title) || field.name;
|
||||||
if (column.dataIndex.length > 1) {
|
if (column.dataIndex.length > 1) {
|
||||||
const subField = getCollectionJoinField(`${name}.${column.dataIndex.join('.')}`);
|
const subField = getCollectionJoinField(`${name}.${column.dataIndex.join('.')}`);
|
||||||
@ -51,6 +48,9 @@ export const useDownloadXlsxTemplateAction = () => {
|
|||||||
}
|
}
|
||||||
column.defaultTitle = column.defaultTitle + '/' + compile(subField?.uiSchema?.title) || subField.name;
|
column.defaultTitle = column.defaultTitle + '/' + compile(subField?.uiSchema?.title) || subField.name;
|
||||||
}
|
}
|
||||||
|
if (field.interface === 'chinaRegion') {
|
||||||
|
column.dataIndex.push('name');
|
||||||
|
}
|
||||||
return column;
|
return column;
|
||||||
})
|
})
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
@ -94,9 +94,6 @@ export const useImportStartAction = () => {
|
|||||||
if (!field) {
|
if (!field) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (field.interface === 'chinaRegion') {
|
|
||||||
column.dataIndex.push('name');
|
|
||||||
}
|
|
||||||
column.defaultTitle = compile(field?.uiSchema?.title) || field.name;
|
column.defaultTitle = compile(field?.uiSchema?.title) || field.name;
|
||||||
if (column.dataIndex.length > 1) {
|
if (column.dataIndex.length > 1) {
|
||||||
const subField = getCollectionJoinField(`${name}.${column.dataIndex.join('.')}`);
|
const subField = getCollectionJoinField(`${name}.${column.dataIndex.join('.')}`);
|
||||||
@ -105,6 +102,9 @@ export const useImportStartAction = () => {
|
|||||||
}
|
}
|
||||||
column.defaultTitle = column.defaultTitle + '/' + compile(subField?.uiSchema?.title) || subField.name;
|
column.defaultTitle = column.defaultTitle + '/' + compile(subField?.uiSchema?.title) || subField.name;
|
||||||
}
|
}
|
||||||
|
if (field.interface === 'chinaRegion') {
|
||||||
|
column.dataIndex.push('name');
|
||||||
|
}
|
||||||
return column;
|
return column;
|
||||||
})
|
})
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
@ -77,7 +77,7 @@ class Importer {
|
|||||||
if (value === undefined || value === null) {
|
if (value === undefined || value === null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const parser = field.buildValueParser({ ...this.context, column });
|
const parser = this.context.db.buildFieldValueParser(field, { ...this.context, column });
|
||||||
await parser.setValue(typeof value === 'string' ? value.trim() : value);
|
await parser.setValue(typeof value === 'string' ? value.trim() : value);
|
||||||
value = parser.getValue();
|
value = parser.getValue();
|
||||||
if (parser.errors.length > 0) {
|
if (parser.errors.length > 0) {
|
||||||
@ -127,6 +127,7 @@ class Importer {
|
|||||||
result[0].push(instance);
|
result[0].push(instance);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.context.log.error(error, row);
|
this.context.log.error(error, row);
|
||||||
|
row.push(error.message);
|
||||||
result[1].push(row);
|
result[1].push(row);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import { InstallOptions, Plugin } from '@nocobase/server';
|
|||||||
import { resolve } from 'path';
|
import { resolve } from 'path';
|
||||||
import { getConfiguration, setConfiguration } from './actions';
|
import { getConfiguration, setConfiguration } from './actions';
|
||||||
import { CircleField, LineStringField, PointField, PolygonField } from './fields';
|
import { CircleField, LineStringField, PointField, PolygonField } from './fields';
|
||||||
|
import { CircleValueParser, LineStringValueParser, PointValueParser, PolygonValueParser } from './value-parsers';
|
||||||
|
|
||||||
export class MapPlugin extends Plugin {
|
export class MapPlugin extends Plugin {
|
||||||
afterAdd() {}
|
afterAdd() {}
|
||||||
@ -13,8 +14,13 @@ export class MapPlugin extends Plugin {
|
|||||||
lineString: LineStringField,
|
lineString: LineStringField,
|
||||||
circle: CircleField,
|
circle: CircleField,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.db.registerFieldTypes(fields);
|
this.db.registerFieldTypes(fields);
|
||||||
|
this.db.registerFieldValueParsers({
|
||||||
|
point: PointValueParser,
|
||||||
|
polygon: PolygonValueParser,
|
||||||
|
lineString: LineStringValueParser,
|
||||||
|
circle: CircleValueParser,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
|
55
packages/plugins/map/src/server/value-parsers/index.ts
Normal file
55
packages/plugins/map/src/server/value-parsers/index.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { BaseValueParser } from '@nocobase/database';
|
||||||
|
|
||||||
|
export class PointValueParser extends BaseValueParser {
|
||||||
|
async setValue(value) {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
this.value = value;
|
||||||
|
} else if (typeof value === 'string') {
|
||||||
|
this.value = value.split(',');
|
||||||
|
} else {
|
||||||
|
this.errors.push('Value invalid');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PolygonValueParser extends BaseValueParser {
|
||||||
|
async setValue(value) {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
this.value = value;
|
||||||
|
} else if (typeof value === 'string') {
|
||||||
|
this.value = value
|
||||||
|
.substring(1, value.length - 1)
|
||||||
|
.split('),(')
|
||||||
|
.map((v) => v.split(','));
|
||||||
|
} else {
|
||||||
|
this.errors.push('Value invalid');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LineStringValueParser extends BaseValueParser {
|
||||||
|
async setValue(value) {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
this.value = value;
|
||||||
|
} else if (typeof value === 'string') {
|
||||||
|
this.value = value
|
||||||
|
.substring(1, value.length - 1)
|
||||||
|
.split('),(')
|
||||||
|
.map((v) => v.split(','));
|
||||||
|
} else {
|
||||||
|
this.errors.push('Value invalid');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CircleValueParser extends BaseValueParser {
|
||||||
|
async setValue(value) {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
this.value = value;
|
||||||
|
} else if (typeof value === 'string') {
|
||||||
|
this.value = value.split(',');
|
||||||
|
} else {
|
||||||
|
this.errors.push('Value invalid');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user