Skip to content

Commit

Permalink
feat: http 请求数据集支持添加函数处理
Browse files Browse the repository at this point in the history
  • Loading branch information
lvisei committed Nov 27, 2023
1 parent 69cab28 commit 6a4f2ea
Show file tree
Hide file tree
Showing 22 changed files with 553 additions and 8 deletions.
23 changes: 22 additions & 1 deletion packages/li-core-assets/src/services/fetch-dataset/helper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { DatasetFilter, DatasetServiceParams } from '@antv/li-sdk';
import { parserDataWithGeo, applyDatasetFilter } from '@antv/li-sdk';
import { applyDatasetFilter, isJSFunction, parseFunction, parserDataWithGeo } from '@antv/li-sdk';

const Chache = new Map();

Expand All @@ -8,6 +8,8 @@ type QueryDataParams = {
requestOptions: Omit<RequestInit, 'body'> & {
body?: Record<string, any>;
};
onComplete?: { type: 'JSFunction'; value: string };
onError?: { type: 'JSFunction'; value: string };
};

type Params = DatasetServiceParams<QueryDataParams>;
Expand Down Expand Up @@ -62,8 +64,19 @@ export const getFetchData = (params: Params) => {
return datasetFilterService({ data, filter }, signal);
}

const { onComplete, onError } = properties;

return fetch(properties.url, requestInit)
.then((res) => res.json())
.then((res) => {
if (onComplete && isJSFunction(onComplete)) {
const _onComplete = parseFunction(onComplete.value);
if (_onComplete) return _onComplete(res);

return res;
}
return res;
})
.then((_data) => {
if (Array.isArray(_data) && _data.length === 0 ? true : typeof _data[0] === 'object') {
const formatData = parserDataWithGeo(_data);
Expand All @@ -74,5 +87,13 @@ export const getFetchData = (params: Params) => {
}

return Promise.reject(new Error('数据格式不是数组对象, 请检查数据格式是否正确。'));
})
.catch((err) => {
if (onError && isJSFunction(onError)) {
const _onError = parseFunction(onError.value);
if (_onError) return _onError(err);

return err;
}
});
};
99 changes: 99 additions & 0 deletions packages/li-editor/docs/common/my-assets/fetch-dataset/helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import type { DatasetFilter, DatasetServiceParams } from '@antv/li-sdk';
import { applyDatasetFilter, isJSFunction, parseFunction, parserDataWithGeo } from '@antv/li-sdk';

const Chache = new Map();

type QueryDataParams = {
url: string;
requestOptions: Omit<RequestInit, 'body'> & {
body?: Record<string, any>;
};
onComplete?: { type: 'JSFunction'; value: string };
onError?: { type: 'JSFunction'; value: string };
};

type Params = DatasetServiceParams<QueryDataParams>;

const datasetFilterService = async (
params: { filter?: DatasetFilter; data: Record<string, any>[] },
signal: AbortSignal,
) => {
const { data, filter } = params;

if (!filter) {
return data;
}

let filterData: Record<string, any>[];

try {
filterData = await applyDatasetFilter(data, filter);
} catch (error) {
const err = new Error(`applyDatasetFilter is failure, filter data: '${JSON.stringify(filter)}'.`);
console.error(err);
return Promise.reject(err);
}

if (signal.aborted) {
return Promise.reject('Aborted the request');
}

return filterData;
};

/**
* 通过 fetch 获取的数据
*/
export const getFetchData = (params: Params) => {
const { properties, filter, signal } = params;
const requestkey = properties.url + properties.requestOptions.method + JSON.stringify(properties.requestOptions.body);
const defaultRequestInit: RequestInit = {
mode: 'cors',
cache: 'default',
signal,
};
const requestInit: RequestInit = Object.assign(defaultRequestInit, {
body:
typeof properties.requestOptions.body === 'object'
? JSON.stringify(properties.requestOptions.body)
: properties.requestOptions.body,
});

if (Chache.has(requestkey)) {
const data = Chache.get(requestkey);
return datasetFilterService({ data, filter }, signal);
}

const { onComplete, onError } = properties;

return fetch(properties.url, requestInit)
.then((res) => res.json())
.then((res) => {
if (onComplete && isJSFunction(onComplete)) {
const _onComplete = parseFunction(onComplete.value);
if (_onComplete) return _onComplete(res);

return res;
}
return res;
})
.then((_data) => {
if (Array.isArray(_data) && _data.length === 0 ? true : typeof _data[0] === 'object') {
const formatData = parserDataWithGeo(_data);

Chache.set(requestkey, formatData);

return datasetFilterService({ data: formatData, filter }, signal);
}

return Promise.reject(new Error('数据格式不是数组对象, 请检查数据格式是否正确。'));
})
.catch((err) => {
if (onError && isJSFunction(onError)) {
const _onError = parseFunction(onError.value);
if (_onError) return _onError(err);

return err;
}
});
};
12 changes: 12 additions & 0 deletions packages/li-editor/docs/common/my-assets/fetch-dataset/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { implementService } from '@antv/li-sdk';
import { getFetchData } from './helper';

export default implementService({
version: 'v0.1',
metadata: {
name: 'GET_FETCH_DATA_LIST',
displayName: '通过 fetch 获取数据',
type: 'Dataset',
},
service: getFetchData,
});
3 changes: 3 additions & 0 deletions packages/li-editor/docs/common/my-assets/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { implementWidget } from '@antv/li-sdk';
import React from 'react';
import BubbleLayer from './BubbleLayer';
import ChoroplethLayer from './ChoroplethLayer';
import GET_FETCH_DATA_LIST from './fetch-dataset';
import MyLayout from './MyLayout';

