Commit b54804b5 by zhaochengxiang

模型打标签

parent cba055b8
......@@ -54,7 +54,7 @@ module.exports = {
changeOrigin: true,
},
'/data-quality': {
target: 'http://localhost:3000',
target: 'http://192.168.0.111:8189',
changeOrigin: true,
},
},
......
import * as service from '../service/tag';
import { call } from 'redux-saga/effects';
export function* batchAddTagResource(payload) {
return yield call(service.batchAddTagResource, payload);
}
export function* deleteTagResource(payload) {
return yield call(service.deleteTagResource, payload);
}
export function* getResourceTagIn(payload) {
return yield call(service.getResourceTagIn, payload);
}
export function* batchAddTagResourceByTagList(payload) {
return yield call(service.batchAddTagResourceByTagList, payload);
}
export function* getTagByKeywordAndCreator(payload) {
return yield call(service.getTagByKeywordAndCreator, payload);
}
export function* getSupportTagForm(payload) {
return yield call(service.getSupportTagForm, payload);
}
export function* saveTag(payload) {
return yield call(service.saveTag, payload);
export function* deleteTags(payload) {
return yield call(service.deleteTags, payload);
}
export function* getTagIdListByNameList(payload) {
return yield call(service.getTagIdListByNameList, payload);
export function* addTags(payload) {
return yield call(service.addTags, payload);
}
\ No newline at end of file
import { GetJSON, PostJSON, Delete } from "../util/axios";
export function batchAddTagResource(payload) {
return PostJSON("/tagger/tagResource/batchAddTagResource", payload);
}
export function deleteTagResource(payload) {
return Delete("/tagger/tagResource/deleteTagResource", payload);
}
import { PostJSON, Delete, callFetchRaw } from "../util/axios";
export function getResourceTagIn(payload) {
return PostJSON("/tagger/tagResource/getResourceTagIn", payload);
}
export function batchAddTagResourceByTagList(payload) {
return PostJSON("/tagger/tagResource/batchAddTagResourceByTagList", payload)
return callFetchRaw('post', '/tagger/manager/tag/resource/getResourceTagIn', payload)
}
export function getTagByKeywordAndCreator(payload) {
return GetJSON("/tagger/tag/getTagByKeywordAndCreator", payload);
export function deleteTags(payload) {
return Delete("/tagger/manager/tag/resource/deleteTagInResource", payload);
}
export function getSupportTagForm(payload) {
return GetJSON("/tagger/tag/getSupportTagForm", payload);
}
export function saveTag(payload) {
return PostJSON("/tagger/tag/saveTag", payload);
}
export function getTagIdListByNameList(payload) {
return PostJSON("/tagger/tag/getTagIdListByNameList", payload);
}
export function addTags(payload) {
return PostJSON("/tagger/manager/tag/resource/batchAddTagResourceByTagList", payload);
}
\ No newline at end of file
......@@ -162,4 +162,28 @@ export function PostFile(url, payload) {
return fileplain.post(url, formData, { params } ).then(
callback
)
}
\ No newline at end of file
}
const config = {
baseURL,
headers: {
'Content-Type': 'multipart/form-data',
},
cache: 'no-cache',
}
export const callFetchRaw = (method, url, options) => {
const { params, ...reqConfig } = options;
var bodyFormData = new FormData();
Object.keys(params||[]).forEach(key => {
bodyFormData.append(key, params[key]);
});
return axios.request({
method,
url,
data: bodyFormData,
...config,
...reqConfig
})
}
......@@ -6,9 +6,7 @@ import MetadataInfo from './MetadataInfo';
import { dispatch } from '../../../../model';
import { highlightSearchContentByTerms, showMessage, getAssetRange, checkMenuAdmit } from '../../../../util';
import { AppContext } from '../../../../App';
import Tag from '../../Tag';
import Separate from './Separate';
import AssetTagModal from './AssetTagModal';
import { AnchorId, AnchorDirId, AssetManageReference } from '../../../../util/constant';
import PermissionButton from '../../../../util/Component/PermissionButton';
import DataQuality, { DataQualityFeignTagList } from '../../../QianKun/data-quality'
......@@ -28,8 +26,6 @@ const AssetAction = (props) => {
const [ assetPaths, setAssetPaths ] = useState([]);
const [ resourceRelations, setResourceRelations ] = useState([]);
const { assets, attributes, attributesFoldMap } = assetParams;
const [ assetTagModalVisible, setAssetTagModalVisible ] = useState(false);
const [ selectTag, setSelectTag ] = useState({});
useEffect(() => {
if (action === 'add') {
......@@ -346,23 +342,6 @@ const AssetAction = (props) => {
})
}
const onAssetTag = (value) => {
setSelectTag(value);
setAssetTagModalVisible(true);
}
const onAssetTagModalCancel = (refresh = false) => {
setAssetTagModalVisible(false);
if (refresh) {
getResourceRelations();
let event = new Event('storage');
event.key = 'tagChange';
window?.dispatchEvent(event);
}
}
const formItemLayout = {
labelCol: {
xs: { span: 24 },
......@@ -608,21 +587,6 @@ const AssetAction = (props) => {
</div>
</React.Fragment>
}
<AppContext.Consumer>
{
value => {
return (
<AssetTagModal
visible={assetTagModalVisible}
id={id}
tag={selectTag}
creator={value?.user?.userName||''}
onCancel={onAssetTagModalCancel}
/>
);
}
}
</AppContext.Consumer>
</div>
)
}
......
import React, { useEffect, useState } from 'react';
import { Modal, Form, Input, Space, Button, Select } from 'antd';
import { dispatch } from '../../../../model';
import { AppContext } from '../../../../App';
import MetadataInfo from './MetadataInfo';
import { showMessage, getAssetRange } from '../../../../util';
import { AssetManageReference } from '../../../../util/constant';
const { Option } = Select;
const DirsSelect = ({ value = [], data = [], onChange, ...restProps }) => {
const onDirChange = (value, option) => {
if (option.length > 0) {
const recentItem = option[option.length-1];
const recentItemTheme = recentItem.children.split('/')[0];
const newValue = [];
option.forEach(item => {
if (item.children.split('/')[0] !== recentItemTheme) {
newValue.push(item.value);
}
})
newValue.push(recentItem.value);
onChange(newValue);
} else {
onChange(value);
}
}
return (
<Select
mode="multiple"
value={value||[]}
onChange={onDirChange}
allowClear
style={{ width: '100%' }}
>
{
(data||[]).map((dir, index) => {
return (
<Option key={index} value={dir.id||''}>{dir.path||''}</Option>
);
})
}
</Select>
)
}
const AssetTagModal = (props) => {
const { onCancel, visible, id, tag, creator } = props;
const [ form ] = Form.useForm();
const [ confirmLoading, setConfirmLoading ] = useState(false);
const [ elements, setElements ] = useState([]);
const [ asset, setAsset ] = useState('');
const [ metadataId, setMetadataId ] = useState('');
const [ dirs, setDirs ] = useState([]);
useEffect(() => {
if (visible && (id||'')!=='') {
getAsset();
} else {
setMetadataId('');
}
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [ visible, id ])
useEffect(() => {
if (visible && (tag?.name||'')!=='') {
getDirectoryByName(tag?.name);
}
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [ visible ])
const getAsset = () => {
dispatch({
type: 'assetmanage.getDataAssetDetail',
payload: {
dataAssetId: id,
range: getAssetRange(AssetManageReference),
},
callback: data => {
setAsset(data);
setElements(data?.elements||[]);
setMetadataId(data.mid||'');
let _fieldsValue = {};
(data.elements||[]).forEach(element => {
_fieldsValue[element.name] = element.value;
})
form.setFieldsValue(_fieldsValue);
}
})
}
const getDirectoryByName = (keyword) => {
dispatch({
type: 'assetmanage.listDirectoryByName',
payload: {
name: keyword
},
callback: data => {
setDirs(data||[]);
}
});
}
//选中元数据后 内容回填.
const fillElementValueBeforeCreate = (mid = metadataId) => {
dispatch({
type: 'assetmanage.fillElementValueBeforeCreate',
payload: {
params: {
metadataIds: mid
}
},
callback: data => {
let _fieldsValue = {};
(data||[]).forEach(element => {
_fieldsValue[element.name] = element.value;
})
form.setFieldsValue(_fieldsValue);
}
})
}
const onOk = async() => {
try {
const row = await form.validateFields();
const newElements = [...elements];
(newElements||[]).forEach(element => {
if (row.hasOwnProperty(element.name)) {
element.value = row[element.name];
}
});
const params = {
dirId: (row.dirs||[]).join(','),
}
if ((metadataId||'')!=='') {
params.metadataId = metadataId;
}
setConfirmLoading(true);
dispatch({
type: 'assetmanage.checkCodeIsExist',
payload: {
data: { ...asset, elements: newElements }
},
callback: isExist => {
if (isExist === 'true') {
setConfirmLoading(false);
showMessage('warn', '已存在相同的资产编号,请重新输入');
} else {
dispatch({
type: 'assetmanage.addOrUpdateDataAsset',
payload: {
params,
data: { ...asset, elements: newElements }
},
callback: data => {
const ids = [id];
if (data && ((data?.id||'')!=='') && (data?.id!==id)) {
ids.push(data?.id);
}
dispatch({
type: 'tag.batchAddTagResourceByTagList',
payload: {
params: {
tagIds: tag?.tagId,
resourceIds: ids.join(','),
type: 'dataAsset',
creator
}
},
callback: () => {
reset();
onCancel && onCancel(true);
},
error: () => {
setConfirmLoading(false);
}
})
},
error: () => {
setConfirmLoading(false);
}
})
}
},
error: () => {
setConfirmLoading(false);
}
})
} catch (errInfo) {
console.log('Validate Failed:', errInfo);
setConfirmLoading(false);
}
}
const reset = () => {
setConfirmLoading(false);
form?.resetFields();
}
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 17 },
},
};
return (
<Modal
forceRender
className='asset-add'
title='资产打标签'
visible={ visible }
width={ 600 }
onCancel={() => {
reset();
onCancel && onCancel();
} }
footer={
<Space>
<Button onClick={() => onCancel && onCancel() }>取消</Button>
<Button type="primary" onClick={ onOk } loading={ confirmLoading }>确定</Button>
</Space>
}
>
{
visible && <Form {...formItemLayout} form={form}>
<AppContext.Consumer>
{
value => {
value?.onGlobalStateChange&&value?.onGlobalStateChange((state, prev) => {
if (state.message === 'data-govern-show-metadata-list-callback-message') {
setMetadataId(state.data?.metadataId||'');
form?.setFieldsValue({ '资产项': state.data?.metadataInfoJson||'' });
if ((state.data?.metadataId||'') !== '') {
fillElementValueBeforeCreate(state.data?.metadataId||'');
}
}
});
return (
<>
<Form.Item
label='资产标签'
name='tag'
>
<span>{tag?.name||''}</span>
</Form.Item>
<Form.Item
label='挂载目录'
name='dirs'
required={true}
>
<DirsSelect data={dirs||[]} />
</Form.Item>
{ (elements||[]).map((element, index) => {
return (
<Form.Item
label={element.name}
name={element.name}
key={index}
>
{ (element.name==='资产项') ? <MetadataInfo /> : <Input disabled={element.manualMaintain==='否'} /> }
</Form.Item>
)
})}
</>
);
}}
</AppContext.Consumer>
</Form>
}
</Modal>
);
}
export default AssetTagModal;
......@@ -7,9 +7,9 @@ import { dispatch } from '../../../../model';
import { showMessage, getQueryParam, isSzseEnv, formatDate } from '../../../../util';
import { AnchorId, AnchorTimestamp, Action, CatalogId, ModelerId } from '../../../../util/constant';
import ExpandedModelTable from "./ExpandedModelTable";
// import Tag from "../../Tag";
import { useContextMenu, Menu as RcMenu, Item as RcItem } from "react-contexify";
import PermissionRcItem from '../../../../util/Component/PermissionRcItem';
import TagCell from './tag-help';
import './ModelTable.less';
import 'react-contexify/dist/ReactContexify.css';
......@@ -140,6 +140,7 @@ const ModelTable = (props) => {
const [ expandedSelectedRowKeys, setExpandedSelectedRowKeys ] = useState([]);
const [ currentItem, setCurrentItem ] = useState(null);
const [ scrollRowIndex, setScrollRowIndex ] = useState();
const [resoureTagMap, setResourceTagMap] = useState();
const expandedDataMapRef = useRef(new Map());
const shouldScrollRef = useRef(false);
......@@ -255,12 +256,27 @@ const ModelTable = (props) => {
)
}
},
{
name:'标签',
key:'tag',
width:360,
className: 'table-tag-cell',
formatter(props) {
return (
props.row?.state?.id === '4' ? <TagCell
id={props.row?.id}
type='model'
tags={resoureTagMap?.[`${props.row?.id}`]}
/> : null
)
}
}
];
const columns = useMemo(() => {
let newCols = [...cols];
if ((visibleColNames||[]).length > 0) {
newCols = newCols.filter(col => visibleColNames.indexOf(col.name)!==-1 || col.name==='序号');
newCols = newCols.filter(col => visibleColNames.indexOf(col.name)!==-1 || col.name==='序号' || col.name==='标签');
if (visibleColNames.indexOf('模型描述') === -1) {
newCols[newCols.length-1].width = null;
......@@ -269,7 +285,7 @@ const ModelTable = (props) => {
return newCols;
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [visibleColNames])
}, [visibleColNames, resoureTagMap])
const summarySelectedCount = useMemo(() => {
let newSelectedRowKeys = Array.from(new Set([...selectedRowKeys, ...expandedSelectedRowKeys]));
......@@ -288,7 +304,9 @@ const ModelTable = (props) => {
}, [selectedRowKeys, expandedSelectedRowKeys, data])
useEffect(() => {
if (data && gridRef.current) {
if (data) {
getResourceTag();
setTimeout(() => {
try {
gridRef.current?.scrollToRow(0);
......@@ -342,6 +360,27 @@ const ModelTable = (props) => {
}
})
const getResourceTag = () => {
const ids = (data??[]).map(item => item.id);
if (ids.length > 0) {
dispatch({
type: 'tag.getResourceTagIn',
payload: {
params: {
resourceIds: ids,
includeAll: true,
includePrivate: true
}
},
callback: data => {
setResourceTagMap(data?.data)
}
});
} else {
setResourceTagMap()
}
}
const modelEventChange = (e) => {
if (e.key === 'modelChange') {
expandedDataMapRef.current.delete(LocalStorage.get('modelId'));
......@@ -622,8 +661,9 @@ const ModelTable = (props) => {
<PermissionRcItem
id={`action-${index}`}
onClick={handleItemClick}
permissionKey={item.cnName}
permissions={currentItem?.optionList?.filter(item => item.enabled).map(item => item.name)}
defaultPermission={true}
// permissionKey={item.cnName}
// permissions={currentItem?.optionList?.filter(item => item.enabled).map(item => item.name)}
>
{item.cnName||''}
</PermissionRcItem>
......
import React, { useEffect } from 'react'
import { Tooltip, Tag, Typography, Space, Button, Modal, Spin } from 'antd'
import {
EllipsisOutlined,
PlusOutlined,
} from '@ant-design/icons'
import DataQuality, { DataQualityFeighTagSelect } from '../../../QianKun/data-quality'
import { showMessage } from '../../../../util'
import { dispatch } from '../../../../model'
import { AppContext } from '../../../../App'
const FC = ({ type, id, tags }) => {
const [data, setData] = React.useState([])
const [tagSelectPopupParams, setTagSelectPopupParams] = React.useState({
visible: false,
type: undefined,
items: undefined,
})
useEffect(() => {
setData(tags)
}, [tags])
const getTags = () => {
dispatch({
type: 'tag.getResourceTagIn',
payload: {
params: {
resourceIds: id,
includeAll: true,
includePrivate: true
}
},
callback: data => {
setData(data?.data?.[`${id}`]);
}
});
}
return (
<React.Fragment>
<TagCell
value={data}
onChange={(val) => {
getTags()
}}
onAdd={(val) => {
setTagSelectPopupParams({
visible: true,
type,
items: [{ resourceId: id }]
})
}}
/>
<TagSelectPopup
onCancel={() => {
setTagSelectPopupParams({
visible: false,
type: undefined,
items: undefined
})
}}
onChange={(val) => {
getTags()
}}
{...tagSelectPopupParams}
/>
</React.Fragment>
)
}
export default FC
export const TagRender = (props) => {
const { data, closable, onClose, style, ...restProps } = props;
const color = React.useMemo(() => {
if (data) {
return (data.type==='public') ? '#196AD2' : '#4C7813'
}
return ''
}, [data])
const onPreventMouseDown = (event) => {
event.preventDefault();
event.stopPropagation();
};
return (
<Tooltip title={data?.name}>
<Tag
onMouseDown={onPreventMouseDown}
closable={closable}
onClose={onClose}
style={{
marginRight: 3,
padding: '2px 7px',
textAlign: 'center',
borderColor: color,
color,
backgroundColor: '#fff',
...style
}}
{...restProps}
>
<Typography.Text ellipsis={true} style={{ width: 56 }}>
<span style={{ color }}>{data?.name}</span>
</Typography.Text>
</Tag>
</Tooltip>
);
};
export function TagCell({ value, readonly = false, onChange, onAdd }) {
const maxShowCount = 3
const [haveMore, _value] = React.useMemo(() => {
return [(value??[]).length>maxShowCount, (value??[]).slice(0, maxShowCount)]
}, [value])
const onClose = (e, tag) => {
e.preventDefault()
if (tag?.id) {
dispatch({
type: 'tag.deleteTags',
payload: {
tagInResourceIds: tag?.id
},
callback: () => {
showMessage('success', '删除成功');
onChange?.()
}
})
}
}
return (
<div style={{
display: 'flex',
marginRight: '12px',
justifyContent: 'space-between',
alignItems: 'center',
height: '100%',
}}>
{
_value?.map((item, index) => <TagRender
key={index}
data={item}
closable={!readonly&&item?.deleteAble}
onClose={(e) => {
onClose(e, item)
}}
style={{ marginRight: 0 }}
/>)
}
{
haveMore && <Tooltip title={
<Space wrap={true}>
{
value?.map((item, index) => <TagRender
key={index}
data={item}
closable={!readonly&&item?.deleteAble}
onClose={(e) => {
onClose(e, item)
}}
/>)
}
</Space>
}>
<Button size='small' icon={<EllipsisOutlined />}></Button>
</Tooltip>
}
{
!readonly && <Button size='small' icon={<PlusOutlined />} onClick={(e) => {
e.stopPropagation()
onAdd?.()
}}></Button>
}
<React.Fragment>
{
(_value??[]).length < maxShowCount && Array.from({ length: maxShowCount-_value.length }, (_, key) => <div key={key} style={{ width: 80 }} />)
}
</React.Fragment>
{
!haveMore && <div style={{ width: 24 }} />
}
</div>
)
}
export function TagSelectPopup({ visible, type, items, onCancel, onChange }) {
const [waiting, setWaiting] = React.useState(false)
const [selectedRows, setSelectedRows] = React.useState([])
const [showDataQuality, setShowDataQuality] = React.useState(false)
const app = React.useContext(AppContext)
React.useEffect(() => {
if (visible) {
setTimeout(() => {
setShowDataQuality(true)
}, 300)
}
}, [visible])
const close = () => {
setWaiting(false)
setShowDataQuality(false)
setSelectedRows([])
onCancel?.()
}
const save = async () => {
if (items) {
if ((selectedRows??[]).length === 0) {
showMessage('warn', '请先选择标签')
return
}
setWaiting(true)
dispatch({
type: 'tag.addTags',
payload: {
params: {
tagIds: (selectedRows??[]).map(item => item.id).toString(),
resourceIds: (items??[]).map(item => item.resourceId).toString(),
type,
catalogId: (items??[]).length>0?items[0].resourceCatalogId:'',
creator: app?.user?.userName,
}
},
callback: () => {
setWaiting(false)
onChange?.(selectedRows)
close()
},
error: () => {
setWaiting(false)
}
})
} else {
onChange?.(selectedRows)
close()
}
}
const footer = React.useMemo(() => {
return [
<Button key={'cancel'}
onClick={() => close()}
>取消</Button>,
<Button key={'save'} type='primary'
onClick={() => save()}
>确定</Button>
]
}, [close, save])
return (
<Modal
visible={visible}
footer={footer}
width='90%'
bodyStyle={{ padding: '15px 15px 0px 15px', overflowX: 'auto', minHeight: '60vh' }}
title='选择标签'
centered destroyOnClose
onCancel={() => { close() }}
>
<Spin spinning={waiting} >
{
showDataQuality ? <DataQuality type={DataQualityFeighTagSelect} onChange={(val) => {
setSelectedRows(val)
}} /> : null
}
</Spin>
</Modal>
)
}
\ No newline at end of file
.add-tag-modal {
.yy-select-item-group {
min-height: 0 !important;
height: 0 !important;
}
}
\ No newline at end of file
import React, { useEffect, useState } from 'react';
import { Button, Tag, Modal } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import AddTagModal from './Component/AddTagModal';
import { dispatch } from '../../../model';
const TagColumn = (props) => {
const { id, type = 'dataAsset', creator = '', styleType = 'simple', onAssetTag } = props;
const [ ownTags, setOwnTags ] = useState([]);
const [ addTagModalVisible, setAddTagModalVisible ] = useState(false);
const [modal, contextHolder] = Modal.useModal();
useEffect(() => {
if ((id||'') !== '') {
getTags();
} else {
setOwnTags([]);
}
window?.addEventListener("storage", tagEventChange);
return () => {
window?.removeEventListener("storage", tagEventChange);
}
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [id])
const tagEventChange = (e) => {
if (e.key === 'tagChange' && (id||'') !== '') {
getTags();
}
}
const getTags = () => {
dispatch({
type: 'tag.getResourceTagIn',
payload: {
params: {
resourceIds: id,
type,
creator,
includeAll: true
}
},
callback: data => {
if (data) {
setOwnTags(data[id]||[]);
}
}
})
}
const onAddBtnClick = () => {
setAddTagModalVisible(true);
}
const onAddTagModalCancel = (refresh=false) => {
setAddTagModalVisible(false);
refresh && getTags();
}
const onAssetTagCancel = (selectTag) => {
setAddTagModalVisible(false);
onAssetTag && onAssetTag(selectTag);
}
const handleClose = (index) => {
const _tag = ownTags[index];
modal.confirm({
title: '提示!',
content: '您确定要删除该标签吗?',
onOk: () => {
dispatch({
type: 'tag.deleteTagResource',
payload: {
esIds: _tag.esId
},
callback: () => {
getTags();
}
})
}
});
}
// let tagsContent = '';
// (ownTags||[]).forEach((item, index) => {
// if (index === 0) {
// tagsContent = (item.name||'');
// } else {
// tagsContent += ',' + (item.name||'');
// }
// })
return (
<React.Fragment>
{
(styleType==='simple') && <div>
{
(ownTags||[]).map((item, index) => {
return (
<Tag
key={index}
style={{ marginBottom: 5 }}
color={item.type==='public'?'#2db7f5':'volcano'}
>
{ item.name||'' }
</Tag>
);
})
}
</div>
}
{
(styleType==='complex') && <div>
<div>
{
(ownTags||[]).map((item, index) => {
return (
<Tag
key={index}
closable
onClose={e => {
e.preventDefault();
handleClose(index);
}}
style={{ marginBottom: 5 }}
color={item.type==='public'?'#2db7f5':'volcano'}
>
{ item.name||'' }
</Tag>
);
})
}
</div>
<div>
<Button
size='small'
type='text'
icon={<PlusOutlined />}
onClick={onAddBtnClick}
>
新增标签
</Button>
</div>
</div>
}
<AddTagModal
visible={addTagModalVisible}
id={id}
type={type}
creator={creator}
onCancel={onAddTagModalCancel}
onAssetTag={onAssetTagCancel}
/>
{contextHolder}
</React.Fragment>
);
}
export default TagColumn;
\ No newline at end of file
......@@ -2,10 +2,10 @@ import React from 'react'
import { loadMicroApp } from 'qiankun'
import { AppContext } from '../../App'
export const DataQualityFeignTagCell = 'data-quality-feign-tag-cell'
export const DataQualityFeighTagSelect = 'data-quality-feign-tag-select'
export const DataQualityFeignTagList = 'data-quality-feign-tag-list'
const FC = ({ type, data }) => {
const FC = ({ type, data, onChange }) => {
const containerRef = React.useRef()
const microApp = React.useRef()
const app = React.useContext(AppContext)
......@@ -20,7 +20,8 @@ const FC = ({ type, data }) => {
props: {
type,
data,
user: app?.user
user: app?.user,
onChange
},
},
);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment