From 4d1dc60985b5b9623dd61a351babd8640f5a4259 Mon Sep 17 00:00:00 2001 From: enncy <877526278@qq.com> Date: Wed, 9 Nov 2022 01:17:13 +0800 Subject: [PATCH] =?UTF-8?q?feat(core):=20=E5=AE=8C=E6=88=90=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=E5=86=85=E5=AE=B9=E7=9A=84=E5=85=83=E7=B4=A0=E5=92=8C?= =?UTF-8?q?=E6=95=B0=E5=80=BC=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/interfaces/script.ts | 43 ++++++++++++++++++++------ packages/core/src/projects/cx.ts | 8 +++-- packages/core/src/projects/init.ts | 36 ++++++++++----------- packages/core/src/projects/zhs.ts | 9 ++++-- packages/core/src/utils/common.ts | 16 ++++++---- packages/core/src/utils/start.ts | 15 ++++++--- 6 files changed, 83 insertions(+), 44 deletions(-) diff --git a/packages/core/src/interfaces/script.ts b/packages/core/src/interfaces/script.ts index 6390b5ce..cba32026 100644 --- a/packages/core/src/interfaces/script.ts +++ b/packages/core/src/interfaces/script.ts @@ -1,4 +1,4 @@ -import { namespaceKey } from '../utils/common'; +import { getValue, namespaceKey, setValue } from '../utils/common'; import { Config } from './config'; export type ScriptConfigsProvider> = T | { (): T }; @@ -24,26 +24,27 @@ export class BaseScript { onbeforeunload?: (...args: any) => any; } -export interface ScriptEvent { - [name: string]: any; -} +export type ScriptConfigs = { + /** 脚本提示 */ + readonly notes?: { + defaultValue: string; + }; +} & Record; /** * 脚本 */ -export class Script = Record> extends BaseScript { +export class Script extends BaseScript { /** 名字 */ name: string; /** 匹配路径 */ url: (string | RegExp)[]; /** 唯一命名空间,用于避免 config 重名 */ namespace?: string; - /** 脚本提示 */ - notes: string[]; /** 后台脚本(不提供管理页面) */ hideInPanel?: boolean; /** 通过 configs 映射并经过解析后的配置对象 */ - cfg: Record = {} as any; + cfg: Record & { notes?: string } = {} as any; /** 未经处理的 configs 原对象 */ private _configs?: ScriptConfigsProvider; /** 存储已经处理过的 configs 对象,避免重复调用方法 */ @@ -69,22 +70,44 @@ export class Script = Record> e hideInPanel, onstart, onactive, - oncomplete + oncomplete, + onbeforeunload }: ScriptOptions & { onstart?: (this: Script, ...args: any) => any; onactive?: (this: Script, ...args: any) => any; oncomplete?: (this: Script, ...args: any) => any; + onbeforeunload?: (this: Script, ...args: any) => any; }) { super(); this.name = name; this.namespace = namespace; this.url = url; - this.notes = notes || []; this._configs = configs; this.hideInPanel = hideInPanel; this.onstart = onstart as any; this.onactive = onactive as any; this.oncomplete = oncomplete as any; + this.onbeforeunload = onbeforeunload as any; + + const _onstart = this.onstart; + this.onstart = (...args: any) => { + _onstart?.call(this, ...args); + const urls: string[] = Array.from(getValue('_urls_')); + const urlHasInRecord = urls.find((u) => u === location.href); + if (!urlHasInRecord) { + setValue('_urls_', urls.concat(location.href)); + } + }; + + const _onbeforeunload = this.onbeforeunload; + this.onbeforeunload = (...args: any) => { + _onbeforeunload?.call(this, ...args); + const urls: string[] = Array.from(getValue('_urls_')); + const urlIndex = urls.findIndex((u) => u === location.href); + if (urlIndex !== -1) { + setValue('_urls_', urls.splice(urlIndex, 1)); + } + }; } onConfigChange(key: keyof T, handler: (pre: any, curr: any, remote: boolean) => any) { diff --git a/packages/core/src/projects/cx.ts b/packages/core/src/projects/cx.ts index 11a3b7ac..80564338 100644 --- a/packages/core/src/projects/cx.ts +++ b/packages/core/src/projects/cx.ts @@ -11,13 +11,16 @@ export const CXProject: Project = { name: '章节提示', namespace: 'cx.chapter', url: [/\/mooc2-ans\/mycourse\/studentcourse/], - notes: ['请点击任意一个章节进入课程,5秒后自动进入。'], + configs: { + notes: { defaultValue: `请点击任意一个章节进入课程\n5秒后将自动进入。` } + }, oncomplete() { const list = $$el('.catalog_task .catalog_jindu'); if (list.length) { list[0].click(); } else { + this.cfg.notes = '全部任务已完成!'; $model('prompt', { content: '全部任务已完成!', onConfirm(val) { @@ -31,8 +34,9 @@ export const CXProject: Project = { name: '课程学习', namespace: 'cx.study', url: [/\/mycourse\/studentstudy/], - notes: ['测试', '111'], + configs: { + notes: { defaultValue: `测试aaaa` }, test: { defaultValue: 1, attrs: { type: 'number', title: '测试选项' }, diff --git a/packages/core/src/projects/init.ts b/packages/core/src/projects/init.ts index 0187eace..0b489538 100644 --- a/packages/core/src/projects/init.ts +++ b/packages/core/src/projects/init.ts @@ -6,7 +6,7 @@ import { Config } from '../interfaces/config'; import { cors } from '../interfaces/cors'; import { Project } from '../interfaces/project'; import { Script } from '../interfaces/script'; -import { getMatchedScripts, namespaceKey } from '../utils/common'; +import { getMatchedScripts, getValue, namespaceKey } from '../utils/common'; import { el, enableElementDraggable, tooltip } from '../utils/dom'; import { StartConfig } from '../utils/start'; import { humpToTarget } from '../utils/string'; @@ -19,7 +19,8 @@ export type ModelAttrs = Pick< title?: string; }; -const modelContainer = el('div', { className: 'model-container' }); +const panel = el('div'); +const root = panel.attachShadow({ mode: 'closed' }); const messageContainer = el('div', { className: 'message-container' }); const InitPanelScript = new Script({ @@ -46,7 +47,7 @@ const InitPanelScript = new Script({ } const visibleProjects = projects.filter((p) => p.scripts.find((s) => !s.hideInPanel)); /** 当前匹配到的脚本,并且面板不隐藏 */ - const matchedScripts = getMatchedScripts(projects).filter((s) => !s.hideInPanel); + const matchedScripts = getMatchedScripts(projects, [location.href]).filter((s) => !s.hideInPanel); const container = el('container-element'); @@ -85,7 +86,7 @@ const InitPanelScript = new Script({ }, (select) => { for (const project of visibleProjects) { - const scripts = getMatchedScripts([project]).filter((s) => !s.hideInPanel); + const scripts = getMatchedScripts([project], getValue('_urls_') || []).filter((s) => !s.hideInPanel); for (const script of scripts) { select.append( el('option', { @@ -153,7 +154,7 @@ const InitPanelScript = new Script({ for (const project of visibleProjects.sort((a, b) => a.scripts.some((s) => a.name + '-' + s.name === this.cfg.currentPanelName) ? -1 : 1 )) { - const scripts = getMatchedScripts([project]); + const scripts = getMatchedScripts([project], getValue('_urls_') || [location.href]); for (const script of scripts) { const scriptContainer = el('div', { className: 'script-panel' }); @@ -163,7 +164,13 @@ const InitPanelScript = new Script({ const configsContainer = el('div', { className: 'configs card', title: '脚本设置' }); const configsBody = el('div', { className: 'configs-body' }); - notesContainer.append(...createNotes(script)); + script.onConfigChange('notes', (pre, curr) => { + console.log('change', curr); + + notesContainer.replaceChildren(...createNotes(script)); + }); + + notesContainer.replaceChildren(...createNotes(script)); configsBody.append(...createConfigs(script.namespace, script.configs || {})); configsContainer.append(configsBody); @@ -267,18 +274,8 @@ const InitPanelScript = new Script({ /** 创建内容板块 */ const createNotes = (script: Script) => { const notes: HTMLDivElement[] = []; - script.notes = script.notes || []; - - /** 创建响应式对象,当 notes 改变时,页面元素内容同样改变 */ - script.notes = new Proxy(script.notes, { - set(target, key, value) { - const note: HTMLDivElement = Reflect.get(notes, key); - note.innerText = value; - return Reflect.set(target, key, value); - } - }); - for (const note of script.notes || []) { + for (const note of script.cfg.notes?.split('\n') || []) { notes.push(el('div', { textContent: note })); } return notes; @@ -308,8 +305,7 @@ const InitPanelScript = new Script({ /** 在顶级页面显示操作面板 */ if (matchedScripts.length !== 0 && self === top) { // 随机位置插入操作面板到页面 - const panel = el('div'); - panel.attachShadow({ mode: 'closed' }).append(modelContainer, container); + root.append(container); document.body.children[random(0, document.body.children.length - 1)].after(panel); render(); @@ -335,7 +331,7 @@ export function $model(type: ModelElement['type'], attrs: ModelAttrs) { if (self === top) { const { disableWrapperCloseable, onConfirm, onCancel, ..._attrs } = attrs; - modelContainer.append( + root.append( el('div', { className: 'model-wrapper' }, (wrapper) => { const model = el('model-element', { onConfirm(val) { diff --git a/packages/core/src/projects/zhs.ts b/packages/core/src/projects/zhs.ts index d2be1979..ce0d7d39 100644 --- a/packages/core/src/projects/zhs.ts +++ b/packages/core/src/projects/zhs.ts @@ -8,8 +8,13 @@ export const ZHSProject: Project = { new Script({ name: '课程学习', url: [/.*/], - notes: ['111'], - namespace: 'zhs.study' + + namespace: 'zhs.study', + configs: { + notes: { + defaultValue: `智慧树111` + } + } }) ] }; diff --git a/packages/core/src/utils/common.ts b/packages/core/src/utils/common.ts index ec1056ca..566b1425 100644 --- a/packages/core/src/utils/common.ts +++ b/packages/core/src/utils/common.ts @@ -10,8 +10,7 @@ import { Script } from '../interfaces/script'; */ export function getValue(key: string, defaultValue?: any) { // eslint-disable-next-line no-undef - const val = GM_getValue(key, defaultValue); - return typeof val === 'undefined' ? '' : val; + return GM_getValue(key, defaultValue); } export function deleteValue(key: string) { @@ -73,11 +72,11 @@ export function removeConfigChangeListener(listenerId: number) { * @param projects 程序列表 * @returns */ -export function getMatchedScripts(projects: Project[]) { +export function getMatchedScripts(projects: Project[], urls: string[]) { const scripts = []; for (const project of projects) { for (const script of project.scripts) { - if (script.url.some((u) => RegExp(u).test(document.location.href))) { + if (script.url.some((u) => urls.some((url) => RegExp(u).test(url)))) { scripts.push(script); } } @@ -100,7 +99,7 @@ export function namespaceKey(namespace: string | undefined, key: any) { * @param script * @returns */ -export function createConfigProxy = Record>(script: Script) { +export function createConfigProxy(script: Script) { const proxy = new Proxy(script.cfg, { set(target, propertyKey, value) { const key = namespaceKey(script.namespace, propertyKey); @@ -118,9 +117,14 @@ export function createConfigProxy = Record { - /** 为对象添加响应式特性,在设置值的时候同步到本地存储中 */ - script.cfg = createConfigProxy(script); + /** 为对象添加响应式特性,在设置值的时候同步到本地存储中 */ + cfg.projects = cfg.projects.map((p) => { + p.scripts = p.scripts.map((s) => { + s.cfg = createConfigProxy(s); + return s; + }); + return p; + }); + const scripts = getMatchedScripts(cfg.projects, [location.href]); + + scripts.forEach((script) => { /** 执行脚本 */ script.onstart?.(cfg);