养成记录的习惯,方便自己,方便他人
1、给<Drawer>
的title
设置省略,并且鼠标悬停展示完整的标题
(1)先 实现鼠标悬停展示完整标题
给<Drawer>
的title
设置<Tooltip>
,一定要记得给他设置 className
。
<Drawer
title={
<Tooltip
trigger="hover"
title={`容器名称:${containerInfo.container_full_name}`}
placement="bottom"
>
{`容器名称:${containerInfo.container_full_name}`}
</Tooltip>
}
...
width={870}
className={containerInfoDrawer}
>
...
</Drawer>
(2)再给title
设置省略
在 css
文件中设置:
.containerInfoDrawer [class~='ant-drawer-title'] {
cursor: pointer;
width: 780px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
(3)运行效果
2、react使用Antd<Form>
中的getFieldDecorator
之后,onchange中执行setFieldsValue
不管事了,如何解决
const [singleImageScanTimeout, setSingleImageScanTimeout] = useState(10);
<FormItem>
{getFieldDecorator('scan_layer_timeout')(
<InputNumber
min={1}
disabled={authStatus}
onChange={(val)=>{
// 这个地方按照自己业务需求来
if (e > singleImageScanTimeout) {
setFieldsValue({
single_image_scan_timeout: val,
});
}
}}
/>)
}
</FormItem>
通过onChange
来实现对scan_single_layer_timeout
进行修改,但发现并没有成功。
解决办法:在getValueFromEvent
中调用setFieldsValue
来做数据数据同步(
const [singleImageScanTimeout, setSingleImageScanTimeout] = useState(10);
<FormItem>
{getFieldDecorator('scan_layer_timeout', {
getValueFromEvent: (e) => {
if (e >= singleImageScanTimeout) {
setSingleImageScanTimeout(e);
setFieldsValue({
single_image_scan_timeout: e,
});
}
return e;
},
})(<InputNumber min={1} max={10} disabled={authStatus} />)}
</FormItem>
<FormItem>
{getFieldDecorator('single_image_scan_timeout', {
getValueFromEvent: (e) => {
setSingleImageScanTimeout(e);
return e;
},
})(<InputNumber min={1} disabled={authStatus} />)}
</FormItem>
3、<Mobal>
和<Select>
结合使用,鼠标滚动,蒙层下的页面随之滚动,发现Select
的下拉框也随之滚动。
就像这样子:
解决办法:给Select
添加 getPopupContainer
属性,例如 getPopupContainer={(trigger) => trigger.parentNode}
<Modal
...
>
<Select
mode="multiple"
size="small"
onChange={(v) => {
...
}}
showArrow
maxTagCount={2}
maxTagPlaceholder={'...'}// 超过两个选项后以...隐藏起来
getPopupContainer={(trigger) => trigger.parentNode}// 防止下拉框随着页面的滚动而滚动
value={...}
>
...
</Select>
</Modal>
4、切换Switch
,变化状态来实现其他项目的正则校验
目的: 想要校验 三个输入框 的值,看这些值是否是正则表达式
4.1 初步实现:没有成功
相关代码:
import { Button, Drawer, Form, message, Select, Switch } from 'antd';
......
const FormItem = Form.Item;
const Option = Select.Option;
const ActionModalEdit = ({
visible,
onClose,
tabSelect,
form,
id,
setShouldUpdate,
}) => {
const { getFieldDecorator, setFieldsValue, validateFields, resetFields } = form;
const [saveButLoading, setSaveButLoading] = useState(false);
const [supportReg, setSupportReg] = useState(false);
const [canSave, setCanSave] = useState(true);
// 判断是否是正则表达式
const isReg = (reg) => {
let isReg;
try {
isReg = eval(reg) instanceof RegExp;
} catch (e) {
isReg = false;
}
return isReg;
};
// 正则校验
const isRegExp = async (rule, value = [], callback) => {
console.log(`value-------1`, value);
console.log(`supportReg--1`, supportReg);
if (supportReg) {
value.forEach((element) => {
console.log('1');
if (!isReg(element)) {
setCanSave(false);
callback(`${element} 错误,请书写正确的正则表达式!`);
} else {
callback && callback();
setCanSave(true);
}
});
callback && callback();
} else {
callback && callback();
}
};
......
const dedupAndTrim = (value) => {
return Array.from(
new Set(value.map((item) => item.trim()).filter((item) => item !== '')),
);
};
useEffect(() => {
if (supportReg) validateFields();
}, [supportReg]);
return (
<Drawer
title={`${visible.title ? '添加' : '编辑'}行为模型`}
visible={!!Object.keys(visible).length}
onClose={onClose}
width="700"
>
<Form className={css.policyform}>
<FormItem label="用户名">
{getFieldDecorator('user_name', {
rules: [
{
required: true,
message: '用户名不能为空',
},
{
validator: isRegExp,
},
],
getValueFromEvent: dedupAndTrim,
})(
<Select
placeholder="请输入用户信息,可输入多个"
mode="tags"
dropdownStyle={{ display: 'none' }}
style={{ width: '100%' }}
/>,
)}
</FormItem>
<FormItem label="进程路径">
{getFieldDecorator('binary_path', {
rules: [
{
required: true,
message: '进程路径不能为空',
},
{
validator: isRegExp,
},
],
getValueFromEvent: dedupAndTrim,
})(
<Select
placeholder="请输入进程路径,可输入多个"
mode="tags"
dropdownStyle={{ display: 'none' }}
style={{ width: '100%' }}
/>,
)}
</FormItem>
<FormItem label="进程命令行">
{getFieldDecorator('cmdline', {
rules: [
{
required: true,
message: '进程命令行不能为空',
},
{
validator: isRegExp,
},
],
getValueFromEvent: dedupAndTrim,
})(
<Select
placeholder="请输入进程命令行,可输入多个"
mode="tags"
dropdownStyle={{ display: 'none' }}
style={{ width: '100%' }}
/>,
)}
</FormItem>
<FormItem>
<span style={{ color: 'rgba(0, 0, 0, 0.85)', lineHeight: '22px' }}>
{' '}
是否支持正则校验:
</span>
<Switch
size="small"
onChange={(val) => {
setSupportReg(val);
}}
/>
</FormItem>
</Form>
<div className={`${css.btn} ${css.actionbtn}`}>
<Button onClick={onClose}>取消</Button>
<Button
onClick={onSave}
>
保存
</Button>
</div>
</Drawer>
);
};
export default Form.create()(ActionModalEdit);
4.2 调试过程中发现:
console.log(`value-------1`, value);
console.log(`supportReg--1`, supportReg);
-
第一次form表单本身校验是:
value
值是正确的,但是supportReg
还是上一次的状态 -
第二次更新的时候是,
useEffect
监听到supportReg
的变化而触发的校验 此时value
值为[]
,但是supportReg
是正确的的值。
4.3 猜想
可能存在异步问题
4.4 经过几番探索
自己尝试在isRegExp
函数采用异步来实现,后来发现并没有成功,还是原来的样子。
猜想,会不会是因为form表单没有没有监听到Switch
的数据导致。
然后尝试给Switch
加上getFieldDecorator
,然后采用 getFieldValue(supportReg)
来获取值,进行判断。后来发现还是不行。
然后有猜想是不是因为 options.valuePropName 子节点的值的属性,如 Switch 的是 'checked'
没有给他配置valuePropName
这个属性,加上之后并没有效果。
4.5 最终效果:
贴了重要的三处:
const isRegExp = (rule, value = [], callback) => {
if (supportReg) {
value.forEach((element) => {
if (!isReg(element)) {
callback(`${element} 错误,请书写正确的正则表达式!`);
}
});
callback();
}
callback();
};
useEffect(() => {
validateFields();
}, [supportReg]);
<FormItem label="是否支持正则校验:">
{getFieldDecorator('supportReg')(
<Switch
checked={supportReg}
size="small"
onChange={(val) => {
setSupportReg(val);
}}
/>,
)}
</FormItem>
我猜想很大的可能是和getFieldDecorator
这个有关,苦思冥想+验证 ,没有找到真正的原因。各位大佬有谁知道可以指点迷津吗?
5、<Selcet>
实现模糊查询
实现效果:
聚焦时展示所有的数据:
查询关键字时展示的数据:
关键代码
// 说明 secondSelectOption 的前提是已经根据接口获得了
const [ready, setReady] = useState(true); // 控制useRequest的触发
const [secondSelectValue, setSecondSelectValue] = useState([]);
const [secondSelectOpen, setSecondSelectOpen] = useState(false);
const [firstText, setFirstText] = useState(searchConfig[0].text);
const [secondSelectOption, setSecondSelectOption] = useState([]);
const [secSelectOptionHasChange, setSecSelectOptionHasChange] = useState([]); // secondSelectOption备份
// 依赖某个状态进行请求
const { run, loading } = useRequest(
() => {
return request(selectReq.url, {
method: 'GET',
params: { search: selectData, page: -1, page_size: -1 },
});
},
{
manual: true,// 是否需要手动触发,若为false则立刻产生请求,若为true
ready,// 重要 - 当manual为true时生效,为true时才产生请求
debounceInterval: 500,
onSuccess: async (res, params) => {
// TODO
},
},
);
// selectData 变化时过滤已经得到的options集合(这里大家也可以根据自己的需要,请求接口也行,记得加一个防抖)
useEffect(() => {
if (secSelectOptionHasChange.length > 5 && selectData) {
let options = _.filter(secSelectOptionHasChange, (item) => {
return item.text.toLowerCase().indexOf(selectData.toLowerCase()) >= 0;
});
setSecondSelectOption(options);
}
if (!selectData) {
setSecondSelectOption(secSelectOptionHasChange);
}
}, [selectData, secSelectOptionHasChange]);
<Select
mode="multiple"
ref={urlSelectRef}
notFoundContent={loading ? <Spin size="small" /> : null}
value={secondSelectValue}
showArrow
onChange={(e) => {
secondSelectFunc(e);
setSecondSelectOpen(false);
urlSelectRef.current.blur();
}}
onFocus={() => {
setReady(false);
setSecondSelectOpen(true);
}}
onBlur={() => {
setReady(true);
setSecondSelectOpen(false);
}}
onSearch={setSelectData}//监听输入框的数据变化
filterOption={false} //下拉框实现模糊搜索,这个过滤⼀定要设置为fasle要不然功能不实现
optionFilterProp="children"
open={secondSelectOpen && secondSelectOption.length > 0}
style={{ width: '65%' }}
>
{secondSelectOption.map((items) => {
return (
<Option value={String(items.args)} key={items.args}>
{items.text}
</Option>
);
})}
</Select>
6、message多次触发弹出多个信息提示 ,但只想要一个message
import { message } from "antd";
// react antd message多次触发弹出多个信息提示
message.config({
top: 100,
duration: 2,
maxCount: 1,
});
message.destroy();
7、上传组件 Upload 的使用
// 判断域名/ip是否正确
const checkIpv6 = (ip) => {
let reg1 = /^(?:\*\.)?[a-z0-9]+(?:[\-.][a-z0-9]+)*\.[a-z]{2,6}$/i;
let reg2 =
/^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}$/;
let valdata = ip.split(',');
let isReal = true;
try {
for (let i = 0; i < valdata.length; i++) {
if (!(reg1.test(valdata[i]) || reg2.test(valdata[i]))) {
isReal = false;
return isReal;
}
}
} catch (error) {
console.log(error);
}
return isReal;
};
// 判断exel文件中的数据是否满足要求
const canSaveData = (list) => {
let err = false;
list.map((i) => {
if (
!i.name ||
!i.address ||
!i.danger_level ||
!i.type ||
!checkIpv6(i.address)
) {
err = true;
}
});
return err;
};
// Upload 的属性配置
const uploadProps = {
name: 'file',
accept: '.xlsx',
action: server.url + '/v1/container/parse_spite_ip_xlsx',
headers: {
'X-Requested-With': null,
'Access-Token': localStorage.getItem('AccessToken'),
},
withCredentials: true,
fileList: fileList,
beforeUpload: (file) => {// 预上传,条件只能上传一个
let arr = [...fileList, file];
if (fileList.length === 0) {
setfileList(arr);
} else {
message.error('只能上传一个文件');
setfileList([...fileList]);
}
return arr.length === 1;
},
onRemove: (file) => {// 删除多余的文件
let list = fileList.filter((item) => item.uid !== file.uid);
setfileList(list);
},
onChange: (info) => {
let { fileList } = info;
let data = [];
let err = false;
let filelist = [];
// 上传成功之后,获取返回来的解析好的数据,判断数据是否正确,正确的数据就保存起来方便后续使用
fileList.forEach((item) => {
if (
item.response &&
item.response instanceof Object &&
item.response.code === 0
) {
data = _.get(fileList, [0, 'response', 'data', 'spite_ip'], []);
err = canSaveData(data);
setError(err);
if (err) {
item.status = 'error';
filelist.push(item);
setfileList(filelist);
message.error('导入的表格数据不正确!');
} else {
setFileData(data);
}
} else {
item.status = 'error';
if (item.response) {
item.response = item.response.msg;
}
}
});
},
};
<Upload {...uploadProps}>
<Button size="small" style={{ width: '68px' }}>
<Icon type="upload" />
上传
</Button>
</Upload>