Commit 82c5dcfe by zhaochengxiang

推荐优化

parent 2799b217
...@@ -14,7 +14,7 @@ import { dispatch, dispatchLatest } from '../../../../model'; ...@@ -14,7 +14,7 @@ import { dispatch, dispatchLatest } from '../../../../model';
import { addEventListenerForSidebar, removeEventListenerForSidebar } from './Help'; import { addEventListenerForSidebar, removeEventListenerForSidebar } from './Help';
import { AppContext } from '../../../../App'; import { AppContext } from '../../../../App';
import DebounceInput from './DebounceInput'; import DebounceInput from './DebounceInput';
import SuggestTable from './SuggestTable'; import Suggest from './suggest';
import { AttentionSvg, UnAttentionSvg } from './ModelSvg'; import { AttentionSvg, UnAttentionSvg } from './ModelSvg';
import { EditModelContext } from './ContextManage'; import { EditModelContext } from './ContextManage';
import { ValidateTip } from './ImportActionHeader'; import { ValidateTip } from './ImportActionHeader';
...@@ -152,12 +152,13 @@ export const EditableCell = ({ ...@@ -152,12 +152,13 @@ export const EditableCell = ({
index, index,
datatypes, datatypes,
require, require,
onPressEnter,
children, children,
...restProps ...restProps
}) => { }) => {
let editingComponent = null; let editingComponent = null;
if (editing) { if (editing) {
let inputNode = <InputDebounce />; let inputNode = <InputDebounce onPressEnter={() => { onPressEnter?.() }} />;
if (inputType === 'check') { if (inputType === 'check') {
inputNode = <Checkbox />; inputNode = <Checkbox />;
...@@ -256,11 +257,6 @@ export const ImportActionTable = (props) => { ...@@ -256,11 +257,6 @@ export const ImportActionTable = (props) => {
const [ form ] = Form.useForm(); const [ form ] = Form.useForm();
const [ editingKey, setEditingKey ] = useState(''); const [ editingKey, setEditingKey ] = useState('');
const [ loadingSuggest, setLoadingSuggest ] = useState(false);
const [ suggests, setSuggests ] = useState([]);
const [ suggestHaveMore, setSuggestHaveMore ] = useState(false);
const [ suggestOffset, setSuggestOffset ] = useState(1);
const [ currentChangedValues, setCurrentChangedValues] = useState({});
const [ englishSuggests, setEnglishSuggests ] = useState([]); const [ englishSuggests, setEnglishSuggests ] = useState([]);
const [ keywordCondition, setKeywordCondition ] = useState({ keyword: '', needFilter: true }); const [ keywordCondition, setKeywordCondition ] = useState({ keyword: '', needFilter: true });
const { keyword, needFilter } = keywordCondition; const { keyword, needFilter } = keywordCondition;
...@@ -271,6 +267,12 @@ export const ImportActionTable = (props) => { ...@@ -271,6 +267,12 @@ export const ImportActionTable = (props) => {
const { pageNum, pageSize, filterData } = filterPageCondition; const { pageNum, pageSize, filterData } = filterPageCondition;
const [ insertIndex, setInsertIndex ] = useState(0); const [ insertIndex, setInsertIndex ] = useState(0);
const [ currentItem, setCurrentItem ] = useState(null); const [ currentItem, setCurrentItem ] = useState(null);
const [suggestParams, setSuggestParams] = useState({
visible: false,
name: undefined,
cnName: undefined,
triggerType: undefined,
})
const { attrIsEditingFunction } = useContext(EditModelContext); const { attrIsEditingFunction } = useContext(EditModelContext);
...@@ -837,7 +839,6 @@ export const ImportActionTable = (props) => { ...@@ -837,7 +839,6 @@ export const ImportActionTable = (props) => {
} }
setEditingKey(''); setEditingKey('');
setSuggests([]);
setEnglishSuggests([]); setEnglishSuggests([]);
} }
...@@ -858,70 +859,6 @@ export const ImportActionTable = (props) => { ...@@ -858,70 +859,6 @@ export const ImportActionTable = (props) => {
ownPrimaryKey = (modelerData.easyDataModelerPrimaryKey.filter(item => item.name===allValues.name).length>0); ownPrimaryKey = (modelerData.easyDataModelerPrimaryKey.filter(item => item.name===allValues.name).length>0);
} }
if (changedValues.hasOwnProperty('cnName') || changedValues.hasOwnProperty('name')) {
if (offset === 1) {
setSuggests([]);
}
const newData = [...data];
const index = newData.findIndex((item) => iid === item.iid);
if (index === -1) {
newData.splice(0, 0, { iid, ...allValues});
} else if (index !== -1) {
const item = newData[index];
newData.splice(index, 1, { ...item, ...allValues });
}
setSuggestHaveMore(false);
setCurrentChangedValues(changedValues);
function getSuggest() {
setLoadingSuggest(true);
dispatchLatest({
type: 'datamodel.suggest',
payload: {
params: {
name: allValues.name||'',
cnName: allValues.cnName||'',
topN: (offset+perSuggestCount-1),
offset
}
},
callback: data => {
setLoadingSuggest(false);
if (changedValues.hasOwnProperty('cnName')) {
const moreSuggests = (data||[]).length>0?(data[0].suggestions||[]):[];
const newSuggests = (offset===1 ? moreSuggests : [...suggests, ...moreSuggests]);
if (moreSuggests.length === perSuggestCount) {
setSuggestHaveMore(true);
}
setSuggestOffset(newSuggests.length+1);
setSuggests(newSuggests);
} else if (changedValues.hasOwnProperty('name')) {
const moreSuggests = (data||[]).length>1?(data[1].suggestions||[]):[];
const newSuggests = (offset===1 ? moreSuggests : [...suggests, ...moreSuggests]);
if (moreSuggests.length === perSuggestCount) {
setSuggestHaveMore(true);
}
setSuggestOffset(newSuggests.length+1);
setSuggests(newSuggests);
}
},
error: () => {
setLoadingSuggest(false);
}
})
}
if (changedValues.hasOwnProperty('cnName')) { if (changedValues.hasOwnProperty('cnName')) {
if (autoTranslateRef.current) { if (autoTranslateRef.current) {
dispatchLatest({ dispatchLatest({
...@@ -935,17 +872,11 @@ export const ImportActionTable = (props) => { ...@@ -935,17 +872,11 @@ export const ImportActionTable = (props) => {
if ((data?.translated||'') !== '') { if ((data?.translated||'') !== '') {
form.setFieldsValue({ name: data?.translated||'' }); form.setFieldsValue({ name: data?.translated||'' });
} }
getSuggest();
} }
}) })
} else {
getSuggest();
} }
} else if (changedValues.hasOwnProperty('name')) { } else if (changedValues.hasOwnProperty('name')) {
autoTranslateRef.current(changedValues.name===''); autoTranslateRef.current(changedValues.name==='');
getSuggest();
}
} else if(changedValues.hasOwnProperty('notNull') ) { } else if(changedValues.hasOwnProperty('notNull') ) {
if (!changedValues.notNull && ownPrimaryKey) { if (!changedValues.notNull && ownPrimaryKey) {
showMessage('info', '主键不允许为空'); showMessage('info', '主键不允许为空');
...@@ -965,10 +896,26 @@ export const ImportActionTable = (props) => { ...@@ -965,10 +896,26 @@ export const ImportActionTable = (props) => {
form.setFieldsValue({ form.setFieldsValue({
...restRecord ...restRecord
}); });
setSuggests([]);
}; };
const onColumnPressEnter = (title) => {
if (title === '中文名称') {
setSuggestParams({
visible: true,
name: form?.getFieldValue('name'),
cnName: form?.getFieldValue('cnName'),
triggerType: 'cnName',
})
} else if (title === '英文名称') {
setSuggestParams({
visible: true,
name: form?.getFieldValue('name'),
cnName: form?.getFieldValue('cnName'),
triggerType: 'name',
})
}
}
const mergedColumns = () => { const mergedColumns = () => {
if (editable) { if (editable) {
const _columns = cols.map((col) => { const _columns = cols.map((col) => {
...@@ -1004,7 +951,10 @@ export const ImportActionTable = (props) => { ...@@ -1004,7 +951,10 @@ export const ImportActionTable = (props) => {
colTitle: col.title, colTitle: col.title,
editing: isEditing(record), editing: isEditing(record),
datatypes: supportedDatatypes, datatypes: supportedDatatypes,
require: col.require require: col.require,
onPressEnter: () => {
onColumnPressEnter(col.title)
}
}), }),
}; };
}); });
...@@ -1049,20 +999,6 @@ export const ImportActionTable = (props) => { ...@@ -1049,20 +999,6 @@ export const ImportActionTable = (props) => {
setKeywordCondition({ keyword: value||'', needFilter: true }); setKeywordCondition({ keyword: value||'', needFilter: true });
} }
const loadMoreSuggests = (event) => {
event.stopPropagation();
onValuesChange(currentChangedValues, form.getFieldsValue(), suggestOffset);
}
const closeSuggests = (event) => {
event.stopPropagation();
setSuggestHaveMore(false);
setSuggests([]);
setSuggestOffset(1);
}
const displayMenu = (e) => { const displayMenu = (e) => {
show(e); show(e);
} }
...@@ -1232,34 +1168,6 @@ export const ImportActionTable = (props) => { ...@@ -1232,34 +1168,6 @@ export const ImportActionTable = (props) => {
//解决屏幕尺寸窄时,字段不好横向拖动的问题 //解决屏幕尺寸窄时,字段不好横向拖动的问题
y: tableWidth>1500?'100%':630, y: tableWidth>1500?'100%':630,
}} }}
expandable={{
columnWidth: 0,
expandedRowRender: record => (
<React.Fragment>
{
editingKey!=='' && <React.Fragment>
{
suggests && suggests.length>0 && (
<SuggestTable suggests={suggests} onSelect={onSuggestChange} />
)
}
</React.Fragment>
}
<div className='flex pt-3' style={{ justifyContent: 'center' }}>
<Tooltip title={!suggestHaveMore?'没有更多推荐字段': ''}>
<Button onClick={loadMoreSuggests} disabled={!suggestHaveMore} loading={loadingSuggest}>加载更多</Button>
</Tooltip>
<Button className='ml-3' onClick={closeSuggests}>收起推荐</Button>
</div>
</React.Fragment>
),
expandIcon: ({ expanded, onExpand, record }) => {
return null;
},
rowExpandable: record => (editingKey!==''&&((suggests||[]).length>0 || (englishSuggests||[]).length>0)),
expandedRowKeys: [editingKey]
}}
/> />
</Form> </Form>
</DndProvider> </DndProvider>
...@@ -1284,6 +1192,21 @@ export const ImportActionTable = (props) => { ...@@ -1284,6 +1192,21 @@ export const ImportActionTable = (props) => {
/> />
} }
<div onClick={(e) => { e.stopPropagation() }}>
<Suggest
{...suggestParams}
onCancel={() => {
setSuggestParams({
visible: false,
name: undefined,
cnName: undefined,
triggerType: undefined,
})
}}
onOk={onSuggestChange}
/>
</div>
<RcMenu id={MENU_ID} > <RcMenu id={MENU_ID} >
{ {
(menuData??[]).map(item => ( (menuData??[]).map(item => (
......
.suggest-table {
.yy-table {
margin: 0 !important;
max-height: 300px !important;
overflow: auto !important;
}
}
\ No newline at end of file
import React, { useState } from 'react'; import React from 'react'
import { Table, Tooltip, Typography } from 'antd'; import { Modal, Button, Spin, Tooltip, Typography } from "antd"
import { Resizable } from 'react-resizable';
import ResizeObserver from 'rc-resize-observer';
import { checkMenuAdmit, isSzseEnv } from '../../../../util'; import { dispatch } from '../../../../model'
import { dispatch } from '../../../../model'; import Table from '../../../../util/Component/Table'
import { checkMenuAdmit, isSzseEnv, showMessage } from '../../../../util'
import './SuggestTable.less'; const topN = 20
const ResizeableHeaderCell = props => { const FC = (props) => {
const { onResize, width, onClick, ...restProps } = props; const { visible, name, cnName, onCancel, onOk, triggerType = 'cnName' } = props
const [loading, setLoading] = React.useState(false)
const [suggests, setSuggests] = React.useState()
const [selectedRows, setSelectedRows] = React.useState()
const [havaMore, setMore] = React.useState(false)
const [offset, setOffset] = React.useState(1)
if (!width) { React.useEffect(() => {
return <th {...restProps} />; getSuggests()
} }, [visible])
return (
<Resizable
width={width}
height={0}
handle={
<span
className="react-resizable-handle"
onClick={(e) => {
e.stopPropagation();
}}
/>
}
onResize={onResize}
draggableOpts={{ enableUserSelectHack: false }}
>
<th
onClick={onClick}
{...restProps}
/>
</Resizable>
);
};
const SourceComponent = (props) => { const onSourceClick = (id, name) => {
const { data, onClick, name } = props; const timestamp = new Date().getTime();
const tempArray = id.split('=');
if (tempArray.length>=3) {
const moreSourceComponent = <div style={{ maxWidth: 400 }}> dispatch({
{ type: 'datamodel.getParent',
(data||[]).map((source, index) => { payload: {
return ( id
<div },
className='pointer' callback: data => {
key={index} window.open(`/center-home/metadetail?mid=${encodeURIComponent(data._id)}&action=metadetail&type=detail&manager=false&activekey=1&name=${encodeURIComponent(name||'')}`);
style={{ }
textDecoration: 'underline',
}}
onClick={(e) => {
e.stopPropagation();
onClick && onClick(source.sourceId, name);
}}
>
{source.sourcePath||''}
</div>
);
}) })
} else {
if (checkMenuAdmit('datastandard')) {
window.open(`/center-home/menu/datastandard?id=${id}&timestamp=${timestamp}`);
}
} }
</div>;
return (
<Tooltip
title={moreSourceComponent}
overlayClassName='tooltip-common'
>
<a
href='#'
onClick={(e) => {
e.stopPropagation();
onClick && onClick(data[0].sourceId, name);
}}
>
{
(data||[]).length>0 && <span>{data[0].sourcePath||''}</span>
} }
</a>
</Tooltip>
);
}
const SuggestTable = (props) => {
const { suggests, onSelect } = props;
const [ tableWidth, setTableWidth ] = useState(0);
const cols = [ const cols = React.useMemo(() => {
return [
{ {
title: '中文名称', title: '中文名称',
dataIndex: 'cnName', dataIndex: 'cnName',
...@@ -158,121 +111,166 @@ const SuggestTable = (props) => { ...@@ -158,121 +111,166 @@ const SuggestTable = (props) => {
ellipsis: true, ellipsis: true,
render: (_, record) => { render: (_, record) => {
return ( return (
<SourceComponent data={record.recommendedStats?.sourceInfos||[]} name={record.name||''} onClick={sourceOnClick} /> <SourceComponent data={record.recommendedStats?.sourceInfos||[]} name={record.name||''} onClick={onSourceClick} />
); );
} }
}, },
]; ]
}, [onSourceClick])
const [ columns, setColumns ] = useState(cols);
const sourceOnClick = (id, name) => {
const timestamp = new Date().getTime();
const tempArray = id.split('=');
if (tempArray.length>=3) {
const getSuggests = () => {
setLoading(true)
dispatch({ dispatch({
type: 'datamodel.getParent', type: 'datamodel.suggest',
payload: { payload: {
id params: {
name,
cnName,
offset,
topN,
}
}, },
callback: data => { callback: data => {
window.open(`/center-home/metadetail?mid=${encodeURIComponent(data._id)}&action=metadetail&type=detail&manager=false&activekey=1&name=${encodeURIComponent(name||'')}`); setLoading(false)
}
if (triggerType === 'cnName') {
setSuggests(prevSuggests => {
const newSuggests = [...prevSuggests??[], ...data?.[0]?.suggestions??[]]
setOffset((data?.[0]?.suggestions??[]).length + offset)
setMore((data?.[0]?.suggestions??[]).length === topN)
return newSuggests
})
} else if (triggerType === 'name') {
setSuggests(prevSuggests => {
const newSuggests = [...prevSuggests??[], ...data?.[1]?.suggestions??[]]
setOffset((data?.[1]?.suggestions??[]).length + offset)
setMore((data?.[1]?.suggestions??[]).length === topN)
return newSuggests
}) })
} else {
if (checkMenuAdmit('datastandard')) {
window.open(`/center-home/menu/datastandard?id=${id}&timestamp=${timestamp}`);
} }
},
error: () => {
setLoading(false)
} }
})
} }
const onTableSelect = (record, selected, selectedRows, nativeEvent) => { const close = () => {
onSelect && onSelect(record); setLoading(false)
setSuggests()
setOffset(1)
setMore(false)
onCancel?.()
} }
const handleResize = index => (e, { size }) => { const save = () => {
const nextColumns = [...columns]; if ((selectedRows??[]).length === 0) {
nextColumns[index] = { showMessage('warn', '请先选择推荐项')
...nextColumns[index], return
width: size.width, }
};
setColumns(nextColumns);
};
const mergedColumns = () => { onOk?.(selectedRows?.[0])
return ( close()
columns.map((column, index) => {
return {
...column,
onHeaderCell: column => ({
width: column.width,
onResize: handleResize(index),
}),
};
})
);
} }
const rowSelection = { const footer = React.useMemo(() => {
type: 'radio', return [
onSelect: onTableSelect, <Button key='cancel'
}; onClick={() => close()}
>取消</Button>,
<Button key='save' type='primary'
onClick={() => save()}
>确定</Button>
]
}, [close, save])
return ( return (
<div className='suggest-table'> <Modal
<ResizeObserver visible={visible}
onResize={({ width }) => { footer={footer}
width='80%'
if (tableWidth !== width) { bodyStyle={{ padding: '15px', overflowX: 'auto', maxHeight: '80vh' }}
setTableWidth(width); title='匹配推荐'
let newColumns = [...cols]; centered destroyOnClose
onCancel={() => { close() }}
newColumns.forEach((column, index) => {
if (!column.width) {
const rowWidth = (cols.reduce((preVal, col) => (col.width?col.width:0) + preVal, 0)) + 50;
if (width > rowWidth) {
column.width = (width-rowWidth)>200?(width-rowWidth):200;
} else {
column.width = 200;
}
}
});
setColumns(newColumns);
}
}}
> >
<Spin spinning={loading}>
<Table <Table
rowSelection={rowSelection} extraColWidth='32px'
size='small'
rowKey='iid'
dataSource={suggests||[]} dataSource={suggests||[]}
columns={cols}
pagination={false} pagination={false}
loading={false}
rowKey='iid'
rowClassName={(record, index) => { rowClassName={(record, index) => {
return 'pointer'; return 'pointer';
}} }}
components={{ onRowClick={(event, record) => {
header: { setSelectedRows([record])
cell: ResizeableHeaderCell,
}
}} }}
columns={mergedColumns()} rowSelection={{
onRow={(record, index) => { type: 'radio',
return { selectedRowKeys: (selectedRows??[]).map(item => item.iid),
onClick: (e) => { onChange: (selectedRowKeys, selectedRows) => {
onSelect && onSelect(record); setSelectedRows(selectedRows)
} },
}
}} }}
/> />
</ResizeObserver> <div className='flex pt-3' style={{ justifyContent: 'center' }}>
<Tooltip title={!havaMore?'没有更多推荐字段':''}>
<Button onClick={getSuggests} disabled={!havaMore} >加载更多</Button>
</Tooltip>
</div> </div>
); </Spin>
</Modal>
)
} }
export default SuggestTable; export default FC
\ No newline at end of file
const SourceComponent = (props) => {
const { data, onClick, name } = props;
const moreSourceComponent = <div style={{ maxWidth: 400 }}>
{
(data||[]).map((source, index) => {
return (
<div
className='pointer'
key={index}
style={{
textDecoration: 'underline',
}}
onClick={(e) => {
e.stopPropagation();
onClick && onClick(source.sourceId, name);
}}
>
{source.sourcePath||''}
</div>
);
})
}
</div>;
return (
<Tooltip
title={moreSourceComponent}
overlayClassName='tooltip-common'
>
<a
href='#'
onClick={(e) => {
e.stopPropagation();
onClick && onClick(data[0].sourceId, name);
}}
>
{
(data||[]).length>0 && <span>{data[0].sourcePath||''}</span>
}
</a>
</Tooltip>
);
}
\ No newline at end of file
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