@frontend_worker/search-selector
Version:
《远程搜索+单选》组件区别于element-ui或iview组件库的select远程搜索, ①将头部显示的值和下拉选项显示的值独立开来, ②避免远程加载时头部默认值显示异常, ③支持插槽自定义 ④将搜索框值放置于下拉弹框里面
436 lines (386 loc) • 13.3 kB
JavaScript
import Vue from 'vue';
const isServer = Vue.prototype.$isServer;
export const deepCopy = obj => {
return JSON.parse(JSON.stringify(obj));
};
// 判断是否是对象
export function isObject (obj) {
return typeof(obj) === 'object';
}
// js对象深度比较 全相等
export function isEqual (obj1, obj2) {
// 如果传入的不是对象,那就直接比较并且返回
if (!isObject(obj1) || !isObject(obj2)) {
return obj1 === obj2;
}
// 如果传入的两个对象为同一个,那直接返回true
if (obj1 === obj2) {
return true;
}
// 如果两个对象的key的长度不一致,返回false
const obj1Keys = Object.keys(obj1);
const obj2Keys = Object.keys(obj2);
if (obj1Keys.length !== obj2Keys.length) {
return false;
}
// 递归比较
for (let key in obj1) {
const res = isEqual(obj1[key], obj2[key]);
if (!res) {
return false;
}
}
return true;
}
// 判断参数是否是其中之一
export function oneOf (value, validList) {
for (let i = 0; i < validList.length; i++) {
if (value === validList[i]) {
return true;
}
}
return false;
}
const SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
const MOZ_HACK_REGEXP = /^moz([A-Z])/;
function camelCase(name) {
return name.replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
return offset ? letter.toUpperCase() : letter;
}).replace(MOZ_HACK_REGEXP, 'Moz$1');
}
// getStyle
export function getStyle (element, styleName) {
if (!element || !styleName) return null;
styleName = camelCase(styleName);
if (styleName === 'float') {
styleName = 'cssFloat';
}
try {
const computed = document.defaultView.getComputedStyle(element, '');
return element.style[styleName] || computed ? computed[styleName] : null;
} catch(e) {
return element.style[styleName];
}
}
// firstUpperCase
function firstUpperCase(str) {
return str.toString()[0].toUpperCase() + str.toString().slice(1);
}
export {firstUpperCase};
// Warn
export function warnProp(component, prop, correctType, wrongType) {
correctType = firstUpperCase(correctType);
wrongType = firstUpperCase(wrongType);
console.error(`[iView warn]: Invalid prop: type check failed for prop ${prop}. Expected ${correctType}, got ${wrongType}. (found in component: ${component})`); // eslint-disable-line
}
export function typeOf(obj) {
const toString = Object.prototype.toString;
const map = {
'[object Boolean]' : 'boolean',
'[object Number]' : 'number',
'[object String]' : 'string',
'[object Function]' : 'function',
'[object Array]' : 'array',
'[object Date]' : 'date',
'[object RegExp]' : 'regExp',
'[object Undefined]': 'undefined',
'[object Null]' : 'null',
'[object Object]' : 'object'
};
return map[toString.call(obj)];
}
// deepCopy
// function deepCopy(data) {
// const t = typeOf(data);
// let o;
// if (t === 'array') {
// o = [];
// } else if ( t === 'object') {
// o = {};
// } else {
// return data;
// }
// if (t === 'array') {
// for (let i = 0; i < data.length; i++) {
// o.push(deepCopy(data[i]));
// }
// } else if ( t === 'object') {
// for (let i in data) {
// o[i] = deepCopy(data[i]);
// }
// }
// return o;
// }
// export {deepCopy};
// scrollTop animation
export function scrollTop(el, from = 0, to, duration = 500, endCallback) {
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = (
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) {
return window.setTimeout(callback, 1000/60);
}
);
}
const difference = Math.abs(from - to);
const step = Math.ceil(difference / duration * 50);
function scroll(start, end, step) {
if (start === end) {
endCallback && endCallback();
return;
}
let d = (start + step > end) ? end : start + step;
if (start > end) {
d = (start - step < end) ? end : start - step;
}
if (el === window) {
window.scrollTo(d, d);
} else {
el.scrollTop = d;
}
window.requestAnimationFrame(() => scroll(d, end, step));
}
scroll(from, to, step);
}
// Find components upward
function findComponentUpward (context, componentName, componentNames) {
if (typeof componentName === 'string') {
componentNames = [componentName];
} else {
componentNames = componentName;
}
let parent = context.$parent;
let name = parent.$options.name;
while (parent && (!name || componentNames.indexOf(name) < 0)) {
parent = parent.$parent;
if (parent) name = parent.$options.name;
}
return parent;
}
export {findComponentUpward};
// Find component downward
export function findComponentDownward (context, componentName) {
const childrens = context.$children;
let children = null;
if (childrens.length) {
for (const child of childrens) {
const name = child.$options.name;
if (name === componentName) {
children = child;
break;
} else {
children = findComponentDownward(child, componentName);
if (children) break;
}
}
}
return children;
}
// Find components downward
export function findComponentsDownward (context, componentName) {
return context.$children.reduce((components, child) => {
if (child.$options.name === componentName) components.push(child);
const foundChilds = findComponentsDownward(child, componentName);
return components.concat(foundChilds);
}, []);
}
// Find components upward
export function findComponentsUpward (context, componentName) {
let parents = [];
const parent = context.$parent;
if (parent) {
if (parent.$options.name === componentName) parents.push(parent);
return parents.concat(findComponentsUpward(parent, componentName));
} else {
return [];
}
}
// Find brothers components
export function findBrothersComponents (context, componentName, exceptMe = true) {
let res = context.$parent.$children.filter(item => {
return item.$options.name === componentName;
});
let index = res.findIndex(item => item._uid === context._uid);
if (exceptMe) res.splice(index, 1);
return res;
}
/* istanbul ignore next */
const trim = function(string) {
return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '');
};
/* istanbul ignore next */
export function hasClass(el, cls) {
if (!el || !cls) return false;
if (cls.indexOf(' ') !== -1) throw new Error('className should not contain space.');
if (el.classList) {
return el.classList.contains(cls);
} else {
return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1;
}
}
/* istanbul ignore next */
export function addClass(el, cls) {
if (!el) return;
let curClass = el.className;
const classes = cls instanceof Array ? cls : (cls || '').split(' ');
for (let i = 0, j = classes.length; i < j; i++) {
const clsName = classes[i];
if (!clsName) continue;
if (el.classList) {
el.classList.add(clsName);
} else {
if (!hasClass(el, clsName)) {
curClass += ' ' + clsName;
}
}
}
if (!el.classList) {
el.className = curClass;
}
}
/* istanbul ignore next */
export function removeClass(el, cls) {
if (!el || !cls) return;
const classes = cls.split(' ');
let curClass = ' ' + el.className + ' ';
for (let i = 0, j = classes.length; i < j; i++) {
const clsName = classes[i];
if (!clsName) continue;
if (el.classList) {
el.classList.remove(clsName);
} else {
if (hasClass(el, clsName)) {
curClass = curClass.replace(' ' + clsName + ' ', ' ');
}
}
}
if (!el.classList) {
el.className = trim(curClass);
}
}
export const dimensionMap = {
xs: '480px',
sm: '576px',
md: '768px',
lg: '992px',
xl: '1200px',
xxl: '1600px',
};
export function setMatchMedia () {
if (typeof window !== 'undefined') {
const matchMediaPolyfill = mediaQuery => {
return {
media: mediaQuery,
matches: false,
on() {},
off() {},
};
};
window.matchMedia = window.matchMedia || matchMediaPolyfill;
}
}
// 用于 select Cascader 计算 +n 的宽度
function getTagWidth(parent, classes, text, tagName = 'div') {
const tag = document.createElement(tagName);
addClass(tag, classes);
tag.innerText=text;
parent.appendChild(tag);
const width = tag.getBoundingClientRect().width;
parent.removeChild(tag);
return width;
}
// 用于 select Cascader 计算 +n 的数量
// parent tag 的父元素
// total 总tag 数
// inputWidth 开启 filter input的width
// moreTagClasses 未显示的标签数
export function getShowNum(parent, total, inputWidth, moreTagClasses) {
const $tags = parent.querySelectorAll('.zz-tag') || [];
const headWidth = parent.getBoundingClientRect().width;
let showNum = total - $tags.length;
// 倒序遍历所有标签
for(let i = $tags.length - 1; i >= 0;i--) {
const { offsetLeft, offsetTop } = $tags[i];
// 判断标签是在第一行还是在其他行
if(offsetTop>10) {
showNum++;
}else {
// 如果标签在第一行
// 获取 标签宽度
const tagWidth = $tags[i].getBoundingClientRect().width;
// 获取 +n 的宽度
const moreTagWidth = showNum === 0 ? 0 : getTagWidth(parent, moreTagClasses, `+${showNum}`);
// 标签宽度 + 标签左侧离边界距离 + moreTagWidth + 搜索input宽度 > select-head宽度,说明超出
if(tagWidth + offsetLeft + moreTagWidth + inputWidth > headWidth) {
showNum++;
} else {
break;
}
}
}
return showNum;
}
export function isPlaceholderNeedToolTip(refNode, placeholderContent =''){
let inputNode = refNode;
const { offsetWidth }= inputNode;
const { paddingLeft, paddingRight } = window.getComputedStyle(inputNode);
let targetDomStyle = {
opacity: 0,
fontSize: getStyle(inputNode, 'font-size'),
fontFamily: getStyle(inputNode, 'font-family'),
fontWeight: getStyle(inputNode, 'font-weight'),
letterSpacing: getStyle(inputNode, 'letter-spacing')
};
let node = document.createElement('span');
node.style.fontSize = targetDomStyle.fontSize;
node.style.fontFamily = targetDomStyle.fontFamily;
node.style.fontWeight = targetDomStyle.fontWeight;
node.style.letterSpacing = targetDomStyle.letterSpacing;
node.style.opacity =targetDomStyle.opacity;
node.innerHTML = placeholderContent;
document.body.appendChild(node);
let rect = node.getBoundingClientRect();
const { width } = rect;
document.body.removeChild(node);
return width < offsetWidth - parseInt(paddingLeft)- parseInt(paddingRight);
}
export function IEVersion() {
var useAgent = navigator.userAgent;
var isIE = useAgent.indexOf('compatible');
var isEdge = useAgent.indexOf('Edge') > -1 && !isIE;
var isIE11 = useAgent.indexOf('Trident') > -1 && useAgent.indexOf('rv:11.0') > -1;
if(isIE) {
var reIE = new RegExp('MSIE (\\d+\\.\\d+);');
reIE.test(useAgent);
var fIEVersion = parseFloat(RegExp(['$1']));
if (fIEVersion === 7) {
return 7;
} else if (fIEVersion === 8) {
return 8;
} else if (fIEVersion === 9) {
return 9;
} else if (fIEVersion === 10) {
return 10;
} else {
return 6;
}
} else if (isEdge) {
return 'edge';
} else if (isIE11) {
return 11;
} else {
return -1;
}
}
export const sharpMatcherRegx = /#([^#]+)$/;
export function isNumber(v) {
return Object.prototype.toString.call(v) === '[object Number]';
}
export function isExist(val) {
return val !== null && val !== undefined;
}
export function getIntStyle(element, styleName) {
return parseInt(getStyle(element, styleName));
}