You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OneAuth/ui/vhtml/utils.js

294 lines
6.4 KiB
JavaScript

function CamelToKebabCase(str) {
// 首先将字符串的第一个字符转换为小写,避免在首字符前加上'-'
if (str.length === 0) return '';
let firstChar = str.charAt(0).toLowerCase();
// 对剩余部分应用原逻辑:找到每个大写字母,并替换为连字符加上该字母的小写形式
let rest = str.slice(1).replace(/([A-Z])/g, function(match, p1) {
return '-' + p1.toLowerCase();
});
return firstChar + rest;
}
const outerClickList = []
const globalClick = document.addEventListener('click', (event) => {
outerClickList.forEach((item) => {
if (item?.dom instanceof Element && typeof item?.callback === 'function') {
if (!item.dom.contains(event.target)) {
item.callback(event)
}
}
})
})
const AddClicker = (dom, typ, callback) => {
if (typ === 'outer') {
let idx = outerClickList.length
outerClickList.push({ dom, callback })
return () => {
outerClickList[idx] = null
}
}
}
const EventsList = [
// 窗口和框架事件
'load',
'unload',
'beforeunload',
'resize',
'scroll',
// 表单事件
'submit',
'reset',
'input',
'change',
'focus',
'blur',
// 键盘事件
'keydown',
'keypress',
'keyup',
// 鼠标事件
'click',
'dblclick',
'contextmenu',
'mousedown',
'mouseup',
'mousemove',
'mouseover',
'mouseout',
'mouseenter',
'mouseleave',
// 触摸事件
'touchstart',
'touchmove',
'touchend',
'touchcancel',
// 拖拽事件
'drag',
'dragstart',
'dragend',
'dragover',
'dragenter',
'dragleave',
'drop',
// 剪贴板事件
'copy',
'cut',
'paste',
// 动画事件
'animationstart',
'animationend',
'animationiteration',
// 过渡事件
'transitionend',
// 文件操作事件
'abort',
'error',
'loadstart',
'progress',
// 音视频事件
'play',
'pause',
'ended',
'volumechange',
'timeupdate',
'loadeddata',
'waiting',
'playing',
// 网络状态事件
'online',
'offline',
// 存储事件
'storage',
// 页面可见性事件
'visibilitychange'
];
function BindInputDomValue(dom, data, key, watch) {
const element = typeof dom === 'string' ? document.querySelector(dom) : dom;
if (!element) {
console.error('DOM元素未找到');
return;
}
// 根据元素类型进行双向绑定
const elementType = element.type || element.tagName.toLowerCase();
switch (elementType) {
// 文本输入类
case 'text':
case 'password':
case 'email':
case 'tel':
case 'url':
case 'search':
case 'number':
case 'range':
case 'color':
case 'date':
case 'time':
case 'datetime-local':
case 'month':
case 'week':
case 'hidden':
case 'textarea':
watch(() => data[key], (value) => {
if (value === undefined) {
element.value = ''
} else {
element.value = value
}
})
element.addEventListener('input', function() {
data[key] = this.value;
});
break;
case 'checkbox':
watch(function() {
element.checked = !!data[key];
});
element.addEventListener('change', function() {
data[key] = this.checked;
console.log(data, data[key])
});
break;
// 单选框
case 'radio':
// 初始化
watch(() => {
element.checked = element.value === data[key];
})
element.addEventListener('change', function() {
if (this.checked) {
data[key] = this.value;
}
});
break;
// 下拉选择框
case 'select-one':
case 'select-multiple':
watch(() => {
let newValue = data[key]
if (element.multiple) {
const values = Array.isArray(newValue) ? newValue : [];
for (let i = 0; i < element.options.length; i++) {
element.options[i].selected = values.includes(element.options[i].value);
}
} else {
element.value = newValue || '';
}
});
// 监听变化
element.addEventListener('change', function() {
if (this.multiple) {
// 多选
const selectedValues = [];
for (let i = 0; i < this.options.length; i++) {
if (this.options[i].selected) {
selectedValues.push(this.options[i].value);
}
}
data[key] = selectedValues;
} else {
// 单选
data[key] = this.value;
}
});
break;
default:
console.warn(`${elementType} not support v!bind only for input element`, element);
return false
}
return true
}
function SetAttr(dom, key, value) {
// 属性名映射表
const propertyMap = {
'htmlfor': 'htmlFor',
'readonly': 'readOnly',
'maxlength': 'maxLength',
'minlength': 'minLength',
'cellspacing': 'cellSpacing',
'cellpadding': 'cellPadding',
'rowspan': 'rowSpan',
'colspan': 'colSpan',
'tabindex': 'tabIndex',
'usemap': 'useMap',
'frameborder': 'frameBorder',
'contenteditable': 'contentEditable',
'spellcheck': 'spellcheck',
'innerhtml': 'innerHTML',
'innertext': 'innerText',
'autocapitalize': 'autocapitalize',
};
// 需要使用 DOM 属性设置的属性
const domProperties = new Set([
'innerHTML', 'innerText', 'outerHTML', 'textContent',
'value', 'checked', 'selected', 'disabled', 'readOnly',
'maxLength', 'minLength', 'htmlFor',
'tabIndex', 'scrollTop', 'scrollLeft', 'scrollWidth', 'scrollHeight',
'clientWidth', 'clientHeight', 'offsetWidth', 'offsetHeight',
'style', 'dataset'
]);
// 布尔属性
const booleanAttributes = new Set([
'checked', 'selected', 'disabled', 'readonly', 'required',
'hidden', 'autofocus', 'multiple', 'novalidate'
]);
// 转换属性名
const lowerKey = key.toLowerCase();
const mappedKey = propertyMap[lowerKey] || key;
// 设置属性的策略:
if (domProperties.has(mappedKey)) {
// DOM 属性
if (value === undefined) {
dom[mappedKey] = ''
} else {
dom[mappedKey] = value;
}
} else if (booleanAttributes.has(lowerKey)) {
// 布尔属性
if (value) {
dom.setAttribute(lowerKey, '');
} else {
dom.removeAttribute(lowerKey);
}
} else {
// 其他属性使用 setAttribute
if (value === undefined) {
dom.removeAttribute(key);
} else {
dom.setAttribute(key, value);
}
}
}
export default { CamelToKebabCase, EventsList, BindInputDomValue, SetAttr, AddClicker }