先更一下
This commit is contained in:
parent
bf77ce01d3
commit
05a4f56c42
2
dist/static/2.js
vendored
2
dist/static/2.js
vendored
File diff suppressed because one or more lines are too long
2
dist/xm-select.js
vendored
2
dist/xm-select.js
vendored
File diff suppressed because one or more lines are too long
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
var demo1 = xmSelect.render({
|
var demo1 = xmSelect.render({
|
||||||
el: '#demo1',
|
el: '#demo1',
|
||||||
max: 2,
|
max: 2,
|
||||||
data: [
|
data: [
|
||||||
{name: '张三', value: 1, selected: true},
|
{name: '张三', value: 1, selected: true},
|
||||||
|
@ -7,12 +7,11 @@
|
|||||||
<script>
|
<script>
|
||||||
var demo1 = xmSelect.render({
|
var demo1 = xmSelect.render({
|
||||||
el: '#demo1',
|
el: '#demo1',
|
||||||
filterable: true,
|
|
||||||
cascader: {
|
cascader: {
|
||||||
show: true,
|
show: true,
|
||||||
showFolderIcon: true,
|
showFolderIcon: true,
|
||||||
showLine: true,
|
showLine: true,
|
||||||
indent: 20,
|
indent: 80,
|
||||||
expandedKeys: true,
|
expandedKeys: true,
|
||||||
},
|
},
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
@ -30,6 +30,7 @@ class Framework extends Component{
|
|||||||
sels: [],
|
sels: [],
|
||||||
show: false,
|
show: false,
|
||||||
tmpColor: '',
|
tmpColor: '',
|
||||||
|
bodyClass: '',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,6 +311,10 @@ class Framework extends Component{
|
|||||||
//树状结构数据更新
|
//树状结构数据更新
|
||||||
if(type === 'close'){
|
if(type === 'close'){
|
||||||
this.onClick();
|
this.onClick();
|
||||||
|
}else
|
||||||
|
//重置class
|
||||||
|
if(type === 'class'){
|
||||||
|
this.setState({ bodyClass: data })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,7 +355,7 @@ class Framework extends Component{
|
|||||||
render(config, state) {
|
render(config, state) {
|
||||||
const { theme, prop, radio, repeat, clickClose, on, max, maxMethod, content, disabled, tree } = config;
|
const { theme, prop, radio, repeat, clickClose, on, max, maxMethod, content, disabled, tree } = config;
|
||||||
const borderStyle = { borderColor: theme.color };
|
const borderStyle = { borderColor: theme.color };
|
||||||
let { data, dataObj, flatData, sels, show, tmpColor } = state;
|
let { data, dataObj, flatData, sels, show, tmpColor, bodyClass } = state;
|
||||||
|
|
||||||
//组件为禁用状态
|
//组件为禁用状态
|
||||||
if(disabled){
|
if(disabled){
|
||||||
@ -387,7 +392,7 @@ class Framework extends Component{
|
|||||||
<i class={ show ? 'xm-icon xm-icon-expand' : 'xm-icon' } />
|
<i class={ show ? 'xm-icon xm-icon-expand' : 'xm-icon' } />
|
||||||
{ sels.length === 0 && <div class="xm-tips">{ config.tips }</div> }
|
{ sels.length === 0 && <div class="xm-tips">{ config.tips }</div> }
|
||||||
<Label { ...labelProps } />
|
<Label { ...labelProps } />
|
||||||
<div class={ ['xm-body', Body.type.name, config.model.type, show ? '':'dis', ].join(' ') } ref={ ref => this.bodyView = ref}>
|
<div class={ ['xm-body', bodyClass, config.model.type, show ? '':'dis', ].join(' ') } ref={ ref => this.bodyView = ref}>
|
||||||
{ Body }
|
{ Body }
|
||||||
</div>
|
</div>
|
||||||
{ disabled && <div class="xm-select-disabled"></div> }
|
{ disabled && <div class="xm-select-disabled"></div> }
|
||||||
|
@ -1,10 +1,34 @@
|
|||||||
import { h, Component, render } from 'preact'
|
import { h, Component, render } from 'preact'
|
||||||
|
import { deepMerge, isFunction } from '@/common/util'
|
||||||
|
|
||||||
class Cascader extends Component{
|
class Cascader extends Component{
|
||||||
|
|
||||||
|
|
||||||
constructor(options){
|
constructor(options){
|
||||||
super(options);
|
super(options);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
expand: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
blockClick(e){
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
optionClick(item, selected, disabled, index, e){
|
||||||
|
|
||||||
|
console.log(item, index);
|
||||||
|
|
||||||
|
this.props.ck(item, selected, disabled);
|
||||||
|
|
||||||
|
let expand = this.state.expand.slice(0, index + 1);
|
||||||
|
expand[index] = item[this.props.prop.value];
|
||||||
|
|
||||||
|
this.setState({ expand });
|
||||||
|
|
||||||
|
//阻止父组件上的事件冒泡
|
||||||
|
this.blockClick(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
//组件将要接收新属性
|
//组件将要接收新属性
|
||||||
@ -18,16 +42,89 @@ class Cascader extends Component{
|
|||||||
}
|
}
|
||||||
|
|
||||||
render(config, state) {
|
render(config, state) {
|
||||||
return (
|
|
||||||
<div class="xm-cascader">
|
|
||||||
|
|
||||||
|
|
||||||
|
const { prop, empty, sels, theme, radio, template, data, cascader } = config;
|
||||||
|
let { name, value, disabled, children } = prop;
|
||||||
|
const showIcon = config.model.icon != 'hidden';
|
||||||
|
|
||||||
|
const renderItem = (item, indent, index) => {
|
||||||
|
//是否被选中
|
||||||
|
let selected = !!sels.find(sel => sel[value] == item[value]);
|
||||||
|
//是否禁用
|
||||||
|
let dis = item[disabled]
|
||||||
|
// 是否半选
|
||||||
|
let half = item.__node.half === true;
|
||||||
|
|
||||||
|
selected = selected || half || item.__node.selected
|
||||||
|
dis = dis || item.__node.disabled;
|
||||||
|
|
||||||
|
const iconStyle = selected ? {
|
||||||
|
color: theme.color,
|
||||||
|
border: 'none'
|
||||||
|
} : {
|
||||||
|
borderColor: theme.color,
|
||||||
|
};
|
||||||
|
|
||||||
|
const itemStyle = { backgroundColor: 'transparent' }
|
||||||
|
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' : half ? 'xm-icon-banxuan' : 'xm-icon-duox'].join(' ');
|
||||||
|
|
||||||
|
|
||||||
|
//处理鼠标选择的背景色
|
||||||
|
const hoverChange = e => {
|
||||||
|
if(e.type === 'mouseenter'){
|
||||||
|
if(!item[disabled]){
|
||||||
|
this.setState({ val: item[value] })
|
||||||
|
}
|
||||||
|
}else if(e.type === 'mouseleave'){
|
||||||
|
this.setState({ val: '' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={ className } style={ itemStyle } value={ item[value] } onClick={
|
||||||
|
this.optionClick.bind(this, item, selected, dis, index)
|
||||||
|
} onMouseEnter={ hoverChange } onMouseLeave={ hoverChange }>
|
||||||
|
{ showIcon && <i class={ iconClass } style={ iconStyle } ></i> }
|
||||||
|
<div class='xm-option-content' dangerouslySetInnerHTML={{ __html: template({ data, item, arr: sels, name: item[name], value: item[value] }) }}></div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderGroup = (item, indent, index) => {
|
||||||
|
|
||||||
|
const child = item[children];
|
||||||
|
|
||||||
|
indent = cascader.indent + 10
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="xm-cascader">
|
||||||
|
{ renderItem(item, indent, index) }
|
||||||
|
{ child && this.state.expand[index] === item[value] &&
|
||||||
|
<div class="xm-cascader-box" index={ index % 4 } style={{ left: indent + 'px' }}>{ child.map(c => renderGroup(c, indent, index + 1)) }</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let arr = data.map(item => renderGroup(item, 0, 0)).filter(a => a);
|
||||||
|
// let safetyArr = deepMerge([], arr);
|
||||||
|
// let safetySels = deepMerge([], sels);
|
||||||
|
|
||||||
|
if(!arr.length){
|
||||||
|
arr.push(<div class="xm-select-empty">{ empty }</div>)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div onClick={ this.blockClick } class="xm-body-cascader">
|
||||||
|
{ arr }
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
//组件完成挂载
|
//组件完成挂载
|
||||||
componentDidMount(){
|
componentDidMount(){
|
||||||
|
this.props.onReset('cascader', 'class');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,12 +11,15 @@ class Tree extends Component{
|
|||||||
this.state = {
|
this.state = {
|
||||||
expandedKeys: [],
|
expandedKeys: [],
|
||||||
filterValue: '',
|
filterValue: '',
|
||||||
|
remote: true,
|
||||||
|
loading: false,
|
||||||
val: emptyVal,
|
val: emptyVal,
|
||||||
}
|
}
|
||||||
|
|
||||||
this.searchCid = 0;
|
this.searchCid = 0;
|
||||||
this.inputOver = true;
|
this.inputOver = true;
|
||||||
this.__value = '';
|
this.__value = '';
|
||||||
|
this.tempData = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
init(props){
|
init(props){
|
||||||
@ -109,7 +112,7 @@ class Tree extends Component{
|
|||||||
//让搜索变成异步的
|
//让搜索变成异步的
|
||||||
this.searchCid = setTimeout(() => {
|
this.searchCid = setTimeout(() => {
|
||||||
this.callback = true;
|
this.callback = true;
|
||||||
this.setState({ filterValue: this.__value })
|
this.setState({ filterValue: this.__value, remote: true })
|
||||||
}, this.props.delay);
|
}, this.props.delay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -158,6 +161,22 @@ class Tree extends Component{
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
postData(){
|
||||||
|
if(this.state.remote){
|
||||||
|
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, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//组件将要接收新属性
|
//组件将要接收新属性
|
||||||
componentWillReceiveProps(props){
|
componentWillReceiveProps(props){
|
||||||
if(this.props.show != props.show){
|
if(this.props.show != props.show){
|
||||||
@ -179,7 +198,7 @@ class Tree extends Component{
|
|||||||
}
|
}
|
||||||
|
|
||||||
render(config, { expandedKeys }) {
|
render(config, { expandedKeys }) {
|
||||||
let { prop, empty, sels, theme, radio, template, data, tree, filterable, searchTips } = config;
|
let { prop, empty, sels, theme, radio, template, data, tree, filterable, remoteSearch, searchTips } = config;
|
||||||
let { name, value, disabled, children } = prop;
|
let { name, value, disabled, children } = prop;
|
||||||
|
|
||||||
const showIcon = config.model.icon != 'hidden';
|
const showIcon = config.model.icon != 'hidden';
|
||||||
@ -276,13 +295,20 @@ class Tree extends Component{
|
|||||||
|
|
||||||
//这里处理过滤数据
|
//这里处理过滤数据
|
||||||
if(filterable){
|
if(filterable){
|
||||||
this.filterData(data, this.state.filterValue);
|
//检查是否需要远程搜索
|
||||||
|
if(remoteSearch){
|
||||||
|
this.postData();
|
||||||
|
}else{
|
||||||
|
this.filterData(data, this.state.filterValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let arr = data.map(item => renderGroup(item, 10 - tree.indent)).filter(a => a);
|
let arr = data.map(item => renderGroup(item, 10 - tree.indent)).filter(a => a);
|
||||||
let safetyArr = deepMerge([], arr);
|
let safetyArr = deepMerge([], arr);
|
||||||
let safetySels = deepMerge([], sels);
|
let safetySels = deepMerge([], sels);
|
||||||
|
|
||||||
|
this.tempData = safetyArr;
|
||||||
|
|
||||||
//工具条操作
|
//工具条操作
|
||||||
const toolbar = (
|
const toolbar = (
|
||||||
<div class='xm-toolbar'>
|
<div class='xm-toolbar'>
|
||||||
@ -338,6 +364,9 @@ class Tree extends Component{
|
|||||||
<div onClick={ this.blockClick } class="xm-body-tree" >
|
<div onClick={ this.blockClick } class="xm-body-tree" >
|
||||||
{ search }
|
{ search }
|
||||||
<div class="scroll-body" style={ {maxHeight: config.height} }>{ arr }</div>
|
<div class="scroll-body" style={ {maxHeight: config.height} }>{ arr }</div>
|
||||||
|
{ this.state.loading && <div class="loading" >
|
||||||
|
<span class="loader"></span>
|
||||||
|
</div> }
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -354,6 +383,18 @@ class Tree extends Component{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//此时页面又被重新渲染了
|
||||||
|
componentDidUpdate(){
|
||||||
|
if(this.callback){
|
||||||
|
this.callback = false;
|
||||||
|
|
||||||
|
let done = this.props.filterDone;
|
||||||
|
if(isFunction(done)){
|
||||||
|
done(this.state.filterValue, this.tempData || []);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Tree;
|
export default Tree;
|
||||||
|
@ -106,6 +106,8 @@ export default function (lan = 'zn') {
|
|||||||
cascader: {
|
cascader: {
|
||||||
//是否展示级联
|
//是否展示级联
|
||||||
show: false,
|
show: false,
|
||||||
|
//间距
|
||||||
|
indent: 200,
|
||||||
},
|
},
|
||||||
//自定义属性名称
|
//自定义属性名称
|
||||||
prop: {
|
prop: {
|
||||||
|
@ -427,22 +427,36 @@ xm-select{
|
|||||||
width: 0 !important;
|
width: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.Cascader{
|
.xm-cascader{
|
||||||
border: none;
|
// position: relative;
|
||||||
background-color: transparent;
|
|
||||||
box-shadow: none;
|
&-box{
|
||||||
width: unset;
|
position: absolute;
|
||||||
min-width: unset;
|
left: 0;
|
||||||
padding: 0;
|
right: 0;
|
||||||
margin-left: -1px;
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 5px 0;
|
||||||
|
|
||||||
.xm-cascader{
|
|
||||||
border: @border;
|
|
||||||
border-radius: 2px;
|
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, .12);
|
|
||||||
background-color: #FFF;
|
|
||||||
min-width: 100px;
|
|
||||||
}
|
}
|
||||||
|
&-box[index="0"]{
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
}
|
||||||
|
&-box[index="1"]{
|
||||||
|
background-color: #FAFAFA;
|
||||||
|
}
|
||||||
|
&-box[index="2"]{
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
}
|
||||||
|
&-box[index="3"]{
|
||||||
|
background-color: #EFEFEF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.cascader{
|
||||||
|
background-color: #EFEFEF;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user