Commit 56ca4d25 authored by 前端-钟卫鹏's avatar 前端-钟卫鹏

merge

parents 17fe950c 2efe44b4
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
import React from 'react';
import SchemaForm, {
IAntdSchemaFormProps, createVirtualBox, registerVirtualBox, Schema, SchemaField, FormButtonGroup, Reset, createControllerBox,
IAntdSchemaFormProps, createVirtualBox, registerVirtualBox, Schema, SchemaField, FormButtonGroup, Reset, createControllerBox, registerValidationRules,
} from '@formily/antd';
import { Input } from '@formily/antd-components';
import { Button, Space, Row, Col } from 'antd';
......@@ -21,6 +21,7 @@ import Children from './components/Children';
import CircleBox from './components/CircleBox';
import Phone from './components/Phone';
import CustomRadio from './components/CustomRadio';
import SearchSelect from './components/SearchSelect';
import './index.less'
import { Checkbox } from '@formily/antd-components';
......@@ -60,6 +61,20 @@ const SchemaSubmit = createVirtualBox('schemaSubmit', Submit)
const SchemaReset = createVirtualBox('schemaReset', Reset)
export const FlexBox = createVirtualBox('flexBox', props => <Row {...props}/>)
// 自定义校验规则
registerValidationRules({
limitByte: (value, desc, rules) => {
const { allowChineseTransform = true, maxByte } = desc;
let str = value;
let message = `不能超过${maxByte}个字符`
if (allowChineseTransform) {
str = str.replace(/[\u4E00-\u9FA5]/g, "AA");
message += `,或者${maxByte / 2}个汉字`
}
return str.length > maxByte ? message : "";
}
});
// 该组件用于schema中嵌套表单, 不过控制台会出现警告
const schemaLayout = createControllerBox("schemaLayout", (_props) => {
const { schema } = _props;
......@@ -99,7 +114,8 @@ const NiceForm: React.FC<NiceFormProps> = props => {
CircleBox,
SchemaFormButtonGroup,
FlexBox,
Phone,
Phone,
SearchSelect,
Input,
};
......
import React, {
useState,
ReactText,
useImperativeHandle,
useEffect,
useRef,
} from 'react';
import { Tree, Space, Tooltip, Button } from 'antd';
import { findItemAndDelete, findTreeKeys } from '@/utils';
import './index.less';
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';
import React, { useState, ReactText, useImperativeHandle, useEffect, useRef } from 'react'
import { Tree, Space, Tooltip, Button } from 'antd'
import { findItemAndDelete, findTreeKeys, treeReduction, getParentTreeTitles } from '@/utils'
import './index.less'
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 {
selected: ReactText[];
......@@ -27,6 +17,8 @@ export interface TabTreeActions {
setExpandedKeys: (keys: ReactText[]) => void;
setSelectKey: (key: ReactText) => void;
setSelectKeys: (keys: ReactText[]) => void;
getParentPath: (id: ReactText) => string
getParent: (id) => any
}
export interface toolsRenderProps {
......@@ -77,11 +69,11 @@ export const createTreeActions = () => {
getSelectKeys() {
return [];
},
setSelectKey() {
console.log(222);
},
setSelectKey() {},
setSelectKeys() {},
setExpandedKeys() {},
getParentPath(id){return ''},
getParent(id){return null}
};
return actions;
};
......@@ -194,7 +186,7 @@ function transformSingleTitle(
// 使选中样式受控
data[item].className = cx(
'god-tabtree-select',
nowKey === data[item].key ? 'show' : 'hide',
Number(nowKey) === Number(data[item].key) ? 'show' : 'hide',
);
if (disabled) {
data[item].disableCheckbox = disabled;
......@@ -223,7 +215,7 @@ const TabTree: React.FC<TabTreeProps> = props => {
handleSubmit,
} = props;
// const selfActions = useTreeActions(actions)
const selfActions = useTreeActions(actions)
// 需展开的key
const [expandkeys, setExpandkeys] = useState<ReactText[]>([]);
......@@ -267,23 +259,38 @@ const TabTree: React.FC<TabTreeProps> = props => {
}
};
if (actions) {
actions.getExpandedKeys = () => expandkeys;
actions.getSelectKey = () => selectKey;
actions.getSelectKeys = () => selected;
actions.selected = selected;
actions.setSelectKeys = (keys: ReactText[]) => {
if (selfActions) {
selfActions.getExpandedKeys = () => expandkeys;
selfActions.getSelectKey = () => selectKey;
selfActions.getSelectKeys = () => selected;
selfActions.selected = selected;
selfActions.setSelectKeys = (keys: ReactText[]) => {
setSelected(keys);
};
actions.setExpandedKeys = (keys: ReactText[]) => {
selfActions.setExpandedKeys = (keys: ReactText[]) => {
setExpandkeys(keys);
};
actions.setSelectKey = (key: ReactText) => {
selfActions.setSelectKey = (key: ReactText) => {
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[]) => {
items.forEach(v => select(v));
const batchSelect = (items: any) => {
if (items.checked) {
// 更改为严格模式
items.checked.forEach(v => select(v))
} else {
items.forEach(v => select(v))
}
};
return (
......
import { useMap } from '@umijs/hooks'
import { useState, useEffect } from 'react'
import React, { useState, useEffect } from 'react'
import { Modal } from 'antd'
import { TabTreeActions } from '@/components/TabTree'
import { ISchemaFormActions } from '@formily/antd'
import { isObject } from '@/utils'
export enum FormState {
FREE, // 空闲状态
......@@ -12,11 +15,20 @@ export interface useTreeTabOptions {
selectCallback?(selectKey?, node?),
fetchMenuData?(),
fetchItemDetailData?(id),
// 重置右侧详情
resetDetail?(),
// 对树形工具栏做render扩展
extendsToolsRender?: any,
// 树形的实例操作方法
treeActions?: TabTreeActions
// 右侧表单的实例操作方法
formActions?: ISchemaFormActions
// 删除菜单时调用的API
deleteMenu?: any
}
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 [ treeData, setTreeData ] = useState<any[]>([])
const [ treeStatus, setTreeStatus ] = useState<FormState>(FormState.FREE)
......@@ -86,6 +98,45 @@ export const useTreeTabs = (options: useTreeTabOptions = {}) => {
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 {
handleSelect,
treeStatus,
......@@ -99,6 +150,8 @@ export const useTreeTabs = (options: useTreeTabOptions = {}) => {
treeExtraMaps,
setTreeMaps: set,
getTreeMaps: get,
resetMenu
resetMenu,
toolsRender,
handleDeleteMenu
}
}
\ No newline at end of file
......@@ -12,7 +12,7 @@ import { classSchema } from './schema'
import { PublicApi } from '@/services/api';
import { useTreeTabs } from '@/hooks/useTreeTabs';
import NiceForm from '@/components/NiceForm';
import { action } from 'mobx';
import { isObject } from '@antv/util';
const { ON_FORM_INPUT_CHANGE } = LifeCycleTypes
......@@ -51,36 +51,15 @@ const ClassProperty: React.FC<{}> = () => {
getTreeMaps,
setTreeMaps,
resetMenu,
toolsRender,
handleDeleteMenu
} = useTreeTabs({
deleteMenu: PublicApi.postProductCustomerDeleteCustomerCategory,
fetchMenuData: fetchClassTreeData,
fetchItemDetailData: PublicApi.getProductCustomerGetCustomerCategory
})
const formInitValue = nodeRecord ? 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 formInitValue = (nodeRecord && treeStatus === FormState.EDIT) ? getTreeMaps(nodeRecord.key) : {}
/* 关联平台后台品类树 */
const {
......@@ -107,17 +86,6 @@ const ClassProperty: React.FC<{}> = () => {
})
}
const handleDeleteMenu = () => {
if (nodeRecord) {
PublicApi.postProductCustomerDeleteCustomerCategory({
id: nodeRecord.id
}).then(() => {
setNodeRecord(undefined)
resetMenu()
})
}
}
const handleSelectOk = () => {
setPlateformVisible(false)
console.log(plateformSelectNode, 'plateformSelectNode')
......
......@@ -521,7 +521,7 @@ const auditDetail: React.FC<ItemProps> = (props: any) => {
destroyOnClose
>
<NiceForm
previewPlaceholder="loading..."
previewPlaceholder="' '"
effects={($, { setFieldState }) => {
FormEffectHooks.onFieldValueChange$('agree').subscribe(state => {
setFieldState('reason', targetState => {
......
......@@ -308,7 +308,7 @@ const memberMaintain: React.FC<[]> = () => {
destroyOnClose
>
<NiceForm
previewPlaceholder="loading..."
previewPlaceholder="' '"
effects={($, { setFieldState }) => {
FormEffectHooks.onFieldValueChange$('agree').subscribe(state => {
setFieldState('reason', targetState => {
......
......@@ -289,7 +289,7 @@ const memberMaintain: React.FC<[]> = () => {
destroyOnClose
>
<NiceForm
previewPlaceholder="loading..."
previewPlaceholder="' '"
effects={($, { setFieldState }) => {
FormEffectHooks.onFieldValueChange$('agree').subscribe(state => {
setFieldState('reason', targetState => {
......
......@@ -175,15 +175,15 @@ const PositionSetting:React.FC<PositionSettingProps> = (props) => {
},
{
title: '品类',
dataIndex: ['customerCategory', 'name'],
dataIndex: 'customerCategoryName',
align: 'center',
key: 'customerCategory.name',
key: 'customerCategoryName',
},
{
title: '品牌',
dataIndex: ['brand', 'name'],
dataIndex: 'brandName',
align: 'center',
key: 'brand.name',
key: 'brandName',
},
{
title: '单位',
......@@ -221,7 +221,7 @@ const PositionSetting:React.FC<PositionSettingProps> = (props) => {
return (
<>
<NiceForm
previewPlaceholder='loading...'
previewPlaceholder=' '
editable={pageStatus !== PageStatus.PREVIEW}
initialValues={initValue}
expressionScope={{
......
......@@ -34,6 +34,7 @@ import { useStateFilterSearchLinkageEffect } from '@/formSchema/effects/useFilte
import { FORM_FILTER_PATH } from '@/formSchema/const';
import { repositSchema } from './schema';
import { PublicApi } from '@/services/api';
import { useAsyncInitSelect } from '@/formSchema/effects/useAsyncInitSelect';
const formActions = createFormActions();
......@@ -189,6 +190,9 @@ const Repositories: React.FC<{}> = () => {
'search',
FORM_FILTER_PATH,
);
// 填充下拉框
// useAsyncInitSelect(['category'])
// useAsyncInitSelect(['brand'])
}}
schema={repositSchema}
/>
......
......@@ -2,6 +2,7 @@ import React from 'react'
import { ISchema } from '@formily/antd';
import { FORM_FILTER_PATH } from '@/formSchema/const';
import { GlobalConfig } from '@/global/config';
import { PublicApi } from '@/services/api';
export const repositSchema: ISchema = {
type: 'object',
......@@ -71,15 +72,24 @@ export const repositSchema: ISchema = {
},
category: {
type: 'string',
"x-component": 'SearchSelect',
"x-component-props": {
placeholder: '请选择品类'
fetchSearch: PublicApi.getProductSelectGetSelectCategory,
placeholder: '请选择品类',
style: {
minWidth: 120
}
},
enum: []
},
brand: {
type: 'string',
"x-component": 'SearchSelect',
"x-component-props": {
placeholder: '请选择品牌'
placeholder: '请选择品牌',
style: {
minWidth: 120
}
},
enum: []
},
......@@ -129,7 +139,16 @@ export const repositMoreSchema: ISchema = {
properties: {
name: {
type: 'string',
required: true,
"x-rules": [
{
required: true,
message: '请输入仓位名称'
},
{
limitByte: true, // 自定义校验规则
maxByte: 60
}
],
title: '仓位名称',
"x-component-props": {
placeholder: '建议名称:商品名称+商城名称+渠道描述'
......@@ -145,7 +164,12 @@ export const repositMoreSchema: ISchema = {
disabled: true,
addonAfter: "{{connectProduct}}"
},
required: true
"x-rules": [
{
required: true,
message: '请选择商品'
}
],
},
category: {
type: 'string',
......@@ -165,7 +189,7 @@ export const repositMoreSchema: ISchema = {
},
warehouseId: {
type: 'string',
title: '仓库名称',
title: '对应仓库名称',
enum: []
},
itemNo: {
......@@ -177,7 +201,12 @@ export const repositMoreSchema: ISchema = {
inventory: {
type: 'number',
"x-component": "CustomSlider",
required: true,
"x-rules": [
{
required: true,
message: '请分配仓位库存'
}
],
"x-component-props": {
min: 0,
max: 200
......@@ -226,7 +255,12 @@ export const repositMoreSchema: ISchema = {
dataSource: GlobalConfig.web.shopInfo
},
"title": "适用商城",
required: true,
"x-rules": [
{
required: true,
message: '请选择适用商城'
}
],
}
}
}
......
......@@ -59,7 +59,7 @@ const AddWarehouse: React.FC<{}> = props => {
>
<Card>
<NiceForm
previewPlaceholder="loading..."
previewPlaceholder="' '"
editable={pageStatus !== PageStatus.PREVIEW}
effects={$ => {}}
initialValues={initValue}
......
......@@ -66,7 +66,7 @@ const AddWarehouse: React.FC<{}> = (props: any) => {
>
<Card>
<NiceForm
previewPlaceholder="loading..."
previewPlaceholder="' '"
editable={pageStatus !== PageStatus.PREVIEW}
effects={($, { setFieldState }) => {
$('onFormMount').subscribe(state => {
......
......@@ -12,20 +12,21 @@ export const setAuth = (info: AuthInfo) => {
export const getAuth = () => {
try {
return JSON.parse(window.localStorage.getItem('auth')) || null
const localAuth = window.localStorage.getItem('auth')
return localAuth ? JSON.parse(localAuth) : null
} catch (error) {
return {}
}
}
export const setRouters = (routers: any[]) => {
window.sessionStorage.setItem('rt', JSON.stringify(routers))
}
export const getRouters = () => {
try {
return JSON.parse(window.sessionStorage.getItem('rt')) || []
const sessionRt = window.sessionStorage.getItem('rt')
return sessionRt ? JSON.parse(sessionRt) : []
} catch (error) {
return []
}
......
......@@ -9,7 +9,7 @@ function isArray(arr: any) {
return Array.isArray(arr)
}
function isObject(obj: any) {
export function isObject(obj: any) {
return Object.prototype.toString.call(obj) === '[object Object]'
}
......@@ -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) => {
const nowUrl = url || window.location.href
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