v1.1.0.Beta+
This commit is contained in:
@@ -33,13 +33,13 @@ class Framework extends Component{
|
||||
}
|
||||
|
||||
init(props, refresh){
|
||||
let { data } = props, sels;
|
||||
let { data, prop, initValue } = props, sels;
|
||||
//如果新数据和旧数据不同 或者 强制刷新 才进行数据处理
|
||||
if(refresh){
|
||||
let dataObj = {};
|
||||
let flatData = [];
|
||||
this.load(data, dataObj, flatData);
|
||||
sels = props.initValue ? this.exchangeValue(props.initValue, true, dataObj) : Object.values(dataObj).filter(item => item[props.prop.selected] === true).filter(item => item[this.props.prop.optgroup] !== true)
|
||||
sels = initValue ? this.exchangeValue(initValue, true, dataObj) : Object.values(dataObj).filter(item => item[prop.selected] === true).filter(item => item[prop.optgroup] !== true)
|
||||
this.setState({ sels, dataObj, flatData });
|
||||
}
|
||||
|
||||
@@ -85,7 +85,8 @@ class Framework extends Component{
|
||||
const { children, optgroup, value, selected, disabled } = prop;
|
||||
data.forEach(item => {
|
||||
//数据提取/处理
|
||||
item.__node = { parent }
|
||||
item.__node = { parent, loading: item.__node && item.__node.loading }
|
||||
|
||||
dataObj[item[value]] = item;
|
||||
flatData.push(item);
|
||||
//遍历子级数据
|
||||
@@ -212,20 +213,19 @@ class Framework extends Component{
|
||||
}
|
||||
this.resetSelectValue(sels, [item], !itemSelected);
|
||||
}
|
||||
let parent = item.__node.parent;
|
||||
if(parent){
|
||||
while(parent){
|
||||
let child = parent[children], len = child.length;
|
||||
let slen = child.filter(i => sels.findIndex(sel => sel[value] === i[value]) !== -1 || i.__node.selected === true).length;
|
||||
parent.__node.selected = slen === len;
|
||||
parent.__node.half = slen > 0 && slen < len;
|
||||
parent = parent.__node.parent;
|
||||
}
|
||||
this.setState({ data: this.state.data })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let parent = item.__node.parent;
|
||||
if(parent){
|
||||
while(parent){
|
||||
let child = parent[children], len = child.length;
|
||||
let slen = child.filter(i => sels.findIndex(sel => sel[value] === i[value]) !== -1 || i.__node.selected === true).length;
|
||||
parent.__node.selected = slen === len;
|
||||
parent.__node.half = (slen > 0 && slen < len) || child.filter(i => i.__node.half === true).length > 0;
|
||||
parent = parent.__node.parent;
|
||||
}
|
||||
this.setState({ data: this.state.data })
|
||||
}
|
||||
|
||||
//检查是否为选择即关闭状态, 强制删除情况下不做处理
|
||||
clickClose && !mandatoryDelete && this.onClick();
|
||||
@@ -265,8 +265,7 @@ class Framework extends Component{
|
||||
let changeData = data.filter(item => item[this.props.prop.selected] === true);
|
||||
this.resetSelectValue(mergeArr(changeData, this.state.sels, this.props.prop), changeData, true);
|
||||
|
||||
let dataObj = {};
|
||||
let flatData = [];
|
||||
let dataObj = {}, flatData = [];
|
||||
this.load(data, dataObj, flatData);
|
||||
this.setState({ data, flatData });
|
||||
}else
|
||||
@@ -285,6 +284,10 @@ class Framework extends Component{
|
||||
//自动判断模式
|
||||
if(type === 'auto'){
|
||||
this.auto(data);
|
||||
}else
|
||||
//树状结构数据更新
|
||||
if(type === 'treeData'){
|
||||
this.value(this.state.sels, null, true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,7 +326,6 @@ class Framework extends Component{
|
||||
}
|
||||
|
||||
render(config, state) {
|
||||
|
||||
const { theme, prop, radio, repeat, clickClose, on, max, maxMethod, content, disabled, tree } = config;
|
||||
const borderStyle = { borderColor: theme.color };
|
||||
let { data, dataObj, flatData, sels, show, tmpColor } = state;
|
||||
@@ -356,7 +358,6 @@ class Framework extends Component{
|
||||
//渲染组件
|
||||
let Body = content ? <Custom { ...bodyProps } /> : tree.show ? <Tree { ...bodyProps } /> : <General { ...bodyProps } />;
|
||||
|
||||
|
||||
return (
|
||||
<xm-select { ...xmSelectProps } >
|
||||
<input class="xm-select-default" name={ config.name } value={ sels.map(item => item[prop.value]).join(',') }></input>
|
||||
|
||||
@@ -52,8 +52,9 @@ class Label extends Component{
|
||||
const conf = label[type];
|
||||
|
||||
//渲染结果
|
||||
let html = '';
|
||||
let innerHTML = true;
|
||||
let html = '', innerHTML = true;
|
||||
//悬浮显示已选择
|
||||
let title = sels.map(item => item[name]).join(',')
|
||||
|
||||
if(type === 'text'){
|
||||
html = sels.map(sel => `${conf.left}${sel[name]}${conf.right}`).join(conf.separator)
|
||||
@@ -103,7 +104,7 @@ class Label extends Component{
|
||||
<div class="scroll" ref={ ref => this.labelRef = ref }>
|
||||
{ innerHTML ?
|
||||
<div class="label-content" dangerouslySetInnerHTML={{__html: html}}></div> :
|
||||
<div class="label-content">{ html }</div>
|
||||
<div class="label-content" title={ title }>{ html }</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,7 +14,7 @@ class General extends Component{
|
||||
remote: true,
|
||||
loading: false,
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
totalSize: 0,
|
||||
});
|
||||
|
||||
this.searchCid = 0;
|
||||
@@ -55,14 +55,16 @@ class General extends Component{
|
||||
if(index <= 1){
|
||||
return ;
|
||||
}
|
||||
this.changePageIndex(index - 1);
|
||||
this.changePageIndex(index - 1);
|
||||
this.props.pageRemote && this.postData(index - 1, true);
|
||||
}
|
||||
pageNextClick(e, size){
|
||||
let index = this.state.pageIndex;
|
||||
if(index >= size){
|
||||
return ;
|
||||
}
|
||||
this.changePageIndex(index + 1);
|
||||
this.changePageIndex(index + 1);
|
||||
this.props.pageRemote && this.postData(index + 1, true);
|
||||
}
|
||||
|
||||
changePageIndex(index){
|
||||
@@ -72,7 +74,7 @@ class General extends Component{
|
||||
}
|
||||
|
||||
searchInput(e){
|
||||
let v = e.target.value;
|
||||
let v = e.target.value;
|
||||
|
||||
if(v === this.__value){
|
||||
return ;
|
||||
@@ -101,7 +103,7 @@ class General extends Component{
|
||||
|
||||
handleComposition(e){
|
||||
let type = e.type;
|
||||
|
||||
|
||||
if(type === 'compositionstart'){
|
||||
this.inputOver = false;
|
||||
clearTimeout(this.searchCid);
|
||||
@@ -109,8 +111,26 @@ class General extends Component{
|
||||
this.inputOver = true;
|
||||
this.searchInput(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
postData(pageIndex = this.state.pageIndex, mandatory = false){
|
||||
if(this.state.remote || mandatory){
|
||||
this.callback = false;
|
||||
this.setState({ loading: true, remote: false });
|
||||
//让输入框失去焦点
|
||||
this.blur();
|
||||
this.props.remoteMethod(this.state.filterValue, (result, totalSize) => {
|
||||
//回调后可以重新聚焦
|
||||
this.focus();
|
||||
|
||||
this.callback = true;
|
||||
this.setState({ loading: false, totalSize });
|
||||
this.props.onReset(result, 'data');
|
||||
}, this.props.show, pageIndex);
|
||||
}
|
||||
}
|
||||
|
||||
//组件将要接收新属性
|
||||
componentWillReceiveProps(props){
|
||||
if(this.props.show != props.show){
|
||||
if(!props.show){
|
||||
@@ -123,22 +143,10 @@ class General extends Component{
|
||||
setTimeout(() => this.focus(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(){
|
||||
if(this.callback){
|
||||
this.callback = false;
|
||||
|
||||
let done = this.props.filterDone;
|
||||
if(isFunction(done)){
|
||||
done(this.state.filterValue, this.tempData || []);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render(config) {
|
||||
|
||||
let { data, flatData, prop, template, theme, radio, sels, empty, filterable, filterMethod, remoteSearch, remoteMethod, delay, searchTips, create } = config
|
||||
}
|
||||
|
||||
render(config) {
|
||||
let { data, flatData, prop, template, theme, radio, sels, empty, filterable, filterMethod, remoteSearch, remoteMethod, delay, searchTips, create, pageRemote } = config
|
||||
|
||||
const { name, value, disabled, children, optgroup } = prop;
|
||||
|
||||
@@ -146,20 +154,7 @@ class General extends Component{
|
||||
//是否开启了搜索
|
||||
if(filterable){
|
||||
if(remoteSearch){//是否进行远程搜索
|
||||
if(this.state.remote){
|
||||
this.callback = false;
|
||||
this.setState({ loading: true, remote: false });
|
||||
//让输入框失去焦点
|
||||
this.blur();
|
||||
remoteMethod(this.state.filterValue, result => {
|
||||
//回调后可以重新聚焦
|
||||
this.focus();
|
||||
|
||||
this.callback = true;
|
||||
this.setState({ loading: false });
|
||||
this.props.onReset(result, 'data');
|
||||
}, this.props.show);
|
||||
}
|
||||
this.postData();
|
||||
}else{
|
||||
const filterData = (item, index) => {
|
||||
const isGroup = item[optgroup];
|
||||
@@ -185,19 +180,17 @@ class General extends Component{
|
||||
//创建条目
|
||||
creator = this.state.filterValue && isFunction(create);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//远程分页
|
||||
if(pageRemote){
|
||||
this.postData();
|
||||
}
|
||||
|
||||
const search = (
|
||||
<div class='xm-search'>
|
||||
<i class="xm-iconfont xm-icon-sousuo"></i>
|
||||
<input type="text" class="xm-input xm-search-input" placeholder={ searchTips }
|
||||
ref={ input => this.searchInputRef = input }
|
||||
onClick={ this.blockClick.bind(this) }
|
||||
onInput={ this.searchInput.bind(this) }
|
||||
onCompositionStart={ this.handleComposition.bind(this) }
|
||||
onCompositionUpdate={ this.handleComposition.bind(this) }
|
||||
onCompositionEnd={ this.handleComposition.bind(this) }
|
||||
/>
|
||||
<input class="xm-input xm-search-input" placeholder={ searchTips } />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -211,7 +204,7 @@ class General extends Component{
|
||||
let paging = '';
|
||||
if(config.paging){
|
||||
//计算当前分页的总页码
|
||||
let size = Math.floor((arr.length - 1) / config.pageSize) + 1;
|
||||
let size = pageRemote ? this.state.totalSize : Math.floor((arr.length - 1) / config.pageSize) + 1;
|
||||
size <= 0 && (size = 1);
|
||||
|
||||
let pageIndex = this.state.pageIndex;
|
||||
@@ -225,11 +218,13 @@ class General extends Component{
|
||||
if(size > 0 && pageIndex <= 0){
|
||||
pageIndex = 1;
|
||||
}
|
||||
|
||||
//实现简单的物理分页
|
||||
let start = (pageIndex - 1) * config.pageSize;
|
||||
let end = start + config.pageSize;
|
||||
arr = arr.slice(start, end);
|
||||
|
||||
if(!pageRemote){
|
||||
//实现简单的物理分页
|
||||
let start = (pageIndex - 1) * config.pageSize;
|
||||
let end = start + config.pageSize;
|
||||
arr = arr.slice(start, end);
|
||||
}
|
||||
|
||||
const disabledStyle = {cursor: 'no-drop', color: '#d2d2d2'};
|
||||
|
||||
@@ -237,23 +232,6 @@ class General extends Component{
|
||||
pageIndex <= 1 && (prevStyle = disabledStyle);
|
||||
pageIndex == size && (nextStyle = disabledStyle);
|
||||
|
||||
// const defaultCurrClass = {
|
||||
// position: 'relative',
|
||||
// borderRadius: '1px',
|
||||
// }
|
||||
// {
|
||||
// ''.padEnd(size, ' ').split('').map((s, i) => (
|
||||
// <span style={
|
||||
// this.state.pageIndex == i + 1 ? {
|
||||
// ...defaultCurrClass,
|
||||
// backgroundColor: theme.color,
|
||||
// borderColor: theme.color,
|
||||
// color: '#FFF',
|
||||
// } : defaultCurrClass
|
||||
// }>{ i + 1 }</span>
|
||||
// ))
|
||||
// }
|
||||
|
||||
this.state.pageIndex !== pageIndex && this.changePageIndex(pageIndex);
|
||||
|
||||
paging = (
|
||||
@@ -324,11 +302,6 @@ class General extends Component{
|
||||
})
|
||||
this.props.onReset(mergeArr(list, selectedList, prop), 'sels');
|
||||
} };
|
||||
}else if(tool === 'SEARCH'){
|
||||
toolStyle.color = theme.color;
|
||||
info = { icon: 'xm-iconfont xm-icon-sousuo', name, method: (pageData) => {
|
||||
|
||||
} };
|
||||
}else {
|
||||
info = tool
|
||||
}
|
||||
@@ -406,7 +379,32 @@ class General extends Component{
|
||||
</div> }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//组件完成挂载
|
||||
componentDidMount(){
|
||||
let input = this.base.querySelector('.xm-search-input');
|
||||
if(input){
|
||||
input.addEventListener('compositionstart', this.handleComposition.bind(this));
|
||||
input.addEventListener('compositionupdate', this.handleComposition.bind(this));
|
||||
input.addEventListener('compositionend', this.handleComposition.bind(this));
|
||||
input.addEventListener('input', this.searchInput.bind(this));
|
||||
this.searchInputRef = input;
|
||||
}
|
||||
}
|
||||
|
||||
//此时页面又被重新渲染了
|
||||
componentDidUpdate(){
|
||||
if(this.callback){
|
||||
this.callback = false;
|
||||
|
||||
let done = this.props.filterDone;
|
||||
if(isFunction(done)){
|
||||
done(this.state.filterValue, this.tempData || []);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default General;
|
||||
|
||||
@@ -34,11 +34,28 @@ class Tree extends Component{
|
||||
|
||||
optionClick(item, selected, disabled, type, e){
|
||||
if(type === 'line'){
|
||||
if(item.__node.loading === true){
|
||||
return;
|
||||
}
|
||||
|
||||
let val = item[this.props.prop.value];
|
||||
let expandedKeys = this.state.expandedKeys;
|
||||
let index = expandedKeys.findIndex(v => v === val);
|
||||
index === -1 ? expandedKeys.push(val) : expandedKeys.splice(index, 1);
|
||||
this.setState({ expandedKeys });
|
||||
|
||||
//是否需要懒加载
|
||||
const { tree, prop, sels } = this.props;
|
||||
let child = item[prop.children];
|
||||
if(tree.lazy && child && child.length === 0 && item.__node.loading !== false){
|
||||
item.__node.loading = true;
|
||||
tree.load(item, (result) => {
|
||||
item.__node.loading = false;
|
||||
item[prop.children] = result;
|
||||
item[prop.selected] = sels.findIndex(i => i[prop.value] === item[prop.value]) != -1
|
||||
this.props.onReset(item, 'treeData');
|
||||
});
|
||||
}
|
||||
}else if(type === 'checkbox'){
|
||||
this.props.ck(item, selected, disabled);
|
||||
}
|
||||
@@ -68,13 +85,13 @@ class Tree extends Component{
|
||||
let dis = item[disabled]
|
||||
// 是否半选
|
||||
let half = item.__node.half === true;
|
||||
|
||||
|
||||
//tree是否遵义严格父子结构
|
||||
if(tree.strict){
|
||||
selected = selected || half || item.__node.selected
|
||||
dis = dis || item.__node.disabled;
|
||||
}
|
||||
|
||||
|
||||
const iconStyle = selected ? {
|
||||
color: theme.color,
|
||||
border: 'none'
|
||||
@@ -88,12 +105,14 @@ class Tree extends Component{
|
||||
}
|
||||
const className = ['xm-option', (dis ? ' disabled' : ''), (selected ? ' selected' : ''), (showIcon ? 'show-icon' : 'hide-icon') ].join(' ');
|
||||
const iconClass = ['xm-option-icon xm-iconfont', radio ? 'xm-icon-danx' : tree.strict && half ? 'xm-icon-banxuan' : 'xm-icon-duox'].join(' ');
|
||||
const treeIconClass = ['xm-tree-icon', expand ? 'expand':'', item[children] && item[children].length > 0 ? 'visible':'hidden'].join(' ');
|
||||
const treeIconClass = ['xm-tree-icon', expand ? 'expand':'', item[children] && (item[children].length > 0 || tree.lazy) ? 'visible':'hidden'].join(' ');
|
||||
|
||||
return (
|
||||
<div class={ className } style={ itemStyle } value={ item[value] } onClick={ this.optionClick.bind(this, item, selected, item[disabled], 'line') }>
|
||||
{ tree.showFolderIcon && <i class={ treeIconClass }></i> }
|
||||
{ tree.showFolderIcon && tree.showLine && <i class={ expand ? 'top-line expand' : 'top-line' } style={ { left: indent - tree.indent + 3 + 'px', width: tree.indent + (expand === 0 ? 10 : -2) + 'px' } }></i> }
|
||||
{ tree.showFolderIcon && tree.showLine && <i class={ expand ? 'left-line expand' : 'left-line' } style={ {left: indent - tree.indent + 3 + 'px'} }></i> }
|
||||
{ item.__node.loading && <span class="loader"></span> }
|
||||
{ showIcon && <i class={ iconClass } style={ iconStyle } onClick={ this.optionClick.bind(this, item, selected, item[disabled], 'checkbox') }></i> }
|
||||
<div class='xm-option-content' dangerouslySetInnerHTML={{ __html: template({ data, item, arr: sels, name: item[name], value: item[value] }) }}></div>
|
||||
</div>
|
||||
@@ -103,11 +122,12 @@ class Tree extends Component{
|
||||
const renderGroup = (item, indent) => {
|
||||
const child = item[children];
|
||||
indent = indent + tree.indent
|
||||
if(child && child.length > 0){//分组模式
|
||||
if(child){//分组模式
|
||||
let expand = this.state.expandedKeys.findIndex(k => item[value] === k) !== -1;
|
||||
child.length === 0 && (expand = false)
|
||||
return (
|
||||
<div class="xm-tree">
|
||||
{ tree.showFolderIcon && tree.showLine && <i class={ expand ? 'left-line expand' : 'left-line' } style={ {left: indent + 3 + 'px'} }></i> }
|
||||
{ tree.showFolderIcon && tree.showLine && expand && <i class='left-line left-line-group' style={ {left: indent + 3 + 'px'} }></i> }
|
||||
{ renderItem(item, indent, expand) }
|
||||
{ expand && <div class="xm-tree-box">{ child.map(c => renderGroup(c, indent)) }</div> }
|
||||
</div>
|
||||
|
||||
@@ -59,7 +59,9 @@ export default function (lan = 'zn') {
|
||||
//分页每页的条数
|
||||
pageSize: 10,
|
||||
//分页无数据是否展示分页
|
||||
pageEmptyShow: true,
|
||||
pageEmptyShow: true,
|
||||
//是否开启远程分页
|
||||
pageRemote: false,
|
||||
//是否开启单选模式
|
||||
radio: false,
|
||||
//是否开启重复选模式
|
||||
@@ -90,7 +92,11 @@ export default function (lan = 'zn') {
|
||||
//默认展开的节点数组
|
||||
expandedKeys: [],
|
||||
//是否严格遵守父子模式
|
||||
strict: true,
|
||||
strict: true,
|
||||
//是否懒加载
|
||||
lazy: false,
|
||||
//懒加载回调
|
||||
load: null,
|
||||
},
|
||||
//自定义属性名称
|
||||
prop: {
|
||||
|
||||
@@ -243,6 +243,13 @@ xm-select{
|
||||
&.selected.hide-icon .xm-option-content{
|
||||
color: #FFF !important;
|
||||
}
|
||||
|
||||
.loader{
|
||||
width: 0.8em;
|
||||
height: 0.8em;
|
||||
margin-right: 6px;
|
||||
color: #C2C2C2;
|
||||
}
|
||||
}
|
||||
|
||||
.xm-select-empty{
|
||||
@@ -392,10 +399,8 @@ xm-select{
|
||||
z-index: 1;
|
||||
border-top: 1px dotted #c0c4cc !important;
|
||||
}
|
||||
&-box{
|
||||
&.dis{
|
||||
|
||||
}
|
||||
.xm-tree-icon+.top-line{
|
||||
margin-left: 1px;
|
||||
}
|
||||
}
|
||||
.scroll-body>.xm-tree>.xm-option>.top-line{
|
||||
@@ -434,22 +439,20 @@ xm-select{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.loader{
|
||||
border: .2em dotted currentcolor;
|
||||
border-radius: 50%;
|
||||
-webkit-animation: 1s loader linear infinite;
|
||||
animation: 1s loader linear infinite;
|
||||
|
||||
|
||||
.loader{
|
||||
border: .2em dotted currentcolor;
|
||||
border-radius: 50%;
|
||||
-webkit-animation: 1s loader linear infinite;
|
||||
animation: 1s loader linear infinite;
|
||||
|
||||
display: inline-block;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
color: inherit;
|
||||
vertical-align: middle;
|
||||
pointer-events: none;
|
||||
}
|
||||
display: inline-block;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
color: inherit;
|
||||
vertical-align: middle;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.xm-select-default{
|
||||
@@ -502,25 +505,15 @@ xm-select{
|
||||
}
|
||||
.xm-tree{
|
||||
.left-line{
|
||||
height: calc(100% - @size);
|
||||
top: @size / 2;
|
||||
height: 100%;
|
||||
bottom: @size / 2;
|
||||
&.expand{
|
||||
height: 100%;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
&:last-child{
|
||||
.left-line{
|
||||
height: calc(100% - @size);
|
||||
}
|
||||
.left-line-group{
|
||||
height: calc(100% - @size);
|
||||
}
|
||||
.xm-tree-icon.hidden+.top-line{
|
||||
top: @size / 2 - 1px;
|
||||
}
|
||||
.xm-tree-icon+.top-line{
|
||||
margin-left: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
xm-select[size='large']{
|
||||
|
||||
Reference in New Issue
Block a user