最近在做一个合同管理系统,需要选择多个附件(包括文件id/第三方模板id等信息),然后生成对应的网络签字页面。首先想的就是使用select多选方案解决,但是我这边使用的form是有封装的select选中后无法返回对象成为硬伤(由于后端想要接收多个值,不想为了一个值还去循环select数据库,当然这不考虑xxs问题的前提下尽量信任前端数据),最后实在没办法,就参考网络上大佬的半成品手撸了一个轮子。放出来供大家参考。
http://www.icodebang.com/article/271436
调用方
import AttachmentSelect from './AttachmentSelect';
//....
onChangeMultipleTemplate = data => {
this.props.form.setFieldsValue({
attachments: data,
});
console.log(data);
};
//....
<Form.Item {...formItemLayout} label="附件" name="attachments" >
{getFieldDecorator('attachments', {
initialValue: formData.attachments,
rules: [
{
required: false,
message: '请选择附件',
},
],
})(
<>
<AttachmentSelect onChange={this.onChangeMultipleTemplate} />
</>
)}
</Form.Item>
AttachmentSelect.js
import React, { PureComponent } from 'react';
import { Select } from 'antd';
import { CheckOutlined } from '@ant-design/icons';
import './AttachmentSelect.less';
function parseValue(value) {
if (!value) {
return '';
}
return String(value);
}
export default class TemplateSelect extends PureComponent {
constructor(props) {
super(props);
this.state = {
value: parseValue(props.title),
data: [],
selectChecked: [],
};
}
componentDidMount() {
data=[{id:'1',title:"11"]}
this.setState({ data: data || [] });
}
setselectChecked = data => {
this.setState({
selectChecked: data,
});
};
onFocus = () => {
// console.log('获取焦点');
this.setState({
isOnFocus: true,
});
};
onBlur = () => {
// console.log('失去焦点');
this.setState({
isOnFocus: false,
});
};
checkSelect = data => {
// this.onFocus()
console.log(data);
// 如果存在则删除,如果不存在则添加
// selectChecked.push(data);
let OptionsList=this.state.data
let tempArr = this.state.selectChecked;
let index = -1;
for (let i = 0; i < tempArr.length; i++) {
if (tempArr[i].title === data.title) {
index = i;
console.log('index:', index);
break;
}
}
if (index !== -1) {
// 存在删除
tempArr.splice(index, 1);
this.setselectChecked([...tempArr]);
OptionsList.map((item, index) => {
if (item.title === data.title) {
item.active = false;
}
});
} else {
// 不存在添加
tempArr.push(data);
this.setselectChecked([...tempArr]);
OptionsList.map((item, index) => {
if (item.title === data.title) {
item.active = true;
}
});
}
this.setState({
data:JSON.parse(JSON.stringify(OptionsList)),
});
this.triggerChange(tempArr)
};
// 删除选中的option
areDeleteOption = value => {
console.log('value:', value);
let OptionsList=this.state.data
let tempArr = this.state.selectChecked,
index = -1;
for (let i = 0; i < tempArr.length; i++) {
if (tempArr[i].title === value.title) {
index = i;
console.log('index:', index);
break;
}
}
OptionsList.map((item, index) => {
if (item.title === value.title) {
item.active = false;
}
});
if (index !== -1) {
tempArr.splice(index, 1);
this.setselectChecked([...tempArr]);
}
this.setState({
data:JSON.parse(JSON.stringify(OptionsList)),
});
this.triggerChange(tempArr)
};
triggerChange = (data) => {
const { onChange } = this.props;
if (onChange) {
onChange(data);
}
this.forceUpdate()
};
render() {
const { value, data, selectChecked } = this.state;
return (
<>
<div
className={`select-body ${this.state.isOnFocus ? 'select-header-visited' : ''}`}
suppressContentEditableWarning
contentEditable="true"
onFocus={this.onFocus}
onBlur={this.onBlur}
onKeyDown={e => {
e.preventDefault();
}}
>
<div className={`select-header `}
placeholder="请选择附件"
suppressContentEditableWarning>
{selectChecked.map((item, index) => {
return (
<span key={index} suppressContentEditableWarning contentEditable="false">
<span className={`card`}>
{item?.title}
<span
onMouseDown={e => {
e.preventDefault();
}}
onMouseUp={e => {
e.preventDefault();
}}
onClick={e => {
this.areDeleteOption(item);
}}
>
x
</span>
</span>
</span>
);
})}
</div>
{this.state.isOnFocus ? (
<>
<div contentEditable="false" suppressContentEditableWarning className={`select-content`}>
{data.map((item, index) => {
return (
<li
key={"select-centent"+index}
onClick={e => {
e.preventDefault();
this.checkSelect(item);
}}
>
{item?.title} {item?.active ? <CheckOutlined key={"select-centent-icon"+index} /> : ''}
</li>
);
})}
</div>
</>
) : (
''
)}
</div>
</>
);
}
}
AttachmentSelect.less
@import '~antd/lib/style/themes/default.less';
/* 下拉框整体样式 */
.select-body {
box-sizing: border-box;
position: relative;
display: inline-block;
width: 100%;
// height: 60px;
margin: 0;
padding: 0;
color: #000000d9;
font-size: 14px;
border-radius: 2px;
border: 1px solid #dad8d8;
&:hover {
border: 1px solid rgb(62, 165, 249);
}
&:focus {
// color: transparent;
// text-shadow: 0 0 0 #000;
outline: none;
border: none;
.select-header {
border: 1px solid rgb(62, 165, 249);
border-radius: 2px;
}
}
// [contenteditable]:focus {
// outline: none;
// border:0;
// background-color:yellow;
// }
/* 下拉框搜索框 */
.select-header {
width: 100%;
min-height: 30px;
position: relative;
// padding: 5px;
padding: 0 11px;
// background-color: #fff;
transition: all .3s cubic-bezier(.645, .045, .355, 1);
cursor: text;
&:empty::before {
content: attr(placeholder);
color:#b2b2b2;
}
.select-header-visited {
border: 1px solid rgb(62, 165, 249);
box-shadow: rgba(62, 165, 249, 0.5) 0px 0px 10px;
}
span.card {
display: inline-block;
padding: 0px 0px 0px 10px;
margin: auto 2px;
border: 1px solid #f0f0f0;
border-radius: 2px;
background-color: rgb(245, 245, 245);
pointer-events: none;
cursor: default;
span {
display: inline-block;
margin-left: 2px;
padding: 0 5px;
color: rgba(0, 0, 0, 0.5);
cursor: pointer;
pointer-events: auto;
}
span:hover {
color: rgba(0, 0, 0, 1);
}
}
}
/* 下拉框下拉列表 */
.select-content {
border-radius: 2px;
border-left: 1px solid #ccc;
border-bottom: 1px solid #ccc;
border-right: 1px solid #ccc;
box-shadow: 4px 4px 10px;
li {
width:100%;
cursor: pointer;
position: relative;
padding-left: 15px;
&::after{
position:absolute;
left: 3%;
bottom: 0;
content: " ";
width:94%;
height: 1px;
background: #ccc;
}
// &.active{
// }
}
}
}