Commit 381c4ada authored by 前端-许佳敏's avatar 前端-许佳敏

修改树形菜单

parent 09168d12
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
"@umijs/test": "^3.2.0", "@umijs/test": "^3.2.0",
"bizcharts": "^4.0.7", "bizcharts": "^4.0.7",
"copy-to-clipboard": "^3.3.1", "copy-to-clipboard": "^3.3.1",
"god": "0.1.20", "god": "0.1.21",
"lint-staged": "^10.0.7", "lint-staged": "^10.0.7",
"mobx": "^5.15.4", "mobx": "^5.15.4",
"mobx-react": "^6.2.2", "mobx-react": "^6.2.2",
......
import React, { useState, useEffect } from 'react'
import { Select } from 'antd';
import { useDebounceFn } from '@umijs/hooks';
import { ISchemaFieldComponentProps, FormPath, useFormEffects, createFormActions, createAsyncFormActions } from '@formily/antd'
// 自定义搜索型下拉框
const SearchSelect = (props: ISchemaFieldComponentProps) => {
const { schema, form, path } = props
// 可选参数 fetchSearch, select为search
const { fetchSearch, fetchParams = 'name', fetchFormat, ...resetProps } = schema.getExtendsComponentProps()
const [dataSource, setDataSource] = useState<any[]>([])
const [loading, setLoading] = useState(false)
console.log(dataSource)
const dispatchSearch = (searchValue: string) => {
if (fetchSearch) {
fetchSearch({
[fetchParams]: searchValue
}).then(({data = []}) => {
const formatData = fetchFormat ? fetchFormat(data) : data.map(v => ({
label: v.name,
value: v.id
}))
setDataSource(formatData)
}).finally(() => {
setLoading(false)
})
}
// 触发自定义事件
form.notify('onSearchSelect', {
...props,
searchValue
})
}
const { run } = useDebounceFn(dispatchSearch, 1000)
return (
<Select
showSearch
onChange={e => console.log(e)}
onSearch={value => {
console.log(value)
setLoading(true)
run(value)
}
}
loading={loading}
options={dataSource}
{...resetProps}
>
</Select>
)
}
SearchSelect.defaultProps = {}
SearchSelect.isFieldComponent = true;
export default SearchSelect
\ No newline at end of file
...@@ -20,6 +20,7 @@ import Children from './components/Children'; ...@@ -20,6 +20,7 @@ import Children from './components/Children';
import CircleBox from './components/CircleBox'; import CircleBox from './components/CircleBox';
import Phone from './components/Phone'; import Phone from './components/Phone';
import CustomRadio from './components/CustomRadio'; import CustomRadio from './components/CustomRadio';
import SearchSelect from './components/SearchSelect';
import './index.less' import './index.less'
import { Checkbox } from '@formily/antd-components'; import { Checkbox } from '@formily/antd-components';
...@@ -113,6 +114,7 @@ const NiceForm: React.FC<NiceFormProps> = props => { ...@@ -113,6 +114,7 @@ const NiceForm: React.FC<NiceFormProps> = props => {
SchemaFormButtonGroup, SchemaFormButtonGroup,
FlexBox, FlexBox,
Phone, Phone,
SearchSelect,
}; };
const defineComponents = Object.assign(customComponents, components); const defineComponents = Object.assign(customComponents, components);
......
import React, { import React, { useState, ReactText, useImperativeHandle, useEffect, useRef } from 'react'
useState, import { Tree, Space, Tooltip, Button } from 'antd'
ReactText, import { findItemAndDelete, findTreeKeys, treeReduction, getParentTreeTitles } from '@/utils'
useImperativeHandle, import './index.less'
useEffect, import deepClone from 'clone'
useRef, import { TreeProps } from 'antd/lib/tree'
} from 'react'; import { PlusOutlined, DeleteOutlined, PlusCircleOutlined } from '@ant-design/icons'
import { Tree, Space, Tooltip, Button } from 'antd'; import cx from 'classnames'
import { findItemAndDelete, findTreeKeys } from '@/utils'; import { EventDataNode } from 'rc-tree/lib/interface'
import './index.less'; import { useSelections } from '@umijs/hooks'
import deepClone from 'clone';
import { TreeProps } from 'antd/lib/tree';
import {
PlusOutlined,
DeleteOutlined,
PlusCircleOutlined,
} from '@ant-design/icons';
import cx from 'classnames';
import { EventDataNode } from 'rc-tree/lib/interface';
import { useSelections } from '@umijs/hooks';
export interface TabTreeActions { export interface TabTreeActions {
selected: ReactText[]; selected: ReactText[];
...@@ -27,6 +17,8 @@ export interface TabTreeActions { ...@@ -27,6 +17,8 @@ export interface TabTreeActions {
setExpandedKeys: (keys: ReactText[]) => void; setExpandedKeys: (keys: ReactText[]) => void;
setSelectKey: (key: ReactText) => void; setSelectKey: (key: ReactText) => void;
setSelectKeys: (keys: ReactText[]) => void; setSelectKeys: (keys: ReactText[]) => void;
getParentPath: (id: ReactText) => string
getParent: (id) => any
} }
export interface toolsRenderProps { export interface toolsRenderProps {
...@@ -77,11 +69,11 @@ export const createTreeActions = () => { ...@@ -77,11 +69,11 @@ export const createTreeActions = () => {
getSelectKeys() { getSelectKeys() {
return []; return [];
}, },
setSelectKey() { setSelectKey() {},
console.log(222);
},
setSelectKeys() {}, setSelectKeys() {},
setExpandedKeys() {}, setExpandedKeys() {},
getParentPath(id){return ''},
getParent(id){return null}
}; };
return actions; return actions;
}; };
...@@ -194,7 +186,7 @@ function transformSingleTitle( ...@@ -194,7 +186,7 @@ function transformSingleTitle(
// 使选中样式受控 // 使选中样式受控
data[item].className = cx( data[item].className = cx(
'god-tabtree-select', 'god-tabtree-select',
nowKey === data[item].key ? 'show' : 'hide', Number(nowKey) === Number(data[item].key) ? 'show' : 'hide',
); );
if (disabled) { if (disabled) {
data[item].disableCheckbox = disabled; data[item].disableCheckbox = disabled;
...@@ -223,7 +215,7 @@ const TabTree: React.FC<TabTreeProps> = props => { ...@@ -223,7 +215,7 @@ const TabTree: React.FC<TabTreeProps> = props => {
handleSubmit, handleSubmit,
} = props; } = props;
// const selfActions = useTreeActions(actions) const selfActions = useTreeActions(actions)
// 需展开的key // 需展开的key
const [expandkeys, setExpandkeys] = useState<ReactText[]>([]); const [expandkeys, setExpandkeys] = useState<ReactText[]>([]);
...@@ -267,23 +259,38 @@ const TabTree: React.FC<TabTreeProps> = props => { ...@@ -267,23 +259,38 @@ const TabTree: React.FC<TabTreeProps> = props => {
} }
}; };
if (actions) { if (selfActions) {
actions.getExpandedKeys = () => expandkeys; selfActions.getExpandedKeys = () => expandkeys;
actions.getSelectKey = () => selectKey; selfActions.getSelectKey = () => selectKey;
actions.getSelectKeys = () => selected; selfActions.getSelectKeys = () => selected;
actions.selected = selected; selfActions.selected = selected;
actions.setSelectKeys = (keys: ReactText[]) => { selfActions.setSelectKeys = (keys: ReactText[]) => {
setSelected(keys); setSelected(keys);
}; };
actions.setExpandedKeys = (keys: ReactText[]) => { selfActions.setExpandedKeys = (keys: ReactText[]) => {
setExpandkeys(keys); setExpandkeys(keys);
}; };
actions.setSelectKey = (key: ReactText) => { selfActions.setSelectKey = (key: ReactText) => {
setSelectKey(key); setSelectKey(key);
}; };
selfActions.getParentPath = (id: ReactText) => {
return getParentTreeTitles(treeData, id)
}
selfActions.getParent = (id) => {
const reductData = treeReduction(treeData)
const targetInfo = reductData[id]
const parentInfo = reductData[targetInfo.parentId]
return parentInfo || null
}
} }
const batchSelect = (items: any[]) => { const batchSelect = (items: any) => {
items.forEach(v => select(v)); if (items.checked) {
// 更改为严格模式
items.checked.forEach(v => select(v))
} else {
items.forEach(v => select(v))
}
}; };
return ( return (
......
export interface MemberType {
id: number;
typeName: string;
}
export interface BusinessType {
id: number;
typeName: string;
}
export interface UseType {
memberType: MemberType[];
businessType: BusinessType[];
}
export interface UserRegister {
useType: UseType;
}
export interface ShopInfo {
id: number;
name: string;
type: number;
environment: number;
logoUrl: string;
describe: string;
state: number;
url: string;
}
export interface Web {
shopInfo: ShopInfo[];
}
export interface PayConfig {
paymemberConfig?: any;
}
export interface CountryList {
name: string;
key: string;
icon: string;
}
export interface Global {
siteId: number;
siteUrl: string;
logo: string;
countryList: CountryList[];
}
export interface RootObject {
userRegister: UserRegister;
web: Web;
payConfig: PayConfig;
global: Global;
}
\ No newline at end of file
import { useMap } from '@umijs/hooks' import { useMap } from '@umijs/hooks'
import { useState, useEffect } from 'react' import React, { useState, useEffect } from 'react'
import { Modal } from 'antd' import { Modal } from 'antd'
import { TabTreeActions } from '@/components/TabTree'
import { ISchemaFormActions } from '@formily/antd'
import { isObject } from '@/utils'
export enum FormState { export enum FormState {
FREE, // 空闲状态 FREE, // 空闲状态
...@@ -12,11 +15,20 @@ export interface useTreeTabOptions { ...@@ -12,11 +15,20 @@ export interface useTreeTabOptions {
selectCallback?(selectKey?, node?), selectCallback?(selectKey?, node?),
fetchMenuData?(), fetchMenuData?(),
fetchItemDetailData?(id), fetchItemDetailData?(id),
// 重置右侧详情
resetDetail?(), resetDetail?(),
// 对树形工具栏做render扩展
extendsToolsRender?: any,
// 树形的实例操作方法
treeActions?: TabTreeActions
// 右侧表单的实例操作方法
formActions?: ISchemaFormActions
// 删除菜单时调用的API
deleteMenu?: any
} }
export const useTreeTabs = (options: useTreeTabOptions = {}) => { export const useTreeTabs = (options: useTreeTabOptions = {}) => {
const { selectCallback, fetchMenuData, fetchItemDetailData, resetDetail } = options const { selectCallback, fetchMenuData, fetchItemDetailData, resetDetail, treeActions, formActions, extendsToolsRender, deleteMenu } = options
const [ treeExtraMaps, { set, get } ] = useMap<any, any>() const [ treeExtraMaps, { set, get } ] = useMap<any, any>()
const [ treeData, setTreeData ] = useState<any[]>([]) const [ treeData, setTreeData ] = useState<any[]>([])
const [ treeStatus, setTreeStatus ] = useState<FormState>(FormState.FREE) const [ treeStatus, setTreeStatus ] = useState<FormState>(FormState.FREE)
...@@ -86,6 +98,45 @@ export const useTreeTabs = (options: useTreeTabOptions = {}) => { ...@@ -86,6 +98,45 @@ export const useTreeTabs = (options: useTreeTabOptions = {}) => {
set(id, data) set(id, data)
}) })
} }
const handleDeleteMenu = (id) => {
deleteMenu({
id: isObject(id) ? nodeRecord.key : id
}).then(() => {
setTreeStatus(FormState.FREE)
setNodeRecord(undefined)
resetMenu()
})
}
// 新增整合树形操作菜单
// 树形工具栏
const toolsRender = {
addNode(node) {
const activeParentId = treeActions && treeActions.getParent(node.key || node.id)?.id
setNodeRecord({
...node,
parentId: activeParentId, // 添加同级的时候 使用上一级的id作为parentId
})
formActions && formActions.reset({ validate: false })
setTreeStatus(FormState.ADD)
},
addChildNode(node) {
setNodeRecord({
...node,
parentId: node.key || node.id
})
formActions && formActions.reset({ validate: false })
set(node.key || node.id, null)
setTreeStatus(FormState.ADD)
},
deleteNode(node) {
const id = node.key || node.id
handleDeleteMenu(id)
},
...extendsToolsRender
}
return { return {
handleSelect, handleSelect,
treeStatus, treeStatus,
...@@ -99,6 +150,8 @@ export const useTreeTabs = (options: useTreeTabOptions = {}) => { ...@@ -99,6 +150,8 @@ export const useTreeTabs = (options: useTreeTabOptions = {}) => {
treeExtraMaps, treeExtraMaps,
setTreeMaps: set, setTreeMaps: set,
getTreeMaps: get, getTreeMaps: get,
resetMenu resetMenu,
toolsRender,
handleDeleteMenu
} }
} }
\ No newline at end of file
...@@ -12,7 +12,7 @@ import { classSchema } from './schema' ...@@ -12,7 +12,7 @@ import { classSchema } from './schema'
import { PublicApi } from '@/services/api'; import { PublicApi } from '@/services/api';
import { useTreeTabs } from '@/hooks/useTreeTabs'; import { useTreeTabs } from '@/hooks/useTreeTabs';
import NiceForm from '@/components/NiceForm'; import NiceForm from '@/components/NiceForm';
import { action } from 'mobx'; import { isObject } from '@antv/util';
const { ON_FORM_INPUT_CHANGE } = LifeCycleTypes const { ON_FORM_INPUT_CHANGE } = LifeCycleTypes
...@@ -51,36 +51,15 @@ const ClassProperty: React.FC<{}> = () => { ...@@ -51,36 +51,15 @@ const ClassProperty: React.FC<{}> = () => {
getTreeMaps, getTreeMaps,
setTreeMaps, setTreeMaps,
resetMenu, resetMenu,
toolsRender,
handleDeleteMenu
} = useTreeTabs({ } = useTreeTabs({
deleteMenu: PublicApi.postProductCustomerDeleteCustomerCategory,
fetchMenuData: fetchClassTreeData, fetchMenuData: fetchClassTreeData,
fetchItemDetailData: PublicApi.getProductCustomerGetCustomerCategory fetchItemDetailData: PublicApi.getProductCustomerGetCustomerCategory
}) })
const formInitValue = nodeRecord ? getTreeMaps(nodeRecord.key) : {} const formInitValue = (nodeRecord && treeStatus === FormState.EDIT) ? getTreeMaps(nodeRecord.key) : {}
// 树形工具栏
const toolsRender = {
addNode(node) {
formActions.reset({ validate: false })
setTreeStatus(FormState.ADD)
},
addChildNode(node) {
formActions.reset({ validate: false })
setNodeRecord({
...node,
parentId: node.id, // 添加子级的时候 使用上一级的id作为parentId
})
setTreeMaps(node.id, null)
setTreeStatus(FormState.ADD)
},
deleteNode(node) {
PublicApi.postProductCustomerDeleteCustomerCategory({
id: node.id
}).then(() => {
resetMenu()
})
}
}
/* 关联平台后台品类树 */ /* 关联平台后台品类树 */
const { const {
...@@ -107,17 +86,6 @@ const ClassProperty: React.FC<{}> = () => { ...@@ -107,17 +86,6 @@ const ClassProperty: React.FC<{}> = () => {
}) })
} }
const handleDeleteMenu = () => {
if (nodeRecord) {
PublicApi.postProductCustomerDeleteCustomerCategory({
id: nodeRecord.id
}).then(() => {
setNodeRecord(undefined)
resetMenu()
})
}
}
const handleSelectOk = () => { const handleSelectOk = () => {
setPlateformVisible(false) setPlateformVisible(false)
console.log(plateformSelectNode, 'plateformSelectNode') console.log(plateformSelectNode, 'plateformSelectNode')
......
...@@ -190,6 +190,9 @@ const Repositories: React.FC<{}> = () => { ...@@ -190,6 +190,9 @@ const Repositories: React.FC<{}> = () => {
'search', 'search',
FORM_FILTER_PATH, FORM_FILTER_PATH,
); );
// 填充下拉框
// useAsyncInitSelect(['category'])
// useAsyncInitSelect(['brand'])
}} }}
schema={repositSchema} schema={repositSchema}
/> />
......
...@@ -2,6 +2,7 @@ import React from 'react' ...@@ -2,6 +2,7 @@ import React from 'react'
import { ISchema } from '@formily/antd'; import { ISchema } from '@formily/antd';
import { FORM_FILTER_PATH } from '@/formSchema/const'; import { FORM_FILTER_PATH } from '@/formSchema/const';
import { GlobalConfig } from '@/global/config'; import { GlobalConfig } from '@/global/config';
import { PublicApi } from '@/services/api';
export const repositSchema: ISchema = { export const repositSchema: ISchema = {
type: 'object', type: 'object',
...@@ -71,15 +72,24 @@ export const repositSchema: ISchema = { ...@@ -71,15 +72,24 @@ export const repositSchema: ISchema = {
}, },
category: { category: {
type: 'string', type: 'string',
"x-component": 'SearchSelect',
"x-component-props": { "x-component-props": {
placeholder: '请选择品类' fetchSearch: PublicApi.getProductSelectGetSelectCategory,
placeholder: '请选择品类',
style: {
minWidth: 120
}
}, },
enum: [] enum: []
}, },
brand: { brand: {
type: 'string', type: 'string',
"x-component": 'SearchSelect',
"x-component-props": { "x-component-props": {
placeholder: '请选择品牌' placeholder: '请选择品牌',
style: {
minWidth: 120
}
}, },
enum: [] enum: []
}, },
......
...@@ -9,7 +9,7 @@ function isArray(arr: any) { ...@@ -9,7 +9,7 @@ function isArray(arr: any) {
return Array.isArray(arr) return Array.isArray(arr)
} }
function isObject(obj: any) { export function isObject(obj: any) {
return Object.prototype.toString.call(obj) === '[object Object]' return Object.prototype.toString.call(obj) === '[object Object]'
} }
...@@ -331,6 +331,44 @@ export const getFieldType = (field) => { ...@@ -331,6 +331,44 @@ export const getFieldType = (field) => {
} }
} }
// 树形结构降为一维对象处理
export const treeReduction = (data: any[]) => {
const hashMaps = {}
const selfData: any[] = deepClone(data)
while (selfData.length > 0) {
const useItem = selfData.shift()
// 存在子集
if (useItem.children && useItem.children.length > 0) {
useItem.children = useItem.children.map(v => {
v.parentId = useItem.id
return v
})
selfData.push(...useItem.children)
}
hashMaps[useItem.id] = useItem
}
return hashMaps
}
// 获取某一节点的title路径
export const getParentTreeTitles = (dataSouce, key) => {
const hashMaps = treeReduction(dataSouce)
let targetKey = key
let targetPath = ''
while (targetKey !== '') {
if (!hashMaps[targetKey]) {
break
}
const title = hashMaps[targetKey].title
targetPath = targetPath === '' ? title : `${title}-${targetPath}`
targetKey = hashMaps[targetKey].parentId || ''
}
return targetPath
}
export const getQueryStringParams = (url?: string) => { export const getQueryStringParams = (url?: string) => {
const nowUrl = url || window.location.href const nowUrl = url || window.location.href
const firstIndex = nowUrl.indexOf('?') const firstIndex = nowUrl.indexOf('?')
......
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