import type { PopsPanelContainerConfig } from "@whitesev/pops/dist/types/src/components/panel/types/components-container.js"; import type { PopsPanelDeepViewConfig } from "@whitesev/pops/dist/types/src/components/panel/types/components-deepMenu.js"; import type { PopsPanelConfig, PopsPanelContentConfig, PopsPanelViewConfig, } from "@whitesev/pops/dist/types/src/components/panel/types/index.js"; import type { UtilsDictionary } from "@whitesev/utils/dist/types/src/Dictionary.js"; import Qmsg from "qmsg"; import { unsafeWindow } from "ViteGM"; import { DOMUtils, log, pops, SCRIPT_NAME, utils } from "../env.base"; import { CommonUtil } from "./../utils/CommonUtil"; import { ATTRIBUTE_DEFAULT_VALUE, ATTRIBUTE_INIT, ATTRIBUTE_INIT_MORE_VALUE, ATTRIBUTE_KEY, ATTRIBUTE_PLUGIN_SEARCH_CONFIG, KEY, } from "./panel-config"; import { PanelContent } from "./panel-content"; import { PanelMenu } from "./panel-menu"; import { PanelMenuResultsHandler, type PanelMenuExecMenuResult } from "./panel-menu-results-handler"; import { PopsPanelStorageApi } from "./panel-storage"; import { PanelUISize } from "./panel-ui-size"; type ExecMenuCallBackOption = { key: string[]; /** * 触发该回调的键 * * (首次执行时它并不是任意键触发的,所以传入的键是空的) */ triggerKey?: string; /** * 当前菜单项的值 * * 如果`key`|`queryKey`是string[],那么`value`也是对应的`string[]` * @example * 例如:[key1, key2],这时候值为:[value1, value2] * @example * 例如:[key1],这时候值为:[value1] * @example * 例如:key,这时候值为:value */ value: T; /** * 主动添加元素|销毁函数,一般用于异步函数主动使用 * @example * addStoreValue(HTMLElement) * @example * addStoreValue([HTMLElement]) * @example * addStoreValue(Function)) * @example * addStoreValue([Function)]) */ addStoreValue: (...args: PanelMenuExecMenuResult) => void; }; type OnceExecMenuStoreData = { /** * 监听的键名 */ keyList: string[]; /** * 重载菜单执行 * * 当Router改变时,某些对应Router判断才生效的css需要重新执行才会添加 * * 调用该函数会无视once的值 * * + 清空存储的元素列表 * + 清空存储的卸载函数 * + 执行菜单项的回调 */ reload(): void; /** * 清空菜单执行情况 * * + 清空存储的元素列表 * + 清空存储的卸载函数 * + 清空值改变的监听器 * + 清空存储的一次执行的键 */ clear(): void; /** * 清空存储的元素列表 */ clearStoreNodeList(): void; /** * 执行卸载函数,该函数是由菜单执行回调的返回值中的函数构成 * * 执行后情况存储的数组 */ execDestoryFnAndClear(): void; /** * 移除值改变的监听器 */ removeValueChangeListener(): void; /** * 清空存储的一次执行的键 */ clearOnceExecMenuData(): void; /** * 判断是否执行 */ checkMenuExec(): boolean; }; type UrlChangeWithExecMenuOnceEventConfig = { /** 当前url */ url: string; /** 更新前的url */ beforeUrl: string; }; /** * 面板 */ const Panel = { /** 数据 */ $data: { /** @private */ __contentConfigInitDefaultValue: null as UtilsDictionary | null, /** @private */ __onceExecMenuData: null as UtilsDictionary | null, /** @private */ __urlChangeReloadMenuExecOnce: null as UtilsDictionary< string, (config?: UrlChangeWithExecMenuOnceEventConfig) => IPromise > | null, /** @private */ __onceExecData: null as UtilsDictionary | null, /** @private */ __panelConfig: {} as Partial, /** * 面板 */ $panel: null as ReturnType | null, /** * 面板配置 */ panelContent: [] as PopsPanelContentConfig[], /** * 菜单项初始化的默认值 */ get contentConfigInitDefaultValue() { if (this.__contentConfigInitDefaultValue == null) { this.__contentConfigInitDefaultValue = new utils.Dictionary(); } return this.__contentConfigInitDefaultValue; }, /** * 菜单项初始化时禁用的键 */ contentConfigInitDisabledKeys: [], /** * 成功只执行了一次的菜单项 * * + .exec * + .execMenu * + .execMenuOnce */ get onceExecMenuData() { if (this.__onceExecMenuData == null) { this.__onceExecMenuData = new utils.Dictionary(); } return this.__onceExecMenuData; }, /** * 菜单项在url改变时需要重新加载的菜单回调 * * + .execMenuOnce */ get urlChangeReloadMenuExecOnce() { if (this.__urlChangeReloadMenuExecOnce == null) { this.__urlChangeReloadMenuExecOnce = new utils.Dictionary(); } return this.__urlChangeReloadMenuExecOnce; }, /** * 成功只执行了一次的项 * * + .onceExec */ get onceExecData() { if (this.__onceExecData == null) { this.__onceExecData = new utils.Dictionary(); } return this.__onceExecData; }, /** 脚本名,一般用在设置的标题上 */ get scriptName() { return SCRIPT_NAME; }, /** * pops.panel的默认配置 */ get panelConfig() { return this.__panelConfig; }, set panelConfig(value) { this.__panelConfig = value; }, /** 菜单项的总值在本地数据配置的键名 */ key: KEY, /** 菜单项在attributes上配置的菜单键 */ attributeKeyName: ATTRIBUTE_KEY, /** 菜单项在attributes上配置的菜单默认值 */ attributeDefaultValueName: ATTRIBUTE_DEFAULT_VALUE, }, init() { this.initContentDefaultValue(); PanelMenu.init(); }, /** * 对菜单配置项的键进行默认值初始化储存(到内存中) */ initContentDefaultValue() { /** * 设置默认值 * @param config */ const initDefaultValue = (config: PopsPanelContainerConfig | PopsPanelViewConfig) => { if (!config.attributes) { // 必须配置attributes属性,用于存储菜单的键和默认值 return; } // 排除掉不需要初始化默认值的配置 if (config.type === "button" || config.type === "container" || config.type === "deepMenu") { return; } const attributes = config.attributes; // 调用初始化方法,返回false则阻止默认行为 const __attr_init__: PanelData_ATTRIBUTE_INIT = attributes[ATTRIBUTE_INIT]; if (typeof __attr_init__ === "function") { const __attr_result__ = __attr_init__(); if (typeof __attr_result__ === "boolean" && !__attr_result__) { return; } } /** * 初始化配置对象,每个是需要配置的键值对 * * key是菜单键 * * value是默认值 */ const menuDefaultConfig = new Map(); // 普通的 键名 const key = attributes[ATTRIBUTE_KEY]; if (key != null) { // 键名对应的默认值 const defaultValue: PanelData_ATTRIBUTE_DEFAULT_VALUE = attributes[ATTRIBUTE_DEFAULT_VALUE]; menuDefaultConfig.set(key, defaultValue); } // 待初始化默认值的配置项 const moreMenuDefaultConfig: PanelData_ATTRIBUTE_INIT_MORE_VALUE = attributes[ATTRIBUTE_INIT_MORE_VALUE]; if (typeof moreMenuDefaultConfig === "object" && moreMenuDefaultConfig) { // 追加|覆盖 Object.keys(moreMenuDefaultConfig).forEach((key) => { const defaultValue = moreMenuDefaultConfig[key]; menuDefaultConfig.set(key, defaultValue); }); } if (!menuDefaultConfig.size) { log.warn("请先配置键", config); return; } if (config.type === "switch") { const disabled = typeof config.disabled === "function" ? config.disabled() : config.disabled; if (typeof disabled === "boolean" && disabled) { this.$data.contentConfigInitDisabledKeys.push(...menuDefaultConfig.keys()); } } // 循环初始化默认值 for (const [__key, __defaultValue] of menuDefaultConfig.entries()) { // 设置默认值 this.setDefaultValue(__key, __defaultValue); } }; /** 嵌套循环初始化默认值 */ const loopInitDefaultValue = (configList: PopsPanelContentConfig["views"]) => { for (let index = 0; index < configList.length; index++) { const configItem = configList[index]; initDefaultValue(configItem); const childViews = (configItem).views; if (childViews && Array.isArray(childViews)) { // 存在子配置forms loopInitDefaultValue(childViews); } } }; const contentConfigList = [...PanelContent.getAllContentConfig()]; for (let index = 0; index < contentConfigList.length; index++) { const leftContentConfigItem = contentConfigList[index]; if (!leftContentConfigItem.views) { // 不存在forms continue; } // 循环左侧容器内存储的右侧配置项 const rightContentConfigList = leftContentConfigItem.views; if (rightContentConfigList && Array.isArray(rightContentConfigList)) { loopInitDefaultValue(rightContentConfigList); } } // 去重 this.$data.contentConfigInitDisabledKeys = [...new Set(this.$data.contentConfigInitDisabledKeys)]; }, /** * 设置初始化使用的默认值 * @param key 键 * @param defaultValue 默认值 */ setDefaultValue(key: string, defaultValue: any) { // 存储到缓存中 if (this.$data.contentConfigInitDefaultValue.has(key)) { log.warn("该key已存在,初始化默认值失败: ", { key, initValue: this.$data.contentConfigInitDefaultValue.get(key), }); } this.$data.contentConfigInitDefaultValue.set(key, defaultValue); }, /** * 获取初始化存储的默认值 * @param key 键 */ getDefaultValue(key: string): T { return this.$data.contentConfigInitDefaultValue.get(key); }, /** * 设置值 * @param key 键 * @param value 值 */ setValue(key: string, value: any) { PopsPanelStorageApi.set(key, value); }, /** * 获取值 * @param key 键 * @param defaultValue 默认值 */ getValue(key: string, defaultValue?: T): T { const localValue = PopsPanelStorageApi.get(key); if (localValue == null) { // 值不存在或值为null/undefined或只有键但无值 if (this.$data.contentConfigInitDefaultValue.has(key)) { // 先判断是否是菜单配置的键 // 是的话取出值并返回 return this.$data.contentConfigInitDefaultValue.get(key); } return defaultValue as T; } return localValue; }, /** * 删除值 * @param key 键 */ deleteValue(key: string) { PopsPanelStorageApi.delete(key); }, /** * 判断该键是否存在 * @param key 键 */ hasKey(key: string) { return PopsPanelStorageApi.has(key); }, /** * 监听调用setValue、deleteValue * @param key 需要监听的键 * @param callback 值改变时触发的回调 * @param option 其它配置 */ addValueChangeListener( key: string, callback: (key: string, newValue: any, oldValue: any) => void, option?: { /** * 是否立即触发此回调,此配置不会触发其它对该键的监听回调 * * 如果同时设置了immediateAll,则该配置优先级最高 * @default false */ immediate?: boolean; /** * 是否立即触发所有监听此键的回调 * * 如果同时设置了immediate,则该配置优先级最低 * @default false */ immediateAll?: boolean; } ) { const listenerId = PopsPanelStorageApi.addValueChangeListener(key, callback); if (option?.immediate || option?.immediateAll) { const value = this.getValue(key); if (option?.immediate) { callback(key, value, value); } else if (option?.immediateAll) { Panel.emitMenuValueChange(key, value, value); } } return listenerId; }, /** * 移除监听 * @param listenerId 监听的id */ removeValueChangeListener(listenerId: number) { PopsPanelStorageApi.removeValueChangeListener(listenerId); }, /** * 主动触发菜单值改变的回调 * @param key 菜单键 * @param newValue 想要触发的新值,默认使用当前值 * @param oldValue 想要触发的旧值,默认使用当前值 */ emitMenuValueChange(key: string, newValue?: any, oldValue?: any) { PopsPanelStorageApi.emitValueChangeListener(key, newValue, oldValue); }, /** * 执行菜单 * * @param queryKey 判断的键,如果是字符串列表,那么它们的判断处理方式是与关系 * @param callback 执行的回调函数 * @param checkExec 判断是否执行回调 * * (默认)如果想要每个菜单是`与`关系,即每个菜单都判断为开启,那么就判断它们的值&就行 * * 如果想要任意菜单存在true再执行,那么判断它们的值|就行 * * + 返回值都为`true`,执行回调,如果回调返回了