Commit e02e68dc by zhaochengxiang

打标签

parent 8df595e3
...@@ -47,6 +47,16 @@ module.exports = { ...@@ -47,6 +47,16 @@ module.exports = {
headers: { headers: {
'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Origin': '*',
}, },
proxy: {
'/api': {
target: 'http://192.168.0.38:8089',
changeOrigin: true,
},
'/data-quality-szse': {
target: 'http://192.168.0.38:8089',
changeOrigin: true,
},
},
// historyApiFallback: true, // historyApiFallback: true,
// hot: false, // hot: false,
// watchContentBase: false, // watchContentBase: false,
......
...@@ -77,6 +77,5 @@ ...@@ -77,6 +77,5 @@
"last 1 safari version" "last 1 safari version"
] ]
}, },
"proxy": "http://192.168.0.38:8089",
"homepage": "http://myhost/data-govern" "homepage": "http://myhost/data-govern"
} }
import * as service from '../service/tag'; import * as service from '../service/tag';
import { call } from 'redux-saga/effects'; 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) { export function* getResourceTagIn(payload) {
return yield call(service.getResourceTagIn, payload); return yield call(service.getResourceTagIn, payload);
} }
export function* batchAddTagResourceByTagList(payload) { export function* deleteTags(payload) {
return yield call(service.batchAddTagResourceByTagList, payload); return yield call(service.deleteTags, 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* getTagIdListByNameList(payload) { export function* addTags(payload) {
return yield call(service.getTagIdListByNameList, payload); return yield call(service.addTags, payload);
} }
\ No newline at end of file
import { GetJSON, PostJSON, Delete } from "../util/axios"; import { GetJSON, PostJSON, Delete, callFetchRaw } from "../util/axios";
export function batchAddTagResource(payload) {
return PostJSON("/tagger/tagResource/batchAddTagResource", payload);
}
export function deleteTagResource(payload) {
return Delete("/tagger/tagResource/deleteTagResource", payload);
}
export function getResourceTagIn(payload) { export function getResourceTagIn(payload) {
return PostJSON("/tagger/tagResource/getResourceTagIn", payload); return callFetchRaw('post', '/taggertest/manager/tag/resource/getResourceTagIn', payload)
}
export function batchAddTagResourceByTagList(payload) {
return PostJSON("/tagger/tagResource/batchAddTagResourceByTagList", payload)
}
export function getTagByKeywordAndCreator(payload) {
return GetJSON("/tagger/tag/getTagByKeywordAndCreator", payload);
}
export function getSupportTagForm(payload) {
return GetJSON("/tagger/tag/getSupportTagForm", payload);
} }
export function saveTag(payload) { export function deleteTags(payload) {
return PostJSON("/tagger/tag/saveTag", payload); return Delete("/taggertest/manager/tag/resource/deleteTagInResource", payload);
} }
export function getTagIdListByNameList(payload) { export function addTags(payload) {
return PostJSON("/tagger/tag/getTagIdListByNameList", payload); return PostJSON("/taggertest/manager/tag/resource/batchAddTagResourceByTagList", payload);
} }
...@@ -6,6 +6,7 @@ import ResizeObserver from 'rc-resize-observer'; ...@@ -6,6 +6,7 @@ import ResizeObserver from 'rc-resize-observer';
import { useContextMenu, Menu as RcMenu, Item as RcItem } from "react-contexify"; import { useContextMenu, Menu as RcMenu, Item as RcItem } from "react-contexify";
import LocalStorage from 'local-storage'; import LocalStorage from 'local-storage';
import { Subject } from "rxjs"; import { Subject } from "rxjs";
import produce from 'immer'
import FilterElementModal from './FilterElementModal'; import FilterElementModal from './FilterElementModal';
import AssetMount from '../../AssetRecycle/Component/AssetMount'; import AssetMount from '../../AssetRecycle/Component/AssetMount';
...@@ -23,6 +24,7 @@ import StartFlowModal from "./StartFlow"; ...@@ -23,6 +24,7 @@ import StartFlowModal from "./StartFlow";
import Table from '../../ResizeableTable'; import Table from '../../ResizeableTable';
import WorksheetModal from "./WorksheetModal"; import WorksheetModal from "./WorksheetModal";
import WorkbookDrawer from "./WorkbookDrawer"; import WorkbookDrawer from "./WorkbookDrawer";
import TagCell, { TagSelectPopup } from './tag-help'
import "./AssetTable.less"; import "./AssetTable.less";
import 'react-contexify/dist/ReactContexify.css'; import 'react-contexify/dist/ReactContexify.css';
...@@ -146,6 +148,11 @@ const AssetTable = (props) => { ...@@ -146,6 +148,11 @@ const AssetTable = (props) => {
const [currentReport, setReport] = useState() const [currentReport, setReport] = useState()
const [worksheetModalVisible, setWorksheetModalVisible] = useState(false) const [worksheetModalVisible, setWorksheetModalVisible] = useState(false)
const [workbookDrawerVisible, setWorkbookDrawerVisible] = useState(false) const [workbookDrawerVisible, setWorkbookDrawerVisible] = useState(false)
const [tagSelectPopupParams, setTagSelectPopupParams] = React.useState({
visible: false,
items: undefined
})
const [resoureTagMap, setResourceTagMap] = React.useState()
const [ modal, contextHolder ] = Modal.useModal(); const [ modal, contextHolder ] = Modal.useModal();
const anchorId = getQueryParam(AnchorId, props?.location?.search); const anchorId = getQueryParam(AnchorId, props?.location?.search);
...@@ -170,6 +177,26 @@ const AssetTable = (props) => { ...@@ -170,6 +177,26 @@ const AssetTable = (props) => {
filter: false, filter: false,
} }
const tagCol = {
title: '标签',
dataIndex: 'tag',
width: 360,
className: 'table-tag-cell',
render: (_, record) => <TagCell
id={record.id}
did={record.dirId}
type='dataAsset'
tags={resoureTagMap?.[record.id]}
onChange={(val) => {
setResourceTagMap((prevResourceTagMap) => {
return produce(prevResourceTagMap||{}, (draft) => {
draft[record.id] = val
})
})
}}
/>
}
const actionCol = { const actionCol = {
title: '操作', title: '操作',
dataIndex: 'action', dataIndex: 'action',
...@@ -946,6 +973,38 @@ const AssetTable = (props) => { ...@@ -946,6 +973,38 @@ const AssetTable = (props) => {
} }
} }
const onBatchAddTagClick = () => {
setTagSelectPopupParams({
visible: true,
items: (assets??[]).filter(item => (checkedKeys??[]).indexOf(item.id)!==-1).map(item => ({
resourceId: item.id,
catalogId: item.dirId,
type: 'dataAsset',
}))
})
}
const getResourceTag = () => {
const ids = (assets??[]).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 onReportAnalyseClick = (val) => { const onReportAnalyseClick = (val) => {
if ((checkedKeys||[]).length === 0) { if ((checkedKeys||[]).length === 0) {
showMessage('warn', '请先选择一个资产'); showMessage('warn', '请先选择一个资产');
...@@ -1045,6 +1104,11 @@ const AssetTable = (props) => { ...@@ -1045,6 +1104,11 @@ const AssetTable = (props) => {
<Button onClick={offlineAssets} disabled={(checkedKeys||[]).length===0} >停用</Button> <Button onClick={offlineAssets} disabled={(checkedKeys||[]).length===0} >停用</Button>
</Tooltip> </Tooltip>
} }
{
reference===AssetManageReference && <Tooltip title={(checkedKeys||[]).length===0?'请先选择资产目录':''}>
<Button onClick={onBatchAddTagClick} disabled={(checkedKeys||[]).length===0} >添加标签</Button>
</Tooltip>
}
<Button onClick={onFilterElementClick}>可见列设置</Button> <Button onClick={onFilterElementClick}>可见列设置</Button>
{ {
reference===AssetManageReference && template?.structured && (reportTypes??[]).length > 0 && <Dropdown overlay={moreMenu} placement="bottomCenter"> reference===AssetManageReference && template?.structured && (reportTypes??[]).length > 0 && <Dropdown overlay={moreMenu} placement="bottomCenter">
...@@ -1133,7 +1197,7 @@ const AssetTable = (props) => { ...@@ -1133,7 +1197,7 @@ const AssetTable = (props) => {
return classNames.join(' '); return classNames.join(' ');
}} }}
loading={loading} loading={loading}
columns={[indexCol, ...columns, actionCol]} columns={reference===AssetManageReference?[indexCol, ...columns, tagCol, actionCol]:[indexCol, ...columns, actionCol]}
rowKey='id' rowKey='id'
dataSource={realAssets} dataSource={realAssets}
pagination={false} pagination={false}
...@@ -1238,6 +1302,19 @@ const AssetTable = (props) => { ...@@ -1238,6 +1302,19 @@ const AssetTable = (props) => {
setWorkbookDrawerVisible(false); setWorkbookDrawerVisible(false);
}} }}
/> />
<TagSelectPopup
{...tagSelectPopupParams}
type='dataAsset'
onCancel={() => {
setTagSelectPopupParams({
visible: false,
items: undefined
})
}}
onChange={() => {
getResourceTag()
}}
/>
<RcMenu id={MENU_ID}> <RcMenu id={MENU_ID}>
{/* { {/* {
......
import React, { useEffect } from 'react'
import { Tooltip, Tag, Typography, Space, Button, Modal, Spin, Select, } from 'antd'
import {
EllipsisOutlined,
PlusOutlined,
} from '@ant-design/icons'
import produce from 'immer'
import DataQuality, { DataQualityFeighTagSelect } from '../../../QianKun/DataQualitySzse'
import { showMessage } from '../../../../util'
import { dispatch } from '../../../../model'
import { AppContext } from '../../../../App'
const FC = ({ type, id, did, tags, onChange }) => {
const [data, setData] = React.useState([])
const [tagSelectPopupParams, setTagSelectPopupParams] = React.useState({
visible: false,
type: undefined,
items: undefined,
})
useEffect(() => {
setData(tags)
}, [tags])
const storageChange = (e) => {
if (e.key === 'tagChangeEvent' && e.id === id) {
getTags()
}
}
React.useEffect(() => {
window?.addEventListener("storage", storageChange)
return () => {
window?.removeEventListener("storage", storageChange)
}
}, [type, id])
const getTags = () => {
dispatch({
type: 'tag.getResourceTagIn',
payload: {
params: {
resourceIds: id,
includeAll: true,
includePrivate: true
}
},
callback: data => {
setData(data?.data?.[`${id}`]);
onChange?.(data?.data?.[`${id}`]);
}
});
}
//资产或者资源详情里的标签同步
const syncTag = () => {
if (type === 'dataAsset') {
let event = new Event('storage')
event.key = 'assetListTagChangeEvent'
event.id = id
window?.dispatchEvent(event)
}
}
return (
<React.Fragment>
<TagCell
value={data}
onChange={(val) => {
getTags()
syncTag()
}}
onAdd={(val) => {
setTagSelectPopupParams({
visible: true,
type,
items: [{ resourceId: id, catalogId: did, type, }]
})
}}
/>
<TagSelectPopup
onCancel={() => {
setTagSelectPopupParams({
visible: false,
type: undefined,
items: undefined
})
}}
onChange={(val) => {
getTags()
syncTag()
}}
{...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={item.id}
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={item.id}
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 TagSelect({ options, onChange }) {
const [popupParams, setPopupParams] = React.useState({
visible: false,
value: undefined
})
return (
<React.Fragment>
<Select
mode='multiple'
placeholder='请选择标签'
tagRender={(props) => {
const { value } = props
const index = (options??[]).findIndex(item => item.value === value)
if (index !== -1) {
return <TagRender key={value} data={options[index]} {...props} />
}
return <React.Fragment></React.Fragment>
}}
onDropdownVisibleChange={(open) => {
if (open) {
setPopupParams({
visible: true,
value: options
})
}
}}
open={false}
style={{ width: 200 }}
value={(options??[]).map(item => item.value)}
options={options}
allowClear
onChange={(newValue) => {
const newOptions = (options??[]).filter(item => newValue.indexOf(item.value) !== -1)
onChange?.(newOptions)
}}
maxTagCount='responsive'
/>
<TagSelectPopup
value={options}
onCancel={(value) => {
setPopupParams({
visible: false,
value: undefined
})
}}
onChange={(value) => {
const newOptions = produce(value??[], (draft) => {
draft?.forEach(item => {
item.value = item.id
item.label = item.name
})
})
onChange?.(newOptions)
}}
{...popupParams}
/>
</React.Fragment>
)
}
export function TagSelectPopup({ visible, type, value, 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(),
creator: app?.user?.userName,
overrideAll: (items??[]).length === 1 ? true : false,
},
data: items
},
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}
data={{
type,
items,
value,
}}
onChange={(val) => {
setSelectedRows(val)
}}
/> : null
}
</Spin>
</Modal>
)
}
\ No newline at end of file
import React from 'react'
import { loadMicroApp } from 'qiankun'
import { AppContext } from '../../../App'
export const DataQualityFeighTagSelect = 'data-quality-feign-tag-select'
export const DataQualityFeignTagList = 'data-quality-feign-tag-list'
const FC = ({ type, data, onChange }) => {
const containerRef = React.useRef()
const microApp = React.useRef()
const app = React.useContext(AppContext)
React.useEffect(() => {
if (containerRef.current) {
microApp.current = loadMicroApp(
{
name: 'data-quality-szse',
entry: '/data-quality-szse/',
container: containerRef.current,
props: {
...app||{},
type,
data,
onChange
},
},
);
}
return () => {
microApp.current?.unmount()
}
}, [])
React.useEffect(() => {
function updateMicroApp() {
if (microApp.current?.update) {
setTimeout(() => {
microApp.current?.update?.({ type, data })
}, 100)
} else {
setTimeout(() => {
updateMicroApp()
}, 100)
}
}
updateMicroApp()
}, [type, data])
return (
<div ref={containerRef} style={{ width: '100%', height: '100%' }} />
);
}
export default FC
\ 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