wx-mini-weview
Version:
weixin UI
287 lines (242 loc) • 10 kB
JavaScript
/**
* @author Myron
* @copyright 2025 Myron
*/
const WvComponentHandler = (options = {}) => {
const handleMethods = options.handleMethods || {};
const properties = {};
// Parse through event types (tap, longpress, etc.)
Object.keys(handleMethods).forEach(eventName => {
const eventMethodsConfig = handleMethods[eventName];
// Handle different configuration formats
let methodConfigs = {};
if (Array.isArray(eventMethodsConfig)) {
// Legacy array format: ['show', 'hide']
eventMethodsConfig.forEach(methodName => {
methodConfigs[methodName] = {}; // Empty config means no default parameters
});
} else {
// New object format: { show: {}, hide: {}, xxx: { defaultParam1 } }
methodConfigs = eventMethodsConfig;
}
// Process each method
Object.keys(methodConfigs).forEach(methodName => {
const defaultParams = methodConfigs[methodName];
const propertyName = `handler${methodName.charAt(0).toUpperCase() + methodName.slice(1)}`;
const eventPropertyName = `${propertyName}Event`;
const paramsPropertyName = `${propertyName}Params`;
properties.handlerSelectAllComponents = {
value: null,
};
// Handler selector property
properties[propertyName] = {
type: String,
value: '',
observer: function(newHandler, oldHandler) {
if (newHandler !== oldHandler) {
const currentEventName = this.data[eventPropertyName] || eventName;
this._bindHandlerToEvent(currentEventName, methodName, newHandler);
}
}
};
// Handler event property (allows changing the event type)
properties[eventPropertyName] = {
type: String,
value: eventName, // Default to the original event type
observer: function(newEventName, oldEventName) {
if (newEventName !== oldEventName) {
const selector = this.data[propertyName];
if (selector) {
// First remove old event bindings
this._removeHandlerForMethod(methodName);
// Then bind with new event name
this._bindHandlerToEvent(newEventName, methodName, selector);
}
}
}
};
// Handler parameters property - only accept arrays for predictable behavior
properties[paramsPropertyName] = {
type: Array,
value: Array.isArray(defaultParams) ? defaultParams : [] // Only use array default params
};
});
});
return Behavior({
properties: properties,
data: {
currentPage: null,
_boundHandlers: {}, // 存储已绑定的处理器信息
},
lifetimes: {
attached() {
this._initAllHandlers();
},
detached() {
this._removeAllEventHandlers();
this.data.currentPage = null;
}
},
methods: {
/**
* 初始化所有事件处理器,如果页面不可用则重试
* @private
* @param {Number} retryCount - 当前重试次数
* @param {Number} maxRetries - 最大重试次数
* @param {Number} retryDelay - 重试延迟时间(毫秒)
*/
_initAllHandlers(retryCount = 0, maxRetries = 10, retryDelay = 300) {
const page = this.getCurrentPage();
if (!page) {
if (retryCount >= maxRetries) {
console.warn('Failed to initialize handlers: maximum retry count reached');
return;
}
// 延迟重试
setTimeout(() => {
this._initAllHandlers(retryCount + 1, maxRetries, retryDelay);
}, retryDelay);
return;
}
// 初始化所有处理器
Object.keys(handleMethods).forEach(eventName => {
const eventMethodsConfig = handleMethods[eventName];
// Handle different configuration formats
let methodConfigs = {};
if (Array.isArray(eventMethodsConfig)) {
// Legacy array format: ['show', 'hide']
eventMethodsConfig.forEach(methodName => {
methodConfigs[methodName] = {}; // Empty config means no default parameters
});
} else {
// New object format: { show: {}, hide: {}, xxx: { defaultParam1 } }
methodConfigs = eventMethodsConfig;
}
// Process each method
Object.keys(methodConfigs).forEach(methodName => {
const propertyName = `handler${methodName.charAt(0).toUpperCase() + methodName.slice(1)}`;
const eventPropertyName = `${propertyName}Event`;
const selector = this.data[propertyName];
if (selector) {
// 使用当前配置的事件类型,默认为handleMethods中的事件
const currentEventName = this.data[eventPropertyName] || eventName;
this._bindHandlerToEvent(currentEventName, methodName, selector);
}
});
});
},
/**
* 将事件处理器绑定到指定事件
* @private
* @param {String} eventName - 事件名称
* @param {String} methodName - 方法名称
* @param {String|Array} selector - 组件选择器或选择器数组
*/
_bindHandlerToEvent(eventName, methodName, selector) {
if (!selector) return;
// 标准化选择器为数组
const selectors = Array.isArray(selector) ? selector : [selector];
let handlerSelectAllComponents = this.data.handlerSelectAllComponents;
let handlerComponents = [];
if(handlerSelectAllComponents){
selectors.forEach(sel => {
const components = handlerSelectAllComponents(sel);
if (components && components.length > 0) {
handlerComponents = handlerComponents.concat(components);
}
});
}else{
const page = this.getCurrentPage();
if (!page) return;
selectors.forEach(sel => {
const components = page.selectAllComponents(sel);
if (components && components.length > 0) {
handlerComponents = handlerComponents.concat(components);
}
});
}
if (!handlerComponents || handlerComponents.length === 0) return;
// 先移除之前该方法绑定的处理器(如果存在)
this._removeHandlerForMethod(methodName);
// 为每个方法生成唯一的scope
const scopeId = `WvHandler_${methodName}_${this.__wxExparserNodeId__}_${Date.now()}`;
// 初始化绑定器存储结构
if (!this.data._boundHandlers[methodName]) {
this.data._boundHandlers[methodName] = {};
}
// 保存绑定信息
this.data._boundHandlers[methodName][eventName] = {
components: handlerComponents,
scope: scopeId
};
// Get parameter property name
const paramsPropertyName = `handler${methodName.charAt(0).toUpperCase() + methodName.slice(1)}Params`;
handlerComponents.forEach(handlerComponent => {
const eventHandler = (e) => {
if (this[methodName]) {
const params = this.data[paramsPropertyName] || [];
// Only support arrays for parameters to ensure predictable behavior
if (Array.isArray(params)) {
this[methodName](...params);
} else {
console.warn(`Method parameters for "${methodName}" should be an array, received: ${typeof params}`);
this[methodName](); // Call without parameters as fallback
}
} else {
console.warn(`Method ${methodName} not found on component`);
}
};
handlerComponent.on(eventName, eventHandler, scopeId);
});
},
/**
* 移除指定方法的处理器
* @private
* @param {String} methodName - 方法名称
*/
_removeHandlerForMethod(methodName) {
const handlerInfo = this.data._boundHandlers[methodName];
if (!handlerInfo) return;
// 移除所有为该方法绑定的事件(可能有多个事件类型)
Object.keys(handlerInfo).forEach(boundEventName => {
const { components, scope } = handlerInfo[boundEventName];
if (components && components.length > 0) {
components.forEach(component => {
if (component && typeof component.off === 'function') {
component.off(boundEventName, scope);
}
});
}
});
// 清除绑定信息
delete this.data._boundHandlers[methodName];
},
/**
* 移除所有事件处理器
* @private
*/
_removeAllEventHandlers() {
if (!this.data._boundHandlers) return;
Object.keys(this.data._boundHandlers).forEach(methodName => {
this._removeHandlerForMethod(methodName);
});
// 重置处理器存储
this.data._boundHandlers = {};
},
/**
* 获取当前页面
* @return {Object} 当前页面实例
*/
getCurrentPage() {
if (!this.data.currentPage) {
const pages = getCurrentPages();
this.data.currentPage = pages[pages.length - 1];
}
return this.data.currentPage;
}
}
});
};
module.exports = {
WvComponentHandler
};