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,94 +859,24 @@ export const ImportActionTable = (props) => { ...@@ -858,94 +859,24 @@ 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 (changedValues.hasOwnProperty('cnName')) {
if (autoTranslateRef.current) {
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({ dispatchLatest({
type: 'datamodel.suggest', type: 'datamodel.translatePhase',
payload: { payload: {
params: { params: {
name: allValues.name||'', phaseInChinese: changedValues.cnName,
cnName: allValues.cnName||'',
topN: (offset+perSuggestCount-1),
offset
} }
}, },
callback: data => { callback: data => {
setLoadingSuggest(false); if ((data?.translated||'') !== '') {
form.setFieldsValue({ name: data?.translated||'' });
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);
} }
}) })
} }
} else if (changedValues.hasOwnProperty('name')) {
if (changedValues.hasOwnProperty('cnName')) { autoTranslateRef.current(changedValues.name==='');
if (autoTranslateRef.current) {
dispatchLatest({
type: 'datamodel.translatePhase',
payload: {
params: {
phaseInChinese: changedValues.cnName,
}
},
callback: data => {
if ((data?.translated||'') !== '') {
form.setFieldsValue({ name: data?.translated||'' });
}
getSuggest();
}
})
} else {
getSuggest();
}
} else if (changedValues.hasOwnProperty('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])
const onSourceClick = (id, name) => {
const timestamp = new Date().getTime();
const tempArray = id.split('=');
if (tempArray.length>=3) {
dispatch({
type: 'datamodel.getParent',
payload: {
id
},
callback: data => {
window.open(`/center-home/metadetail?mid=${encodeURIComponent(data._id)}&action=metadetail&type=detail&manager=false&activekey=1&name=${encodeURIComponent(name||'')}`);
}
})
} else {
if (checkMenuAdmit('datastandard')) {
window.open(`/center-home/menu/datastandard?id=${id}&timestamp=${timestamp}`);
}
}
}
const cols = React.useMemo(() => {
return [
{
title: '中文名称',
dataIndex: 'cnName',
width: isSzseEnv?360:160,
ellipsis: true,
render: (text, _, __) => {
return (
<Tooltip title={text||''}>
<span>{text||''}</span>
</Tooltip>
)
}
},
{
title: '英文名称',
dataIndex: 'name',
width: isSzseEnv?360:160,
ellipsis: true,
render: (text, _, __) => {
return (
<Tooltip title={text||''}>
<span>{text||''}</span>
</Tooltip>
)
}
},
{
title: '业务含义',
dataIndex: 'remark',
width: isSzseEnv?360:160,
ellipsis: true,
render: (text, _, __) => {
return (
<Tooltip title={text||''}>
<Typography.Text ellipsis={true}>{text||''}</Typography.Text>
</Tooltip>
)
}
},
{
title: '匹配度',
dataIndex: 'score',
width: 100,
render: (_, record, index) => {
return (
<React.Fragment>
<span style={{ color: '#f50' }}>{`${record.recommendedStats?.score}%`}</span>
{ index===0 && <span style={{ color: '#f50' }}> 推荐</span> }
</React.Fragment>
);
}
},
{
title: '使用次数',
dataIndex: 'referencesCount',
width: 80,
ellipsis: true,
render: (_, record) => {
return (
<span>{record.recommendedStats?.referencesCount}</span>
);
}
},
{
title: '来源',
dataIndex: 'source',
ellipsis: true,
render: (_, record) => {
return (
<SourceComponent data={record.recommendedStats?.sourceInfos||[]} name={record.name||''} onClick={onSourceClick} />
);
}
},
]
}, [onSourceClick])
const getSuggests = () => {
setLoading(true)
dispatch({
type: 'datamodel.suggest',
payload: {
params: {
name,
cnName,
offset,
topN,
}
},
callback: data => {
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
})
}
},
error: () => {
setLoading(false)
}
})
}
const close = () => {
setLoading(false)
setSuggests()
setOffset(1)
setMore(false)
onCancel?.()
}
const save = () => {
if ((selectedRows??[]).length === 0) {
showMessage('warn', '请先选择推荐项')
return
}
onOk?.(selectedRows?.[0])
close()
} }
const footer = React.useMemo(() => {
return [
<Button key='cancel'
onClick={() => close()}
>取消</Button>,
<Button key='save' type='primary'
onClick={() => save()}
>确定</Button>
]
}, [close, save])
return ( return (
<Resizable <Modal
width={width} visible={visible}
height={0} footer={footer}
handle={ width='80%'
<span bodyStyle={{ padding: '15px', overflowX: 'auto', maxHeight: '80vh' }}
className="react-resizable-handle" title='匹配推荐'
onClick={(e) => { centered destroyOnClose
e.stopPropagation(); onCancel={() => { close() }}
>
<Spin spinning={loading}>
<Table
extraColWidth='32px'
size='small'
rowKey='iid'
dataSource={suggests||[]}
columns={cols}
pagination={false}
rowClassName={(record, index) => {
return 'pointer';
}}
onRowClick={(event, record) => {
setSelectedRows([record])
}}
rowSelection={{
type: 'radio',
selectedRowKeys: (selectedRows??[]).map(item => item.iid),
onChange: (selectedRowKeys, selectedRows) => {
setSelectedRows(selectedRows)
},
}} }}
/> />
} <div className='flex pt-3' style={{ justifyContent: 'center' }}>
onResize={onResize} <Tooltip title={!havaMore?'没有更多推荐字段':''}>
draggableOpts={{ enableUserSelectHack: false }} <Button onClick={getSuggests} disabled={!havaMore} >加载更多</Button>
> </Tooltip>
<th </div>
onClick={onClick} </Spin>
{...restProps} </Modal>
/> )
</Resizable> }
);
}; export default FC
const SourceComponent = (props) => { const SourceComponent = (props) => {
const { data, onClick, name } = props; const { data, onClick, name } = props;
...@@ -81,198 +273,4 @@ const SourceComponent = (props) => { ...@@ -81,198 +273,4 @@ const SourceComponent = (props) => {
</a> </a>
</Tooltip> </Tooltip>
); );
} }
\ No newline at end of file
const SuggestTable = (props) => {
const { suggests, onSelect } = props;
const [ tableWidth, setTableWidth ] = useState(0);
const cols = [
{
title: '中文名称',
dataIndex: 'cnName',
width: isSzseEnv?360:160,
ellipsis: true,
render: (text, _, __) => {
return (
<Tooltip title={text||''}>
<span>{text||''}</span>
</Tooltip>
)
}
},
{
title: '英文名称',
dataIndex: 'name',
width: isSzseEnv?360:160,
ellipsis: true,
render: (text, _, __) => {
return (
<Tooltip title={text||''}>
<span>{text||''}</span>
</Tooltip>
)
}
},
{
title: '业务含义',
dataIndex: 'remark',
width: isSzseEnv?360:160,
ellipsis: true,
render: (text, _, __) => {
return (
<Tooltip title={text||''}>
<Typography.Text ellipsis={true}>{text||''}</Typography.Text>
</Tooltip>
)
}
},
{
title: '匹配度',
dataIndex: 'score',
width: 100,
render: (_, record, index) => {
return (
<React.Fragment>
<span style={{ color: '#f50' }}>{`${record.recommendedStats?.score}%`}</span>
{ index===0 && <span style={{ color: '#f50' }}> 推荐</span> }
</React.Fragment>
);
}
},
{
title: '使用次数',
dataIndex: 'referencesCount',
width: 80,
ellipsis: true,
render: (_, record) => {
return (
<span>{record.recommendedStats?.referencesCount}</span>
);
}
},
{
title: '来源',
dataIndex: 'source',
ellipsis: true,
render: (_, record) => {
return (
<SourceComponent data={record.recommendedStats?.sourceInfos||[]} name={record.name||''} onClick={sourceOnClick} />
);
}
},
];
const [ columns, setColumns ] = useState(cols);
const sourceOnClick = (id, name) => {
const timestamp = new Date().getTime();
const tempArray = id.split('=');
if (tempArray.length>=3) {
dispatch({
type: 'datamodel.getParent',
payload: {
id
},
callback: data => {
window.open(`/center-home/metadetail?mid=${encodeURIComponent(data._id)}&action=metadetail&type=detail&manager=false&activekey=1&name=${encodeURIComponent(name||'')}`);
}
})
} else {
if (checkMenuAdmit('datastandard')) {
window.open(`/center-home/menu/datastandard?id=${id}&timestamp=${timestamp}`);
}
}
}
const onTableSelect = (record, selected, selectedRows, nativeEvent) => {
onSelect && onSelect(record);
}
const handleResize = index => (e, { size }) => {
const nextColumns = [...columns];
nextColumns[index] = {
...nextColumns[index],
width: size.width,
};
setColumns(nextColumns);
};
const mergedColumns = () => {
return (
columns.map((column, index) => {
return {
...column,
onHeaderCell: column => ({
width: column.width,
onResize: handleResize(index),
}),
};
})
);
}
const rowSelection = {
type: 'radio',
onSelect: onTableSelect,
};
return (
<div className='suggest-table'>
<ResizeObserver
onResize={({ width }) => {
if (tableWidth !== width) {
setTableWidth(width);
let newColumns = [...cols];
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);
}
}}
>
<Table
rowSelection={rowSelection}
dataSource={suggests||[]}
pagination={false}
loading={false}
rowKey='iid'
rowClassName={(record, index) => {
return 'pointer';
}}
components={{
header: {
cell: ResizeableHeaderCell,
}
}}
columns={mergedColumns()}
onRow={(record, index) => {
return {
onClick: (e) => {
onSelect && onSelect(record);
}
}
}}
/>
</ResizeObserver>
</div>
);
}
export default SuggestTable;
\ 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