const AttributeInfor = implementWidget({
Expand Down Expand Up @@ -50,9 +51,11 @@ const ZoomControl = implementWidget({

const layers = [BubbleLayer, ChoroplethLayer];
const widgets = [MyLayout, AttributeInfor, ZoomControl];
const services = [GET_FETCH_DATA_LIST];

export default {
version: 'v0.1',
layers,
widgets,
services,
};
2 changes: 2 additions & 0 deletions packages/li-editor/src/constants/registry-default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
DatasetPreview,
DatasetsPanel,
Export,
FetchDataset,
FiltersPanel,
Folder,
LayersPanel,
Expand All @@ -19,6 +20,7 @@ export const Registry_Default_Editor_Widgets: ImplementEditorWidget[] = [
WidgetsPanel,
UploadDataset,
TilesetsDataset,
FetchDataset,
DatasetPreview,
MapSetting,
Export,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,9 @@ const DatasetPreview = (props: DatasetPreviewProps) => {
className="li-dataset-preview"
title={`${editorDataset.metadata.name}`}
open={visible}
bodyStyle={{ padding: 0 }}
destroyOnClose
width={'calc(100vw - 200px)'}
footer={false}
footer={null}
onCancel={() => onCancel()}
>
<Table
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ const AddDatasetModal = ({ title, visible, onClose, onSubmit }: AddDatasetModalP
destroyOnClose
open={visible}
footer={null}
bodyStyle={{ paddingBottom: 0 }}
onCancel={onClose}
>
{segmentedOptions.length > 1 && (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Input, InputNumber, Select, Switch } from 'antd';
import React, { useEffect, useState } from 'react';
import './index.less';

type Props = {
onChange?: (e: any) => void;
};
type Type = 'string' | 'expression' | 'boolean' | 'number';

const DynamicFormItemValue = (props: Props) => {
const [type, setType] = useState<Type>('string');
const [val, setVal] = useState<string | boolean | number>();
const { onChange } = props;

const onValueChange = (val: string | boolean | number) => {
setVal(val);
};

const onTypeChange = (types: Type) => {
setType(types);
};

useEffect(() => {
if (type === 'expression') {
const vals = val ? { type: 'JSExpression', value: `${val}` } : null;
return onChange?.(vals);
}

return onChange?.(val);
}, [type, val]);

return (
<div className="li-dynamic-form-item__item__value">
<div className="li-dynamic-form-item__item__value__content">
{type === 'string' && <Input placeholder="请输入" onChange={(e) => onValueChange(e.target.value)} />}
{type === 'boolean' && <Switch onChange={onValueChange} />}
{type === 'number' && <InputNumber placeholder="请输入" onChange={(e) => onValueChange(Number(e))} />}
{type === 'expression' && (
<Input
prefix="{{"
suffix="}}"
placeholder="请输入 JS 表达式"
onChange={(e) => onValueChange(e.target.value)}
/>
)}
</div>
<div className="li-dynamic-form-item__item__value__type">
<Select
style={{ width: '90px' }}
value={type}
onChange={onTypeChange}
options={[
{ value: 'string', label: '字符串' },
{ value: 'boolean', label: '布尔值' },
{ value: 'number', label: '数值' },
// { value: 'expression', label: '表达式' },
]}
/>
</div>
</div>
);
};

export default DynamicFormItemValue;
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { isEmpty } from 'lodash-es';

export const arrayConversionObject = (initialVal: { field: string; value: string | boolean }[]) => {
if (isEmpty(initialVal)) {
return {};
}

const new_val: Record<string, string | boolean> = {};

for (let i = 0; i < initialVal.length; i++) {
if (initialVal[i]?.field) {
new_val[initialVal[i].field] = initialVal[i].value;
}
}

return new_val;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
@import '../../../theme/index.less';

.li-dynamic-form-item__item {
display: flex;
flex: 1;
justify-content: space-between;
height: 42px;
overflow: hidden;

.ant-form-item {
margin-bottom: 10px;
}

&__field {
width: 200px;
}

&__icon {
width: 10px;
color: @border-primary-color;
}

&__value {
display: flex;
align-items: center;
justify-content: space-between;
width: 450px;
overflow: hidden;

&__content {
width: 100%;

.ant-input-number {
width: 100%;
}

.ant-input-number-input {
vertical-align: baseline;
}
}

&__type {
margin-left: 5px;
}
}
}

.li-dynamic-form-item__add-btn {
width: 100px;
margin: 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import { Button, Form, Input, Space } from 'antd';
import React from 'react';
import DynamicFormItemValue from './DynamicFormItemValue';
import './index.less';

type DynamicFormItemProps = {
fieldName: string;
};

const DynamicFormItem = (props: DynamicFormItemProps) => {
const { fieldName } = props;

return (
<Form.List name={fieldName}>
{(fields, { add, remove }) => (
<>
{fields.map(({ key, name, ...restField }) => (
<Space key={key} align="baseline" className="li-dynamic-form-item__item">
<Form.Item {...restField} name={[name, 'field']} className="li-dynamic-form-item__item__field">
<Input placeholder="name" />
</Form.Item>

<div className="li-dynamic-form-item__item__icon">:</div>
<Form.Item {...restField} name={[name, 'value']} className="li-dynamic-form-item__item__value">
<DynamicFormItemValue />
</Form.Item>
<MinusCircleOutlined onClick={() => remove(name)} />
</Space>
))}
<Form.Item className="li-dynamic-form-item__add-btn">
<Button onClick={() => add()} block icon={<PlusOutlined />}>
添加
</Button>
</Form.Item>
</>
)}
</Form.List>
);
};

export default DynamicFormItem;
Loading

0 comments on commit 6a4f2ea

Please sign in to comment.