import { DataPaging, DOMUtils, log, pops } from "@/env"; import { GenerateData } from "@/main/data/NetDiskGenerateDataUtils"; import { NetDiskRegularExtractor } from "@/main/NetDiskRegularExtractor"; import type { Paging } from "@whitesev/data-paging/dist/types/src/index.js"; import type { PopsRightClickMenuDataConfig } from "@whitesev/pops/dist/types/src/components/rightClickMenu/types/index.js"; import { NetDiskGlobalData } from "@/main/data/NetDiskGlobalData"; import { NetDiskCheckLinkValidity, type NetDiskCheckLinkValidityInfoConfig, } from "@/main/handler/check-valid/NetDiskCheckLinkValidity"; import { NetDisk } from "@/main/NetDisk"; import { NetDiskPops } from "@/main/pops/NetDiskPops"; import { NetDiskView } from "../NetDiskView"; import { NetDiskSuspensionConfig } from "../suspension/NetDiskSuspensionView"; import indexCSS from "./index.css?raw"; import { NetDiskLinkViewData } from "./NetDiskLinkViewData"; import { NetDiskLinkViewEvent } from "./NetDiskLinkViewEvent"; /** * 传递给生成需要的网盘参数数据 */ export type LinkViewRuleData = { /** * 规则键名 */ ruleKeyName: string; /** * 规则下标 */ ruleIndex: number | string; /** * 分享码 */ shareCode: string; /** * 访问码 */ accessCode: AccessCodeType; }; /** * 元素的attribute上存储的匹配到的规则信息 */ export type LinkViewItemInfo = { /** * 规则键名 */ ruleKeyName: string; /** * 规则下标 */ ruleIndex: number; /** * 分享码 */ shareCode: string; /** * 访问码 */ accessCode: AccessCodeType; }; /** * 注册右侧菜单需要传递的配置 */ export type LinkViewRegisterContextMenuShowTextOption = Pick< PopsRightClickMenuDataConfig, "text" | "callback" | "item" > & { icon?: PopsRightClickMenuDataConfig["icon"]; iconIsLoading?: PopsRightClickMenuDataConfig["iconIsLoading"]; }; export type LinkViewData = { /** 规则键名 */ ruleKeyName: string; /** 分享码 */ shareCode: string; /** 匹配后处理的数据 */ netDiskDictData: NetDiskDictData; }; export const NetDiskLinkView = { $el: { get $urlBoxAll() { return NetDiskView.$el.$linkView.$shadowRoot.querySelector(".netdisk-url-box-all")!; }, }, $inst: { dataPaging: null as any as Paging, }, $data: { dataPagingEnable: false, isSmallWindow: false, }, /** * 显示视图 * * 如果视图未创建,则自动创建后再显示 */ show() { const dataPagingEnable = NetDiskGlobalData.smallWindow["netdisk-ui-link-view-data-paging-enable"].value; if (NetDiskView.$el.$linkView == null) { this.$data.dataPagingEnable = dataPagingEnable; this.createLinkView(); NetDiskLinkViewEvent.init(); } else { NetDiskView.$el.$linkView.show(); if (this.$data.dataPagingEnable !== dataPagingEnable) { // 状态已改变 if (dataPagingEnable) { // 开启状态 this.$inst.dataPaging.show(); } else { // 关闭状态 this.$inst.dataPaging.hide(); } this.refreshLinkView(); this.$data.dataPagingEnable = dataPagingEnable; } } if (this.$data.isSmallWindow) { NetDiskSuspensionConfig.mode.current_suspension_smallwindow_mode.value = "smallwindow"; } else { NetDiskSuspensionConfig.mode.current_suspension_smallwindow_mode.value = "window"; } }, /** * 销毁视图 */ destory() { NetDiskView.$el.$linkView?.close(); // @ts-expect-error NetDiskView.$el.$linkView = void 0; }, /** * 创建视图 */ createLinkView() { this.$data.isSmallWindow = NetDiskGlobalData.features["netdisk-behavior-mode"].value .toLowerCase() .includes("smallwindow"); const NetDiskViewConfig = { view: { "netdisl-small-window-shrink-status": GenerateData("netdisl-small-window-shrink-status", false), "netdisk-ui-small-window-position": GenerateData<{ left: number; top: number; } | null>("netdisk-ui-small-window-position", null), }, }; const boxAllContainerHTML = /*html*/ `
`; /** * 判断是否存在悬浮按钮的行为模式 */ const hasSuspension = () => NetDiskGlobalData.features["netdisk-behavior-mode"].value.toLowerCase().includes("suspension"); /** * 关闭视图 */ const closeView = () => { if (hasSuspension()) { // 存在悬浮按钮 // 仅隐藏弹窗并显示悬浮按钮 NetDiskSuspensionConfig.mode.current_suspension_smallwindow_mode.value = "suspension"; NetDiskView.$el.$linkView.hide(); NetDiskView.$inst.suspension.init(); } else { this.destory(); } }; if (this.$data.isSmallWindow) { // 小窗 const emitter = new pops.fn.EventEmiter("alert"); // 再右上角添加展开|收起按钮 emitter.on("pops:before-append-to-page", ($shadowRoot) => { const $headerControl = $shadowRoot.querySelector(".pops-header-control")!; const $title = $shadowRoot.querySelector(".pops-alert-title")!; const $content = $shadowRoot.querySelector(".pops-alert-content")!; // 展开 const $launchIcon = DOMUtils.createElement( "button", { className: "pops-header-control", innerHTML: /*html*/ ` `, }, { type: "button", "data-type": "launch", "data-header": true, } ); // 收起 const $shrinkIcon = DOMUtils.createElement( "button", { className: "pops-header-control", innerHTML: /*html*/ ` `, }, { type: "button", "data-type": "shrink", "data-header": true, } ); DOMUtils.before($headerControl, $launchIcon); DOMUtils.before($headerControl, $shrinkIcon); DOMUtils.on( $launchIcon, "click", function () { // 展开-切换为收缩图标 DOMUtils.addClass($launchIcon, "pops-hide-important"); DOMUtils.removeClass($shrinkIcon, "pops-hide-important"); DOMUtils.removeClass($title, "pops-no-border-important"); DOMUtils.removeClass($content, "pops-hide-important"); NetDiskViewConfig.view["netdisl-small-window-shrink-status"].value = false; }, { capture: true, } ); DOMUtils.on( $shrinkIcon, "click", function () { // 收缩-切换为展开图标 DOMUtils.removeClass($launchIcon, "pops-hide-important"); DOMUtils.addClass($shrinkIcon, "pops-hide-important"); DOMUtils.addClass($title, "pops-no-border-important"); DOMUtils.addClass($content, "pops-hide-important"); NetDiskViewConfig.view["netdisl-small-window-shrink-status"].value = true; }, { capture: true, } ); if (NetDiskViewConfig.view["netdisl-small-window-shrink-status"].value) { $shrinkIcon.click(); } else { $launchIcon.click(); } }); NetDiskView.$el.$linkView = NetDiskPops.alert( { emitter: emitter, title: { text: "网盘", position: "center", }, content: { text: boxAllContainerHTML, html: true, }, btn: { ok: { enable: false, }, close: { callback() { closeView(); }, }, }, mask: { // 小窗没有遮罩层 enable: false, }, // 不需要动画 animation: false, dragMoveCallBack(moveElement, left, top) { NetDiskViewConfig.view["netdisk-ui-small-window-position"].value = { left: left, top: top, }; }, class: "whitesevPop netdisk-link-view-small-window", style: /*css*/ ` ${indexCSS} .pops { --container-title-height: 35px; --content-max-height: ${NetDiskGlobalData.smallWindow["netdisk-ui-small-window-max-height"].value}px; --netdisk-line-space: 8px; --netdisk-icon-size: 24px; } .pops[type-value="alert"]{ transform: none; } .pops { max-height: var(--content-max-height); } .pops[type-value=alert] .pops-alert-content{ max-height: calc(var(--content-max-height) - var(--container-title-height) - var(--container-bottom-btn-height)); } .pops-header-controls button.pops-header-control[type][data-header]{ padding: 0px 5px; } .netdisk-url-div{ padding: 0px; } .netdisk-icon .netdisk-icon-img{ width: var(--netdisk-icon-size); height: var(--netdisk-icon-size); min-width: var(--netdisk-icon-size); min-height: var(--netdisk-icon-size); margin: 0px var(--netdisk-line-space); } .netdisk-status{ margin-right: var(--netdisk-line-space); } .netdisk-url{ padding: 2px 0px; } `, }, NetDiskView.$config.viewSizeConfig.mainViewSmallWindow ); const smallWindowPosition = NetDiskViewConfig.view["netdisk-ui-small-window-position"].value; const $pops = NetDiskView.$el.$linkView.$pops; if (smallWindowPosition) { let viewWidth = DOMUtils.width($pops, true); let viewHeight = DOMUtils.height($pops, true); let maxWindowLeft = DOMUtils.width(window); let maxWindowTop = DOMUtils.height(window); const { transformLeft, transformTop } = DOMUtils.getTransform($pops); /* 最大的left偏移*/ let maxLeft = maxWindowLeft - viewWidth + transformLeft; // 最大的top偏移 let maxTop = maxWindowTop - viewHeight + transformTop; /* 最小的left偏移*/ let minLeft = 0 + transformLeft; /* 最小的top偏移*/ let minTop = 0 + transformTop; if (smallWindowPosition.top > maxTop) { smallWindowPosition.top = maxTop; } else if (smallWindowPosition.top < minTop) { smallWindowPosition.top = minTop; } if (smallWindowPosition.left > maxLeft) { smallWindowPosition.left = maxLeft; } else if (smallWindowPosition.left < minLeft) { smallWindowPosition.left = minLeft; } $pops.style.transitionDuration = "0s"; $pops.style.left = smallWindowPosition["left"] + "px"; $pops.style.top = smallWindowPosition["top"] + "px"; setTimeout(() => { $pops.style.transitionDuration = "0s"; }, 300); } } else { // 大窗 NetDiskView.$el.$linkView = NetDiskPops.alert( { title: { text: "网盘", position: "center", }, content: { text: boxAllContainerHTML, html: true, }, btn: { ok: { enable: false, }, close: { callback() { closeView(); }, }, }, mask: { clickCallBack() { closeView(); }, }, class: "whitesevPop netdisk-link-view-window", style: /*css*/ ` ${indexCSS} .pops { max-height: 60vh; } @media screen and (max-width: 600px) { .pops { max-height: 50vh; } } `, }, NetDiskView.$config.viewSizeConfig.mainView ); } /** * 分页容器 */ const $paginationWrapper = NetDiskView.$el.$linkView.$shadowRoot.querySelector( ".netdisk-url-pagination-wrapper" )!; // 是否启用数据分页 const data = NetDiskLinkViewData.generateViewData(); const pageShowDataMaxCount = NetDiskGlobalData.smallWindow["netdisk-ui-link-view-data-paging-show-data-count"].value; this.$inst.dataPaging = new DataPaging({ data: data, pageShowDataMaxCount: pageShowDataMaxCount, pageMaxStep: this.$data.isSmallWindow ? 2 : 4, currentPage: 1, pageChangeCallBack: async (page) => { NetDiskCheckLinkValidity.clearAllDelayCheckLinkValidity(); const enableDataPaging = NetDiskGlobalData.smallWindow["netdisk-ui-link-view-data-paging-enable"].value; await this.dataPagingChangeCallback({ data: this.$inst.dataPaging.CONFIG.data, refreshView: true, page: enableDataPaging ? page : void 0, }); }, }); this.$inst.dataPaging.addCSS(NetDiskView.$el.$linkView.$shadowRoot); this.$inst.dataPaging.append($paginationWrapper); const $style = DOMUtils.createElement("style", { type: "text/css", textContent: /*css*/ ` .pops-content:has(.netdisk-url-pagination-wrapper){ display: flex; flex-direction: column; overflow: hidden; } .pops-content:has(.netdisk-url-pagination-wrapper) .netdisk-url-box-all{ flex: 1; overflow: auto; } .pops-content .netdisk-url-pagination-wrapper{ flex: 0; display: flex; justify-content: center; padding: 4px 0px; } .pops-content #data-paging-wrapper{ display: flex; align-items: center; } .pops-content #data-paging-wrapper a{ } // 小窗 .netdisk-link-view-small-window #data-paging-wrapper{ display: flex; flex-wrap: nowrap; align-items: center; } .netdisk-link-view-small-window .pops-content .netdisk-url-pagination-wrapper{ scale: 0.7; padding: 0px; } `, }); NetDiskView.$el.$linkView.$shadowRoot.appendChild($style); if (NetDiskGlobalData.smallWindow["netdisk-ui-link-view-data-paging-enable"].value) { // 留空 } else { // 不启用分页,隐藏组件 this.$inst.dataPaging.hide(); } this.refreshLinkView(); // 链接视图的z-index const netDiskLinkViewZIndex = NetDiskGlobalData.smallWindow["netdisk-link-view-z-index"].value; if (netDiskLinkViewZIndex > 0) { DOMUtils.css(NetDiskView.$el.$linkView.$pops, { "z-index": netDiskLinkViewZIndex, }); } }, /** * 刷新视图 */ refreshLinkView() { // 分页刷新 const currentPage = this.$inst.dataPaging.PAGE_CONFIG.currentPage(); this.$inst.dataPaging.CONFIG.pageChangeCallBack(currentPage); }, /** * 清空视图 */ clearLinkView() { DOMUtils.empty(this.$el.$urlBoxAll); this.$el.$urlBoxAll.scrollTo(0, 0); }, /** * 分页 页码更新回调 */ async dataPagingChangeCallback(config: { /** * 数据 */ data: LinkViewData[]; /** * 是否清空旧视图 * * + true:清空旧视图,然后添加 * + false:不清空旧视图,仅追加 */ refreshView: boolean; /** * 分页,传入数字则分页处理 */ page?: number; /** * 是否进行有效性校验 * @default true */ isCheckValid?: boolean; }) { const { refreshView, page, isCheckValid } = config; let { data } = config; if (!data.length) { log.warn("data is empty"); return; } if (refreshView) { // 清空旧视图 this.clearLinkView(); } let documentFragment = document.createDocumentFragment(); /** * 用于验证链接有效性的数据 */ const checkValidInfoList: NetDiskCheckLinkValidityInfoConfig[] = []; // 当前下标 let currentIndex = 0; // 最大数量 let maxCount = data.length; // 当前数量 let count = 0; if (typeof page === "number") { // 分页限制 const dataCount = NetDiskGlobalData.smallWindow["netdisk-ui-link-view-data-paging-show-data-count"].value; maxCount = dataCount; currentIndex = (page - 1) * dataCount; } while (true) { if (currentIndex > data.length - 1) { // 下标越界 break; } if (count >= maxCount) { // 超出分页限制数量 break; } const dataItem = data[currentIndex]; const { ruleKeyName, netDiskDictData: netDiskData, shareCode } = dataItem; const uiLink = NetDiskRegularExtractor.extractShowLink({ ruleKeyName: ruleKeyName, ruleIndex: netDiskData.ruleIndex!, shareCode: shareCode, accessCode: netDiskData.accessCode, matchText: netDiskData.matchText, showToast: false, }); if (!uiLink) { continue; } const boxViewInfo = this.createBoxItemInfo( NetDiskView.$inst.icon.getIcon(ruleKeyName), ruleKeyName, netDiskData["ruleIndex"]!, shareCode, netDiskData["accessCode"], uiLink ); checkValidInfoList.push({ $urlBox: boxViewInfo.$urlBox, ruleKeyName, ruleIndex: netDiskData.ruleIndex!, shareCode, accessCode: netDiskData.accessCode, }); documentFragment.appendChild(boxViewInfo.$urlBox); currentIndex++; count++; } // 添加元素 this.$el.$urlBoxAll.appendChild(documentFragment); // 验证链接有效性 if (isCheckValid ?? true) { NetDiskCheckLinkValidity.check(checkValidInfoList); } }, /** * 添加新的链接 * @param ruleKeyName 规则名称 * @param ruleIndex 规则的索引下标 * @param shareCode 分享码 * @param accessCode 访问码 * @param matchText 匹配到的文本 */ addBoxItemView( ruleKeyName: string, ruleIndex: number, shareCode: string, accessCode: AccessCodeType, matchText: string ) { NetDiskView.$inst.historyMatch.changeMatchedData(ruleKeyName, ruleIndex, shareCode, accessCode, matchText); if (!NetDiskView.$el.$linkView) { // 还未创建视图,那就不添加元素 return; } log.info(ruleKeyName, ruleIndex, shareCode, accessCode); let icon = NetDiskView.$inst.icon.getIcon(ruleKeyName); let uiLink = NetDiskRegularExtractor.extractShowLink({ ruleKeyName, ruleIndex, shareCode, accessCode, matchText, }); if (!uiLink) { // 不存在显示到页面中的链接 return; } this.$inst.dataPaging.changeConfig({ data: NetDiskLinkViewData.generateViewData(), }); // 重新渲染组件 this.$inst.dataPaging.refresh(this.$inst.dataPaging.CONFIG.data); // this.$inst.dataPaging.CONFIG.pageChangeCallBack(this.$inst.dataPaging.PAGE_CONFIG.currentPage()); if ( this.$inst.dataPaging.PAGE_CONFIG.currentPage() === this.$inst.dataPaging.PAGE_CONFIG.maxPage || !this.$data.dataPagingEnable ) { // 当前在最后一页 let boxViewInfo = this.createBoxItemInfo(icon, ruleKeyName, ruleIndex, shareCode, accessCode, uiLink); this.$el.$urlBoxAll.appendChild(boxViewInfo.$urlBox); NetDiskCheckLinkValidity.check({ $urlBox: boxViewInfo.$urlBox, ruleKeyName, ruleIndex, shareCode, accessCode, }); } }, /** * 修改已存在的view * @param ruleKeyName 规则名称 * @param ruleIndex 规则的索引下标 * @param shareCode 分享码 * @param accessCode 访问码 * @param matchText 匹配到的文本 */ changeBoxItemView( ruleKeyName: string, ruleIndex: number, shareCode: string, accessCode: AccessCodeType, matchText: string ) { NetDiskView.$inst.historyMatch.changeMatchedData(ruleKeyName, ruleIndex, shareCode, accessCode, matchText); if (!NetDiskView.$el.$linkView) { // 还未创建视图,那就不修改元素 return; } const uiLink = NetDiskRegularExtractor.extractShowLink({ ruleKeyName, ruleIndex, shareCode, accessCode, matchText, }); if (!uiLink) { // 不存在显示到页面中的链接 return; } const $url = NetDiskView.$el.$linkView.$pops.querySelector( `.netdisk-url a[data-sharecode='${shareCode}'][data-rule-index='${ruleIndex}']` ); if ($url) { log.info("修改网盘链接视图"); log.info($url); $url.setAttribute("data-accesscode", accessCode!); DOMUtils.html($url, uiLink); } }, /** * 创建在元素属性上的attribute的数据JSON */ createBoxAttrRuleInfo(data: LinkViewRuleData) { return { /** 网盘 */ "data-rule-key": data.ruleKeyName, /** 网盘索引 */ "data-rule-index": data.ruleIndex, /** 访问码 */ "data-sharecode": data.shareCode, /** 访问码 */ "data-accesscode": data.accessCode, }; }, /** * 解析创建在元素属性上的attribute的数据 * @param $el 元素 */ parseBoxAttrRuleInfo($el: HTMLElement) { let result: LinkViewItemInfo = { ruleKeyName: $el.getAttribute("data-rule-key")!, ruleIndex: parseInt($el.getAttribute("data-rule-index")!), shareCode: $el.getAttribute("data-sharecode")!, accessCode: $el.getAttribute("data-accesscode"), }; if (isNaN(result.ruleIndex)) { log.warn("元素上的 ruleIndex 的值是NaN,调整为默认值0", $el); result.ruleIndex = 0; } return result; }, /** * 创建在元素属性上的attribute的数据 * @param data 数据 * @param $el 需要处理的元素 */ handleBoxAttrRuleInfo(data: LinkViewRuleData, $el: HTMLElement | HTMLElement[]) { let ruleInfoJSON = this.createBoxAttrRuleInfo(data); for (const key in ruleInfoJSON) { const value = ruleInfoJSON[key as keyof typeof ruleInfoJSON] ?? ""; if (Array.isArray($el)) { $el.forEach(($ele) => { $ele.setAttribute(key, value.toString()); }); } else { $el.setAttribute(key, value.toString()); } } }, /** * 创建每一项的网盘元素信息 * @param ruleImgSrc 规则图标src * @param ruleKeyName 规则键名 * @param ruleIndex 规则的索引下标 * @param shareCode 分享码 * @param accessCode 访问码 * @param uiLinkText 显示出来的链接文本 */ createBoxItemInfo( ruleImgSrc: string, ruleKeyName: string, ruleIndex: number, shareCode: string, accessCode: AccessCodeType, uiLinkText: string ) { let $urlBox = DOMUtils.createElement("div", { className: "netdisk-url-box", innerHTML: /*html*/ `
`, }); const { $urlDiv, $icon, $iconImg, $checkValidStatus, $url, $link } = this.parseBoxItemInfo($urlBox); // 设置网盘图标(设置为背景图片) $iconImg.style.cssText = `background: url(${ruleImgSrc}) no-repeat;background-size: 100%;`; // 设置显示的网盘链接 DOMUtils.html($link, uiLinkText); // 将数据信息添加到元素attr上 this.handleBoxAttrRuleInfo( { ruleKeyName: ruleKeyName, ruleIndex: ruleIndex, shareCode: shareCode, accessCode: accessCode, }, [$iconImg, $link] ); // 触发规则的渲染函数 for (let i = 0; i < NetDisk.$rule.rule.length; i++) { const ruleConfig = NetDisk.$rule.rule[i]; if (ruleConfig.setting.key === ruleKeyName && typeof ruleConfig.afterRenderUrlBox === "function") { ruleConfig.afterRenderUrlBox({ $viewBox: $urlBox, $urlDiv, $url, $link, ruleKeyName, ruleIndex, shareCode, accessCode, }); } } return { $urlBox, $urlDiv, $icon, $iconImg, $checkValidStatus, $url, $link, }; }, /** * 解析元素上的各个元素 * @param $viewBox 元素 */ parseBoxItemInfo($viewBox: HTMLElement) { let $urlBox = $viewBox.matches(".netdisk-url-box") ? $viewBox : $viewBox.closest(".netdisk-url-box")!; let $urlDiv = $urlBox.querySelector(".netdisk-url-div")!; let $icon = $urlBox.querySelector(".netdisk-icon")!; let $iconImg = $urlBox.querySelector(".netdisk-icon-img")!; /** 校验有效性 */ let $checkValidStatus = $urlBox.querySelector(".netdisk-status")!; let $url = $urlBox.querySelector(".netdisk-url")!; let $link = $urlBox.querySelector(".netdisk-link")!; return { $urlBox, $urlDiv, $icon, $iconImg, $checkValidStatus, $url, $link, }; }, };