Skip to content

Commit 2d5573f

Browse files
committed
✨ 排除指定网址执行 #144
1 parent e3fbaf5 commit 2d5573f

File tree

5 files changed

+164
-29
lines changed

5 files changed

+164
-29
lines changed

src/app/service/script/controller.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,8 @@ export default class ScriptController {
5050
importByUrl(url: string) {
5151
return this.dispatchEvent("importByUrl", url);
5252
}
53+
54+
exclude(id: number, exclude: string, remove: boolean) {
55+
return this.dispatchEvent("exclude", { id, exclude, remove });
56+
}
5357
}

src/app/service/script/event.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export type ScriptEvent =
1616
| "enable"
1717
| "disable"
1818
| "delete"
19+
| "exclude"
1920
| "checkUpdate"
2021
| "importByUrl";
2122

@@ -168,4 +169,54 @@ export default class ScriptEventListener {
168169
public importByUrlHandler(url: string) {
169170
return this.manager.openInstallPageByUrl(url);
170171
}
172+
173+
@ListenEventDecorator("exclude")
174+
public excludeHandler({
175+
id,
176+
exclude,
177+
remove,
178+
}: {
179+
id: number;
180+
exclude: string;
181+
remove: boolean;
182+
}) {
183+
const logger = this.logger.with({ scriptId: id });
184+
return new Promise((resolve, reject) => {
185+
this.dao
186+
.findById(id)
187+
.then((script) => {
188+
if (!script) {
189+
return reject(new Error("脚本不存在"));
190+
}
191+
script.selfMetadata = script.selfMetadata || {};
192+
const excludes = script.selfMetadata.exclude || [];
193+
if (remove) {
194+
for (let i = 0; i < excludes.length; i += 1) {
195+
if (excludes[i] === exclude) {
196+
excludes.splice(i, 1);
197+
}
198+
}
199+
} else {
200+
excludes.push(exclude);
201+
}
202+
script.selfMetadata.exclude = excludes;
203+
this.dao.save(script).then(
204+
() => {
205+
logger.info("script exclude success");
206+
ScriptManager.hook.trigger("upsert", script, "system");
207+
resolve({ id: script.id });
208+
},
209+
(e) => {
210+
logger.error("script exclude failed", Logger.E(e));
211+
reject(e);
212+
}
213+
);
214+
return resolve(1);
215+
})
216+
.catch((e) => {
217+
logger.error("exclude error", Logger.E(e));
218+
reject(e);
219+
});
220+
});
221+
}
171222
}

src/pages/components/ScriptMenuList/index.tsx

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
IconDelete,
1616
IconEdit,
1717
IconMenu,
18+
IconMinus,
1819
IconSettings,
1920
} from "@arco-design/web-react/icon";
2021
import IoC from "@App/app/ioc";
@@ -25,15 +26,34 @@ import RuntimeController from "@App/runtime/content/runtime";
2526

2627
const CollapseItem = Collapse.Item;
2728

