提交 d658d620 编写于 作者: youjia727's avatar youjia727

增加层级联动逻辑关系设置

上级 c9b271f0
VITE_BASE_API="public_variable"
VITE_RANDOM = "tInterview19a6e8cf8aa68bdf39bec4315f284e5dvv="
VITE_RANDOM = "sdfsdfsdffdfgffgfghfhfghfghfghfgfghfghf"
NODE_ENV = 'development'
\ No newline at end of file
VITE_BASE_API="http://192.168.110.72:8000"
VITE_RANDOM = "tInterview19a6e8cf8aa68bdf39bec4315f284e5dvv="
VITE_RANDOM = "asdasasdasasdfsfddgdfdfgfgdfgdf"
NODE_ENV = 'development'
\ No newline at end of file
VITE_BASE_API="https://workapi.dajishizhijia.com"
VITE_RANDOM = "tInterview19a6e8cf8aa68bdf39bec4315f284e5dvv="
VITE_BASE_API="https://xxxxxxxxxxxxxxxxxxxxx"
VITE_RANDOM = "dsdsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfs"
NODE_ENV = 'production'
\ No newline at end of file
VITE_BASE_API="https://workapi-t.dajishizhijia.com"
VITE_RANDOM = "tInterview19a6e8cf8aa68bdf39bec4315f284e5dvv="
VITE_BASE_API="https://xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
VITE_RANDOM = "sdfsdfsdfsdfsdfsdfddfgdfgdfdfgdfdfg"
NODE_ENV='production'
\ No newline at end of file
......@@ -8,12 +8,10 @@
"name": "vite-react-ts-admin",
"version": "0.0.0",
"dependencies": {
"@ant-design/cssinjs": "^1.9.1",
"@ant-design/icons": "^5.0.1",
"@dnd-kit/core": "^6.0.8",
"@dnd-kit/modifiers": "^6.0.1",
"@dnd-kit/sortable": "^7.0.2",
"@dnd-kit/utilities": "^3.2.1",
"antd": "^5.4.6",
"dayjs": "^1.11.7",
"less": "^4.1.3",
......
......@@ -10,12 +10,10 @@
"preview": "vite preview"
},
"dependencies": {
"@ant-design/cssinjs": "^1.9.1",
"@ant-design/icons": "^5.0.1",
"@dnd-kit/core": "^6.0.8",
"@dnd-kit/modifiers": "^6.0.1",
"@dnd-kit/sortable": "^7.0.2",
"@dnd-kit/utilities": "^3.2.1",
"antd": "^5.4.6",
"dayjs": "^1.11.7",
"less": "^4.1.3",
......
......@@ -2,10 +2,11 @@ import { App } from 'antd';
import { useLocation, useRoutes } from 'react-router-dom';
import router from '@/router';
import LoginLayout from '@/components/layout/LoginLayout';
import { ReactElement } from 'react';
function View() {
// 解析路由配置表对象
const outlet = useRoutes(router);
const outlet = useRoutes(router) as ReactElement;
// 初始化location对象实例
const location = useLocation();
......@@ -13,7 +14,7 @@ function View() {
<App>
{/* outlet 占位符,类似于窗口, 有点像vue中的router-view */}
{location.pathname === '/login' ?
<LoginLayout>{outlet as React.ReactElement<any, any>}</LoginLayout> :
<LoginLayout>{outlet}</LoginLayout> :
<>{outlet}</>
}
</App>
......
......@@ -258,7 +258,7 @@
cursor: default;
}
.multipline {
.multiple-line {
height: 58px;
padding-left: 10px;
border: 1px solid #e2e6ed;
......@@ -476,10 +476,15 @@
margin-right: 0;
}
.delete-area-item {
.delete-area-item,
.delete-area-btn {
display: flex;
align-items: center;
}
.delete-area-btn {
display: none;
}
}
}
......@@ -492,7 +497,7 @@
padding-bottom: 0;
.local-wrapper {
display: flex;
display: none;
align-items: center;
.local-text {
......@@ -543,4 +548,21 @@
}
}
}
.cascader-info {
.local-wrapper {
display: flex;
}
}
.cascader-wrapper {
.cascader-item {
.delete-area-btn {
display: flex;
}
}
}
}
\ No newline at end of file
.rule-wrapper {
padding: 16px 0;
padding-top: 16px;
}
.rule-boxs {
......@@ -14,7 +14,7 @@
input {
flex: 1;
margin-left: 14px;
margin-left: 12px;
&:first-of-type {
margin-left: 0;
......@@ -312,11 +312,15 @@
}
.cascader-select-wrapper {
max-height: 314px;
max-height: 310px;
padding: 8px 10px 0;
overflow: auto;
}
.last-cascader-select {
padding-right: 12px;
}
.cascader-title {
font-size: 15px;
font-weight: 600;
......@@ -371,13 +375,14 @@
.cascader-add-wrapper {
font-size: 12px;
height: 32px;
line-height: 32px;
padding: 0 10px;
height: 36px;
line-height: 36px;
color: @primary-color;
text-align: center;
>div {
display: inline-block;
// display: inline-block;
.add-cascader-item-icon {
margin-right: 6px;
......
......@@ -34,7 +34,7 @@ export interface baseProps {
export interface cascaderModeTypes {
label: string,
placeholder: string
text: string
}
/* 基本信息的配置 */
......@@ -104,18 +104,18 @@ const cascaderInit = {
levelCount: 3, //层级数
cascaderMode: [{
label: 'levelOne',
placeholder: ''
text: ''
}, {
label: 'levelTwo',
placeholder: ''
text: ''
}, {
label: 'levelThree',
placeholder: ''
text: ''
}],
setDetail: true,
setDetail: false,
details: {
label: 'details',
placeholder: '详细内容输入区'
text: '填写者输入区'
},
// 配置option的属性
fieldNames: {
......@@ -222,7 +222,7 @@ export const baseCompList: Array<baseCompProps> = [{
icon: 'a-ziyuan22',
tag: 'date'
}, {
label: '层级联动',
label: '级联选择',
icon: 'guanlian',
tag: 'cascader'
}];
......@@ -290,19 +290,19 @@ export const templateCompList = [{
title: '地址',
cascaderMode: [{
label: 'province',
placeholder: '省/自治区/直辖市'
text: '省/自治区/直辖市'
}, {
label: 'city',
placeholder: ''
text: ''
}, {
label: 'district',
placeholder: '区/县'
text: '区/县'
}],
options: [], //配置数据
setDetail: true, //是否设置其他输入
setDetail: false, //是否设置其他输入
details: {
label: 'address',
placeholder: '详细地址'
text: '详细地址'
}
}
}];
\ No newline at end of file
export const options = [{
value: '1',
value: '',
text: '',
children: [{
value: '2',
value: '',
text: '',
children: [{
value: '3',
value: '',
text: '',
children: [{
value: '4',
value: '',
text: '',
}, {
value: '',
......
import { useState, memo, useMemo, useEffect } from 'react';
import { Popover, Input, Cascader } from 'antd';
import { Popover, Cascader } from 'antd';
import { CheckOutlined, CaretDownOutlined, CloseCircleFilled } from '@ant-design/icons';
import '@/assets/style/cascaderList.less';
......@@ -77,7 +77,6 @@ function CascaderList(props: propTypes) {
return treeToList(options);
}, [])
// 控制弹框显示
const [open, setOpen] = useState(false);
// 当前选中数据
......@@ -130,6 +129,10 @@ function CascaderList(props: propTypes) {
if (!checkedList.length || tabKey === 0) return options;
return allCascaderList.find(item => item.text === checkedList[tabKey - 1])?.children;
};
/* 当前选中信息 */
const checkSelectOption = (text: string, key: number) => {
return checkedList[key] === text;
};
/* 完成全部选择 */
const onSelectCascaderFinish = (selectValues: Array<string>) => {
setOpen(false);
......@@ -170,9 +173,9 @@ function CascaderList(props: propTypes) {
<ul className='cascader-list-wrapper'>
{renderCallback(checkedList, activeKey)?.map((item: objTypes) => (
<li key={item.value} onClick={(e) => handleSelectCascader(e, item.text)}
className={`${checkedList.includes(item.text) ? 'checked-cascader' : ''} cursor`}
className={`${checkSelectOption(item.text, activeKey) ? 'checked-cascader' : ''} cursor`}
>
{checkedList.includes(item.text) ? <CheckOutlined className='checked-cascader-icon primary-color' /> : null}
{checkSelectOption(item.text, activeKey) ? <CheckOutlined className='checked-cascader-icon primary-color' /> : null}
<span>{item.text}</span>
</li>
))}
......@@ -197,7 +200,7 @@ function CascaderList(props: propTypes) {
allowClear
open={false}
value={renderCheckedText}
placeholder={placeholder ?? '请选择'}
placeholder={placeholder || '请选择'}
displayRender={() => checkedList.join(separator)}
suffixIcon={<CaretDownOutlined />}
clearIcon={<CloseCircleFilled onClick={handleClear} />}
......
......@@ -10,7 +10,7 @@ import CascaderList from '@/components/CascaderList';
import utils from '@/assets/utils';
import dayjs from 'dayjs';
import { useUpdate } from '@/hooks/useUpdate';
import { baseProps, optionProps, objProps } from '@/assets/utils/formConfig/editorConfig';
import { baseProps, optionProps, objProps, cascaderModeTypes } from '@/assets/utils/formConfig/editorConfig';
import '@/assets/style/preview.less';
const { TextArea } = Input;
......@@ -484,8 +484,11 @@ function Preview(props: propTypes) {
validateFormItem(attr, arr);
};
/* 层级联动的文本占位符提示 */
const cascaderPlaceholderCallback = (cascaderMode: Array<{ label: string, placeholder: string }>) => {
const placeholderList = cascaderMode.map(item => item.placeholder);
const cascaderPlaceholderCallback = (cascaderMode: Array<cascaderModeTypes>) => {
let placeholderList: Array<string> = [];
cascaderMode.forEach(item => {
item.text.trim().length ? placeholderList.push(item.text) : null;
})
return placeholderList.join('/');
};
/* 点击提交数据 */
......@@ -731,8 +734,8 @@ function Preview(props: propTypes) {
{item.type === 'cascader' ?
<div className='space-wrapper'>
<CascaderList
column={item.cascaderMode.length}
options={options}
column={item.levelCount}
options={item.tag === 'address' ? options : item.options}
checkedValue={submitValues[item.id]?.value}
onFinish={(checkedList: Array<string>) => validateFormItem(item.id, checkedList)}
placeholder={cascaderPlaceholderCallback(item.cascaderMode)}
......
import { memo } from 'react';
import { useUpdate } from '@/hooks/useUpdate';
import {
PlusCircleOutlined, EnvironmentOutlined,
CloseOutlined, CaretDownOutlined
} from '@ant-design/icons';
import { baseProps } from '@/assets/utils/formConfig/editorConfig';
import { PlusCircleOutlined, EnvironmentOutlined, CloseOutlined, CaretDownOutlined } from '@ant-design/icons';
import { baseProps, cascaderModeTypes } from '@/assets/utils/formConfig/editorConfig';
type propsType = {
item: baseProps
}
type cascaderObj = {
label: string,
placeholder: string
}
const cascaderList = [{
label: 'province',
placeholder: '省/自治区/直辖市'
text: '省/自治区/直辖市'
}, {
label: 'city',
placeholder: ''
text: ''
}, {
label: 'district',
placeholder: '区/县'
text: '区/县'
}];
/* 层级联动 */
......@@ -49,13 +40,13 @@ function AddressCascader(props: propsType) {
})
};
/* 添加层级的文字提示 */
const addressTextCallback = (cascaderItem: { label: string, placeholder: string }) => {
const addressTextCallback = (cascaderItem: cascaderModeTypes) => {
const idx = cascaderList.findIndex(el => el.label === cascaderItem.label);
return cascaderList[idx + 1].placeholder;
return cascaderList[idx + 1].text;
};
/* 添加层级 */
const handleAddCascader = () => {
const cascaderItem = item.cascaderMode[item.cascaderMode.length - 1];
const cascaderItem: cascaderModeTypes = item.cascaderMode[item.cascaderMode.length - 1];
const idx = cascaderList.findIndex(el => el.label === cascaderItem.label);
update(() => {
item.cascaderMode.push(cascaderList[idx + 1]);
......@@ -65,16 +56,16 @@ function AddressCascader(props: propsType) {
return (
<>
<div className='cascader-wrapper'>
{item.cascaderMode.map((cascader: cascaderObj, idx: number) => (
{item.cascaderMode.map((cascader: cascaderModeTypes, idx: number) => (
<div className='cascader-item' key={cascader.label}>
<span>{cascader.placeholder}</span>
<span>{cascader.text}</span>
<div className='delete-area-item'>
<CaretDownOutlined />
{idx + 1 === item.cascaderMode.length && idx !== 0 ?
<>
<div className='delete-area-btn'>
<div className='line cascader-line'></div>
<CloseOutlined onClick={() => handleDeleteCascader(idx)} className='hover-color' title='删除' />
</>
</div>
: null
}
</div>
......@@ -83,7 +74,7 @@ function AddressCascader(props: propsType) {
</div>
{item.setDetail ?
<div className="placeholder-info cascader-info">
<span>{item.details.placeholder}</span>
<span>{item.details.text}</span>
<div className='local-wrapper'>
{item.tag === 'address' ?
<>
......
import { useState, memo } from 'react';
import { useUpdate } from '@/hooks/useUpdate';
import {
PlusCircleOutlined, CloseOutlined, CaretDownOutlined, EditOutlined
} from '@ant-design/icons';
import { baseProps, objProps } from '@/assets/utils/formConfig/editorConfig';
import { PlusCircleOutlined, CloseOutlined, CaretDownOutlined, EditOutlined } from '@ant-design/icons';
import { baseProps, cascaderModeTypes, objProps } from '@/assets/utils/formConfig/editorConfig';
import CascaderConfig from '../modal/CascaderConfig';
type propsType = {
item: baseProps
}
type cascaderObj = {
label: string,
placeholder: string
}
/* 下拉选择的内容 */
const options = [{
value: 1,
label: '一级联动'
}, {
value: 2,
label: '二级联动'
}, {
value: 3,
label: '三级联动'
}]
/* 层级联动 */
function Cascader(props: propsType) {
......@@ -44,17 +23,30 @@ function Cascader(props: propsType) {
item.setDetail = visible;
})
};
/* placeholder 占位提示语 */
const placeholderCallback = (tipList: Array<cascaderModeTypes>) => {
const tipsText: Array<string> = [];
tipList.forEach(tips => {
tips.text.trim().length ? tipsText.push(tips.text) : null;
})
return tipsText.length ? '' + tipsText.join(' / ') + '' : '';
};
/* 编辑层级联动配置的回调函数 */
const cascaderCallback = (visible: boolean, cascaderRule?: objProps) => {
cascaderRule ? Object.assign(item, cascaderRule) : null;
setOpen(false)
const cascaderCallback = (visible: boolean, config?: objProps) => {
console.log(config)
if (config) {
item.options = config.options;
item.cascaderMode = config.title;
item.levelCount = config.count;
}
setOpen(visible);
};
return (
<>
<div className='cascader-wrapper'>
<div className='cascader-item'>
<span>填写者选择区</span>
<span>填写者选择区{placeholderCallback(item.cascaderMode)}</span>
<div className='delete-area-item'>
<CaretDownOutlined />
</div>
......@@ -62,7 +54,7 @@ function Cascader(props: propsType) {
</div>
{item.setDetail ?
<div className="placeholder-info cascader-info">
<span>{item.details.placeholder}</span>
<span>{item.details.text}</span>
<div className='local-wrapper'>
<CloseOutlined onClick={() => handleSetDetail(false)} className='hover-color' title='删除' />
</div>
......@@ -73,13 +65,12 @@ function Cascader(props: propsType) {
<EditOutlined className='icon-block' />
<span>编辑选项</span>
</div>
{!(item.cascaderMode.length >= 3) && !item.setDetail ? <div className='split-add'></div> : null}
{!item.setDetail ?
<>
<div className='split-add'></div>
<div onClick={() => handleSetDetail(true)} className='setting-block opacity'>
<PlusCircleOutlined className='icon-block' />
<span>添加详细</span>
<span>添加详细输入</span>
</div>
</> : null
}
......
......@@ -39,7 +39,7 @@ function View(props: propsTypes) {
return (
<>
{/* multiline 代表多行输入 */}
<div className={`placeholder-info ${tag === 'textarea' ? 'multiline' : ''}`}>填写者回答区</div>
<div className={`placeholder-info ${tag === 'textarea' ? 'multiple-line' : ''}`}>填写者回答区</div>
<div className="form-item-setting">
<div className='setting-block opacity' onClick={() => setOpen(true)}>
<PlusCircleOutlined className='icon-block' />
......
import { useState } from 'react';
import { useState, useRef } from 'react';
import { PlusCircleOutlined, MinusCircleOutlined, RightOutlined, CaretDownOutlined } from '@ant-design/icons';
import { Modal, Input, Select } from 'antd';
import utils from '@/assets/utils';
import { useUpdate } from '@/hooks/useUpdate';
import { options } from '@/assets/utils/tree';
import useMessage from '@/hooks/useMessage';
import { baseProps, cascaderModeTypes } from '@/assets/utils/formConfig/editorConfig';
import '@/assets/style/modal.less';
......@@ -20,28 +20,24 @@ type propTypes = {
type cascaderOptionTypes = {
value: string,
text: string,
children?: Array<cascaderOptionTypes>
children?: Array<cascaderOptionTypes>,
[key: string]: any
}
// 层级联动配置
const cascaderMode = [{
const cascaderModeList = [{
label: 'levelOne',
placeholder: ''
text: ''
}, {
label: 'levelTwo',
placeholder: ''
text: ''
}, {
label: 'levelThree',
placeholder: ''
text: ''
}, {
label: 'levelFour',
placeholder: ''
}]
// 将字符串转换成js执行
function stringToJs(str: string) {
return new Function('return ' + str)();
};
text: ''
}];
// 截取树的节点
function treeDataSlice<T,>(treeList: Array<T>, level: number) {
......@@ -55,110 +51,281 @@ function treeDataSlice<T,>(treeList: Array<T>, level: number) {
level && treeDataSlice(ele.children, level);
}
};
// 返回列表显示
function treeListCallback<T,>(treeList: Array<T> = [], idx: number): any {
if (!idx) return treeList;
idx--;
return treeListCallback(((treeList[idx]) as cascaderOptionTypes)?.children || [], idx)
};
// 修改层级选项的数据(input输入)
function editCascaderOptionValue(treeList: Array<cascaderOptionTypes> = [], value: string, idx: number, optIdx: number): Array<cascaderOptionTypes> {
const newList = treeListCallback(treeList, idx);
newList[optIdx].text = value;
newList[optIdx].value = value;
return treeList;
};
// 生成树形结构数据
function initTreeData<T,>(treeList: Array<T>, level: number) {
if (level === 4) return options;
// 深拷贝一份
const cloneTreeList = utils.deepClone(treeList);
treeDataSlice(cloneTreeList, level);
console.log(cloneTreeList)
return cloneTreeList;
};
// 树结构的节点
// function addTreeData(nodeCount: number, level: number) {
// // nodeCount 节点数, level 层级数,从0开始
// const treeNode: cascaderOptionTypes = {
// value: '',
// text: '',
// level
// }
// if (nodeCount === 1) return treeNode;
// nodeCount--;
// treeNode['children'] = [addTreeData(nodeCount, level), addTreeData(nodeCount, level)];
// return treeNode;
// };
function addTreeData(nodeCount: number) {
// nodeCount 节点数
const treeNode: cascaderOptionTypes = {
value: '',
text: ''
}
if (nodeCount === 1) return treeNode;
nodeCount--;
treeNode['children'] = [addTreeData(nodeCount), addTreeData(nodeCount)];
return treeNode;
};
// 将树形结构转换成数组
function treeToList<T>(tree: Array<T>) {
let res: Array<T> = [];
let id = 0;
formateData(tree, 0);
function formateData(tree: Array<T>, level: number, pid?: number) {
for (let i = 0; i < tree.length; i++) {
let count = level || 0;
const element = tree[i] as cascaderOptionTypes;
element['level'] = count;
element['id'] = id;
element['pid'] = pid;
res.push(element as T);
id++;
if (element.children) {
count++;
formateData(element.children as Array<T>, count, element.id);
}
}
}
return res;
};
/* 返回增加节点的新树结构 */
function addTreeNodeCallback<T>(treeList: Array<T>, level: number): Array<T> {
let newList = [...treeList];
for (let i = 0; i < newList.length; i++) {
const element = newList[i] as cascaderOptionTypes;
if (!element.children) {
// 增加新节点到树上结构
const treeNode = addTreeData(level);
element['children'] = [treeNode, treeNode];
} else {
addTreeNodeCallback(element.children, level);
}
}
return newList;
};
const numArrIndex = ['', '', '', ''];
/* 层级联动数据配置 */
function CascaderConfig(props: propTypes) {
const { open, cancel, item } = props;
// console.log('item===========', item)
const update = useUpdate();
const selectRef = useRef(null);
const message = useMessage();
// 控制弹框显示
const [show, setShow] = useState(open);
// 是否点击确认按钮
const [clickBtn, setClickBtn] = useState(false);
// 选择层级联动次数
const [visible, setVisible] = useState(false);
// 当前编辑的列索引
const [idxObj, setIdxObj] = useState({
idx0: 0,
idx1: 0,
idx2: 0,
idx3: 0,
idx4: 0
idx3: 0
})
// 层级联动级数
const [count, setCount] = useState<number>(item.levelCount || 3);
// 层级联动显示数据
const [cascaderMode, setCascaderMode] = useState<Array<cascaderModeTypes>>(item.cascaderMode);
// 层级联动列表
const [cascaderOption, setCascaderOption] = useState(() => {
// 层级联动树型列表
const [cascaderOption, setCascaderOption] = useState<Array<cascaderOptionTypes>>(() => {
return item.options.length ? item.options : initTreeData(options, count);
});
// 层级联动数组列表
// const [cascaderList, setCascaderList] = useState<Array<cascaderOptionTypes>>(treeToList(cascaderOption));
/* 标题输入框内容发生变化 */
const titleInputChange = (value: string, idx: number) => {
setCascaderMode(preList => {
const newList = utils.deepClone(preList);
newList[idx].placeholder = value;
newList[idx].text = value;
return newList;
})
};
/* 筛选出需要修改的层级下标索引 */
const levelNumberCallback = (idx: number) => {
const arr = [0, 1, 2, 3];
return arr.filter(el => el > idx);
};
/* 当前选项选中 */
const handleSelectActive = (idx: number, optIdx: number) => {
const newObj = { ...idxObj };
const arr = [0, 1, 2, 3];
const newArr = arr.filter(el => el > idx);
const newArr = levelNumberCallback(idx);
// 修改当前选中项,后面层级选中第一个
for (let i = 0; i < newArr.length; i++) {
newObj[('idx' + (newArr[i] + 1)) as keyof typeof idxObj] = 0;
newObj[('idx' + newArr[i]) as keyof typeof idxObj] = 0;
}
// 设置当前选中状态
newObj[('idx' + (idx + 1)) as keyof typeof idxObj] = optIdx;
newObj[('idx' + idx) as keyof typeof idxObj] = optIdx;
setIdxObj(newObj);
};
/* 生成当前显示选项列表 */
const treeListCallback = (treeList: Array<cascaderOptionTypes> = [], idx: number): Array<cascaderOptionTypes> => {
if (!idx) return treeList;
let renderCascaderOptions: Array<cascaderOptionTypes> = [];
for (let i = 0; i < idx; i++) {
// 上一层级选中的索引值
const preEditIdx = idxObj[('idx' + i) as keyof typeof idxObj];
// 上一层级的孩子列表
renderCascaderOptions = (renderCascaderOptions.length ? renderCascaderOptions[preEditIdx].children : treeList[preEditIdx].children) ?? [];
}
return renderCascaderOptions;
};
/* 级联列表显示 */
const renderListCallback = (idx: number, treeList?: Array<cascaderOptionTypes>) => {
return treeListCallback(treeList ?? utils.deepClone(cascaderOption), idx);
};
/* 修改层级选项的数据(input输入) */
const editCascaderOptionValue = (treeList: Array<cascaderOptionTypes> = [], value: string, idx: number, optIdx: number) => {
const newList = renderListCallback(idx, treeList);
newList[optIdx].text = value;
newList[optIdx].value = value;
return treeList;
};
/* 选项输入框内容变化 */
const cascaderInputChange = (value: string, idx: number, optIdx: number) => {
const newData = editCascaderOptionValue(utils.deepClone(cascaderOption), value, idx, optIdx);
setCascaderOption(newData);
};
/* 点击选择层级联动变化的图标 */
const handleSelect = () => {
const { current } = selectRef;
current && (current as objProps).focus();
};
/* 增加节点标题 */
const addTreeOptionTitle = <T,>(modeList: Array<T>, level: number): Array<T> => {
const sliceList = cascaderModeList.slice(modeList.length);
const newModeList = [...modeList];
for (let i = 0; i < level; i++) {
const element = sliceList[i] as T;
newModeList.push(element)
}
return newModeList;
};
/* 选择层级联动级数变化 */
const handleChange = (value: number) => {
let treeData: Array<cascaderOptionTypes> = [];
let newCascaderMode: Array<cascaderModeTypes> = [];
if (value > count) {
// 增加节点
treeData = addTreeNodeCallback(cascaderOption, value - count); //增加节点树
newCascaderMode = addTreeOptionTitle(cascaderMode, value - count); // 增加节点标题
} else {
// 删除节点
treeData = initTreeData(cascaderOption, value); //删除节点树
newCascaderMode = [...cascaderMode].slice(0, value); // 删除节点标题
}
handleSelectActive(0, 0);
setCascaderOption(treeData);
setCascaderMode(newCascaderMode);
setCount(value);
};
/* 级联列表显示 */
const renderListCallback = (idx: number) => {
console.log('idx=======', idx)
return treeListCallback(cascaderOption, idx);
/* 增加选项的回调函数 */
const editCascaderOptionCallback = (treeList: Array<cascaderOptionTypes>, level: number, type: string, treeNode?: cascaderOptionTypes, optIdx?: number) => {
const cloneTreeList = utils.deepClone(treeList);
let childrenList: Array<cascaderOptionTypes> = [];
for (let i = 0; i < level; i++) {
// 上一层级选中的索引值
const preEditIdx = idxObj[('idx' + i) as keyof typeof idxObj];
childrenList = (childrenList.length ? childrenList[preEditIdx].children : cloneTreeList[preEditIdx].children) || [];
}
// 限制至少设置一个选项
if (type === 'delete' && childrenList.length <= 1) {
message.info('至少设置一个选项');
return false;
}
type === 'add' ? childrenList.push(treeNode as cascaderOptionTypes) : childrenList.splice(optIdx as number, 1);
setCascaderOption(cloneTreeList);
};
/* 添加层级的选项 */
const handleAddCascaderOption = (idx: number) => {
const levelList = cascaderMode.map((el, i) => i + 1).reverse();
const treeNode = addTreeData(levelList[idx]); //当前选项节点
// 如果是第一级就直接添加,其它级需要插入到上一级选中的children中
idx ? editCascaderOptionCallback(cascaderOption, idx, 'add', treeNode) : setCascaderOption([...cascaderOption, treeNode]);
};
/* 删除层级选项 */
const handleDeleteCascaderOption = (level: number, optIdx: number) => {
const cloneCascaderOption = [...cascaderOption];
if (!level) {
// 限制至少设置一个选项
if (cloneCascaderOption.length <= 1) {
message.info('至少设置一个选项');
return false;
}
// 如果是第一级就直接删除选项
cloneCascaderOption.splice(optIdx, 1);
setCascaderOption(cloneCascaderOption);
} else {
// 如果是其它层级就需要找到上一节点children的列表,再删除当前选项
editCascaderOptionCallback(cascaderOption, level, 'delete', undefined, optIdx);
};
handleSelectActive(level, optIdx - 1);
};
/* 判断是否有相同的值 */
const uniqueValueCallback = (treeList: Array<cascaderOptionTypes>): boolean => {
const values: Array<string> = []; //数据数组,判断是否重复
return treeList.some(el => {
// 判断是否是空字符串
if (!el.value.trim().length) {
el.value.length > 0 ? message.info('选项内容不能为空') : message.info('请输入选项内容');
return true;
};
// 判断是否内容重复
if (values.includes(el.value)) {
message.info('选项内容重复,请修改')
return true;
} else {
values.push(el.value);
};
// 如果有children就递归判断内容
if (el.children) {
return uniqueValueCallback(el.children);
};
});
};
/* 点击确认操作 */
const handleOk = () => {
setShow(false);
if (!uniqueValueCallback(cascaderOption)) {
setShow(false);
setClickBtn(true);
}
};
/* modal框完全关闭之后 */
const afterClose = () => {
if (!clickBtn) return cancel(false);
// 关闭弹框,并且传递数据
// let ruleObj = {
// options: clone.options,
// fieldNames: clone.fieldNames,
// cascaderPlaceholder: clone.cascaderPlaceholder,
// mode: clone.mode
// }
cancel(false);
const configData = {
options: cascaderOption,
title: cascaderMode,
count
}
cancel(false, configData);
};
return (
......@@ -176,15 +343,16 @@ function CascaderConfig(props: propTypes) {
<div className='cascader-config-wrapper'>
{cascaderMode.map((ele, idx) => (
<div className='cascader-config-item' key={ele.label}>
<div className='cascader-select-wrapper'>
<div className={`cascader-select-wrapper ${cascaderMode.length === idx + 1 ? 'last-cascader-select' : ''}`}>
<Input className='form-item-input cascader-title'
value={ele.placeholder} onChange={e => titleInputChange(e.target.value, idx)}
value={ele.text} onChange={e => titleInputChange(e.target.value, idx)}
placeholder={numArrIndex[idx] + '级标题名称'}
/>
{renderListCallback(idx).map((opt: cascaderOptionTypes, i: number) => (
<div className='cascader-select-item' key={'cascader-select-' + idx + '-' + i}>
<MinusCircleOutlined className='cascader-option-icon hover-color' title='删除' />
<div className={`input-wrapper ${idxObj[('idx' + (idx + 1)) as keyof typeof idxObj] === i ? 'cascader-option-active' : ''}`} onClick={() => handleSelectActive(idx, i)}>
<MinusCircleOutlined className='cascader-option-icon hover-color' title='删除' onClick={() => handleDeleteCascaderOption(idx, i)} />
<div className={`input-wrapper ${idxObj[('idx' + idx) as keyof typeof idxObj] === i ? 'cascader-option-active' : ''}`}
onClick={() => handleSelectActive(idx, i)}>
<Input value={opt.text} onChange={e => cascaderInputChange(e.target.value, idx, i)} placeholder={numArrIndex[idx] + '级选项名称'} />
{cascaderMode.length === idx + 1 ? null : <RightOutlined className='cascader-right-icon' />}
</div>
......@@ -192,7 +360,7 @@ function CascaderConfig(props: propTypes) {
))}
</div>
<div className='cascader-add-wrapper'>
<div className='opacity'>
<div className='cursor' onClick={() => handleAddCascaderOption(idx)}>
<PlusCircleOutlined className='add-cascader-item-icon' />
<span>添加选项</span>
</div>
......@@ -204,10 +372,14 @@ function CascaderConfig(props: propTypes) {
<div className='select-cascader-level'>
<span>下拉框级数</span>
<Select
ref={selectRef}
value={count}
size='small'
open={visible}
onClick={() => setVisible(!visible)}
onBlur={() => setVisible(false)}
popupClassName='select-cascader-level-popover'
suffixIcon={<CaretDownOutlined style={{ fontSize: 10 }} />}
suffixIcon={<CaretDownOutlined onClick={handleSelect} style={{ fontSize: 10 }} />}
style={{ width: 64 }}
dropdownMatchSelectWidth={100}
onChange={handleChange}
......@@ -223,4 +395,5 @@ function CascaderConfig(props: propTypes) {
</Modal>
)
}
export default CascaderConfig;
\ No newline at end of file
......@@ -132,7 +132,7 @@ function RuleInput(props: propTypes) {
))}
</div>
{newRules.length >= 6 ? null :
<div style={{ textAlign: 'center', paddingTop: 10 }}>
<div style={{ textAlign: 'center', paddingBottom: 10 }}>
<Button onClick={handleAddRule} icon={<PlusOutlined />}>添加验证规则</Button>
</div>
}
......
......@@ -66,11 +66,11 @@ export function useReactive(data: objProps) {
/* 普通更新数据 */
export function useUpdate() {
const [_, update] = useState<number>(0);
const [_, update] = useState(0);
function updateCallback(callback: Function) {
callback();
update(Date.now());
update(+new Date());
};
return updateCallback;
};
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册