29+
function isExclude(script: ScriptMenu, host: string) {
30+
if (!script.customExclude) {
31+
return false;
32+
}
33+
for (let i = 0; i < script.customExclude.length; i += 1) {
34+
if (script.customExclude[i] === `*://${host}*`) {
35+
return true;
36+
}
37+
}
38+
return false;
39+
}
40+
2841
// 用于popup页的脚本操作列表
2942
const ScriptMenuList: React.FC<{
3043
script: ScriptMenu[];
3144
isBackscript: boolean;
32-
}> = ({ script, isBackscript }) => {
45+
currentUrl: string;
46+
}> = ({ script, isBackscript, currentUrl }) => {
3347
const [list, setList] = useState([] as ScriptMenu[]);
3448
const message = IoC.instance(MessageInternal) as MessageInternal;
3549
const scriptCtrl = IoC.instance(ScriptController) as ScriptController;
3650
const runtimeCtrl = IoC.instance(RuntimeController) as RuntimeController;
51+
let url: URL;
52+
try {
53+
url = new URL(currentUrl);
54+
} catch (e) {
55+
// ignore error
56+
}
3757
useEffect(() => {
3858
setList(script);
3959
}, [script]);
@@ -116,11 +136,7 @@ const ScriptMenuList: React.FC<{
116136
overflow: "hidden",
117137
textOverflow: "ellipsis",
118138
whiteSpace: "nowrap",
119-
color:
120-
item.runStatus &&
121-
item.runStatus !== SCRIPT_RUN_STATUS_RUNNING
122-
? "rgb(var(--gray-5))"
123-
: "",
139+
color: item.runNum === 0 ? "rgb(var(--gray-5))" : "",
124140
}}
125141
>
126142
{item.name}
@@ -170,6 +186,28 @@ const ScriptMenuList: React.FC<{
170186
>
171187
编辑
172188
</Button>
189+
{url && (
190+
<Button
191+
className="text-left"
192+
status="warning"
193+
type="secondary"
194+
icon={<IconMinus />}
195+
onClick={() => {
196+
scriptCtrl
197+
.exclude(
198+
item.id,
199+
`*://${url.host}*`,
200+
isExclude(item, url.host)
201+
)
202+
.finally(() => {
203+
window.close();
204+
});
205+
}}
206+
>
207+
{isExclude(item, url.host) ? "恢复" : "排除"}
208+
{` ${url.host} 在上执行`}
209+
</Button>
210+
)}
173211
<Popconfirm
174212
title="确定要删除此脚本吗?"
175213
icon={<IconDelete />}

src/pages/popup/App.tsx

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ function App() {
4343
const [notice, setNotice] = useState("");
4444
const [isRead, setIsRead] = useState(true);
4545
const [version, setVersion] = useState(ExtVersion);
46+
const [currentUrl, setCurrentUrl] = useState("");
4647

4748
const message = IoC.instance(MessageInternal) as MessageInternal;
4849
useEffect(() => {
@@ -59,6 +60,7 @@ function App() {
5960
if (!tabs.length) {
6061
return;
6162
}
63+
setCurrentUrl(tabs[0].url || "");
6264
message
6365
.syncSend("queryPageScript", { url: tabs[0].url, tabId: tabs[0].id })
6466
.then(
@@ -70,6 +72,9 @@ function App() {
7072
const list = resp.scriptList;
7173
list.sort((a, b) => {
7274
if (a.enable === b.enable) {
75+
if (a.runNum !== b.runNum) {
76+
return b.runNum - a.runNum;
77+
}
7378
return b.updatetime - a.updatetime;
7479
}
7580
return a.enable ? -1 : 1;
@@ -114,22 +119,18 @@ function App() {
114119
style={{
115120
maxHeight: "none",
116121
}}
117-
onClickMenuItem={(key) => {
122+
onClickMenuItem={async (key) => {
118123
switch (key) {
119124
case "newScript":
120-
chrome.tabs.query({ active: true }, async (tab) => {
121-
if (tab.length) {
122-
await chrome.storage.local.set({
123-
activeTabUrl: {
124-
url: tab[0].url,
125-
},
126-
});
127-
window.open(
128-
"/src/options.html#/script/editor?target=initial",
129-
"_blank"
130-
);
131-
}
125+
await chrome.storage.local.set({
126+
activeTabUrl: {
127+
url: currentUrl,
128+
},
132129
});
130+
window.open(
131+
"/src/options.html#/script/editor?target=initial",
132+
"_blank"
133+
);
133134
break;
134135
default:
135136
window.open(key, "_blank");
@@ -189,7 +190,11 @@ function App() {
189190
style={{ padding: "0" }}
190191
contentStyle={{ padding: "0" }}
191192
>
192-
<ScriptMenuList script={scriptList} isBackscript={false} />
193+
<ScriptMenuList
194+
script={scriptList}
195+
isBackscript={false}
196+
currentUrl={currentUrl}
197+
/>
193198
</CollapseItem>
194199

195200
<CollapseItem
@@ -198,7 +203,11 @@ function App() {
198203
style={{ padding: "0" }}
199204
contentStyle={{ padding: "0" }}
200205
>
201-
<ScriptMenuList script={backScriptList} isBackscript />
206+
<ScriptMenuList
207+
script={backScriptList}
208+
isBackscript
209+
currentUrl={currentUrl}
210+
/>
202211
</CollapseItem>
203212
</Collapse>
204213
<div className="flex flex-row arco-card-header !h-6">

src/runtime/background/runtime.ts

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
SCRIPT_TYPE_NORMAL,
1111
ScriptDAO,
1212
ScriptRunResouce,
13+
SCRIPT_RUN_STATUS_RUNNING,
1314
} from "@App/app/repo/scripts";
1415
import ResourceManager from "@App/app/service/resource/manager";
1516
import ValueManager from "@App/app/service/value/manager";
@@ -48,6 +49,7 @@ export type ScriptMenu = {
4849
runStatus?: SCRIPT_RUN_STATUS;
4950
runNum: number;
5051
menus?: ScriptMenuItem[];
52+
customExclude?: string[];
5153
};
5254

5355
// 后台脚本将会将代码注入到沙盒中
@@ -69,6 +71,9 @@ export default class Runtime extends Manager {
6971

7072
include: UrlInclude<ScriptRunResouce> = new UrlInclude();
7173

74+
// 自定义排除
75+
customizeExclude: UrlMatch<ScriptRunResouce> = new UrlMatch();
76+
7277
static hook = new Hook<"runStatus">();
7378

7479
// 运行中和开启的后台脚本
@@ -258,7 +263,7 @@ export default class Runtime extends Manager {
258263
number,
259264
Map<number, { script: Script; runNum: number }>
260265
>();
261-
const addRunScript = (tabId: number, script: Script) => {
266+
const addRunScript = (tabId: number, script: Script, num: number = 1) => {
262267
let scripts = runScript.get(tabId);
263268
if (!scripts) {
264269
scripts = new Map();
@@ -270,7 +275,7 @@ export default class Runtime extends Manager {
270275
scripts.set(script.id, scriptNum);
271276
}
272277
if (script.status === SCRIPT_STATUS_ENABLE) {
273-
scriptNum.runNum += 1;
278+
scriptNum.runNum += num;
274279
}
275280
};
276281
chrome.tabs.onRemoved.addListener((tabId) => {
@@ -279,12 +284,19 @@ export default class Runtime extends Manager {
279284
// 给popup页面获取运行脚本,与菜单
280285
this.message.setHandler(
281286
"queryPageScript",
282-
async (action: string, { tabId }: any) => {
287+
async (action: string, { url, tabId }: any) => {
283288
const tabMap = scriptMenu.get(tabId);
284-
const matchScripts = runScript.get(tabId) || [];
285-
const allPromise: Promise<ScriptMenu>[] = Array.from(
286-
matchScripts,
287-
async ([, item]: [number, { script: Script; runNum: number }]) => {
289+
const run = runScript.get(tabId);
290+
let matchScripts = [];
291+
if (!run) {
292+
matchScripts = this.matchUrl(url).map((item) => {
293+
return { runNum: 0, script: item };
294+
});
295+
} else {
296+
matchScripts = Array.from(run.values());
297+
}
298+
const allPromise: Promise<ScriptMenu>[] = matchScripts.map(
299+
async (item) => {
288300
const menus: ScriptMenuItem[] = [];
289301
if (tabMap) {
290302
tabMap.get(item.script.id)?.forEach((scriptItem) => {
@@ -306,6 +318,8 @@ export default class Runtime extends Manager {
306318
updatetime: item.script.updatetime || item.script.createtime,
307319
hasUserConfig: !!item.script.config,
308320
runNum: item.runNum,
321+
customExclude:
322+
item.script.selfMetadata && item.script.selfMetadata.exclude,
309323
menus,
310324
};
311325
}
@@ -316,6 +330,7 @@ export default class Runtime extends Manager {
316330
updatetime: script.updatetime || script.createtime,
317331
hasUserConfig: !!script?.config,
318332
runNum: item.runNum,
333+
customExclude: script.selfMetadata && script.selfMetadata.exclude,
319334
menus,
320335
};
321336
}
@@ -338,14 +353,18 @@ export default class Runtime extends Manager {
338353
});
339354
});
340355
}
356+
341357
backScriptList.push({
342358
id: item.id,
343359
name: item.name,
344360
enable: item.status === SCRIPT_STATUS_ENABLE,
345361
updatetime: item.updatetime || item.createtime,
346362
runStatus: item.runStatus,
347363
hasUserConfig: !!item.config,
348-
runNum: item.status === SCRIPT_STATUS_ENABLE ? 1 : 0,
364+
runNum:
365+
item.runStatus && item.runStatus !== SCRIPT_RUN_STATUS_RUNNING
366+
? 1
367+
: 0,
349368
menus,
350369
});
351370
});
@@ -371,6 +390,11 @@ export default class Runtime extends Manager {
371390
// 清理之前的数据
372391
runScript.delete(sender.tabId);
373392
}
393+
const exclude = this.customizeExclude.match(sender.url);
394+
// 自定义排除的
395+
exclude.forEach((val) => {
396+
addRunScript(sender.tabId!, val, 0);
397+
});
374398
const filter: ScriptRunResouce[] = this.matchUrl(
375399
sender.url,
376400
(script) => {
@@ -579,6 +603,15 @@ export default class Runtime extends Manager {
579603
}
580604
});
581605
}
606+
if (script.selfMetadata && script.selfMetadata.exclude) {
607+
script.selfMetadata.exclude.forEach((url) => {
608+
try {
609+
this.customizeExclude.add(url, script);
610+
} catch (e) {
611+
logger.error("url加载错误", Logger.E(e));
612+
}
613+
});
614+
}
582615
return Promise.resolve(true);
583616
}
584617

0 commit comments

Comments
 (0)