mirror of
https://github.com/tidys/cc-inspector-chrome
synced 2025-10-10 19:25:25 +00:00
代码整理
This commit is contained in:
80
src/scripts/inject-view/ad.vue
Normal file
80
src/scripts/inject-view/ad.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<div v-show="ads.length" class="ad">
|
||||
<div class="body" @mouseenter="onMouseEnter" @mouseleave="onMouseLeave">
|
||||
<div class="list ccui-scrollbar">
|
||||
<Banner v-for="(item, index) in ads" :data="item" :key="index"></Banner>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import ccui from "@xuyanfeng/cc-ui";
|
||||
import { defineComponent, onMounted, onUnmounted, ref, toRaw } from "vue";
|
||||
import { GA_EventName } from "../../ga/type";
|
||||
import Banner from "./banner.vue";
|
||||
import { emitter, Msg } from "./const";
|
||||
import { AdItem, getAdData } from "./loader";
|
||||
import { ga } from "./util";
|
||||
const { CCButton } = ccui.components;
|
||||
export default defineComponent({
|
||||
name: "ad",
|
||||
components: { CCButton, Banner },
|
||||
setup(props, { emit }) {
|
||||
onMounted(async () => {
|
||||
const data = await getAdData();
|
||||
if (!data) {
|
||||
console.log(`get ad failed`);
|
||||
return;
|
||||
}
|
||||
if (!data.valid) {
|
||||
console.log(`set ad forbidden`);
|
||||
return;
|
||||
}
|
||||
if (!data.data.length) {
|
||||
console.log(`not find any ad`);
|
||||
return;
|
||||
}
|
||||
ads.value = data.data;
|
||||
console.log("get ads ", toRaw(ads.value));
|
||||
|
||||
ga(GA_EventName.ShowAd);
|
||||
});
|
||||
onUnmounted(() => {});
|
||||
function testBanner() {
|
||||
const data = new AdItem();
|
||||
data.name = "ad test 11111111111 11111111111 44444444444444 5555555555555 111111111111111111 2222222222222222 33333333333333 444444444444444";
|
||||
data.store = "http://www.baidu.com";
|
||||
emitter.emit(Msg.ChangeAd, data);
|
||||
}
|
||||
|
||||
let ads = ref<AdItem[]>([]);
|
||||
|
||||
return {
|
||||
ads,
|
||||
onMouseEnter() {},
|
||||
onMouseLeave() {},
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@color-bg: #8d8d8da6;
|
||||
@color-hover: #f9c04e;
|
||||
@color-active: #ffaa00;
|
||||
.ad {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.body {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
.list {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
390
src/scripts/inject-view/app.vue
Normal file
390
src/scripts/inject-view/app.vue
Normal file
@@ -0,0 +1,390 @@
|
||||
<template>
|
||||
<div class="ad" ref="rootEl" v-show="!picking" @contextmenu.prevent="onContextMenuRoot" @mouseleave="onMouseLeaveRoot" @mouseenter="onMouseEnterCocosLogo">
|
||||
<div class="title">
|
||||
<div class="btns" v-show="showBtns">
|
||||
<div v-for="(item, index) in listArray" :key="index" class="list" @click="item.click($event, item)" :title="item.txt" v-show="item.visible">
|
||||
<i class="iconfont icon" :class="item.icon" @contextmenu.prevent.stop="item.contextmenu"></i>
|
||||
</div>
|
||||
</div>
|
||||
<i class="iconfont icon_cocos cocos" @mousedown="onMouseDown" @click="onCocosLogoClick"></i>
|
||||
</div>
|
||||
<!-- <Memory></Memory> -->
|
||||
<CCDialog></CCDialog>
|
||||
<CCMenu></CCMenu>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import ccui from "@xuyanfeng/cc-ui";
|
||||
import { IUiMenuItem } from "@xuyanfeng/cc-ui/types/cc-menu/const";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { defineComponent, onMounted, ref, toRaw } from "vue";
|
||||
import { GA_EventName } from "../../ga/type";
|
||||
import { DocumentEvent } from "../const";
|
||||
import { inspectTarget } from "../inject/inspect-list";
|
||||
import Ad from "./ad.vue";
|
||||
import Banner from "./banner.vue";
|
||||
import Memory from "./memory.vue";
|
||||
import { appStore } from "./store";
|
||||
import { ga } from "./util";
|
||||
declare const cc: any;
|
||||
const { CCDialog, CCMenu } = ccui.components;
|
||||
interface ListItem {
|
||||
icon: string;
|
||||
txt: string;
|
||||
visible: boolean;
|
||||
/**
|
||||
* 点击回调
|
||||
*/
|
||||
click: (event: MouseEvent, item: ListItem) => void;
|
||||
contextmenu: (event: MouseEvent) => void;
|
||||
}
|
||||
export default defineComponent({
|
||||
name: "ad",
|
||||
components: { CCDialog, Banner, Memory, CCMenu },
|
||||
setup() {
|
||||
function randomSupport(): { icon: string; title: string } {
|
||||
const arr = [
|
||||
{ icon: "icon_shop_cart", title: "冲冲冲" },
|
||||
{ icon: "icon_good", title: "赞一个" },
|
||||
{ icon: "icon_coffe", title: "请我喝杯咖啡" },
|
||||
];
|
||||
const idx = Math.floor(Math.random() * arr.length);
|
||||
return arr[idx];
|
||||
}
|
||||
|
||||
const store = appStore();
|
||||
store.init();
|
||||
const rnd = randomSupport();
|
||||
const { config } = storeToRefs(appStore());
|
||||
const listArray = ref<ListItem[]>([
|
||||
{
|
||||
icon: `${rnd.icon} ani_shop_cart`,
|
||||
txt: rnd.title,
|
||||
contextmenu: () => {},
|
||||
visible: true,
|
||||
click: () => {
|
||||
ccui.dialog.showDialog({
|
||||
title: "Recommended Plugins",
|
||||
comp: Ad,
|
||||
width: 310,
|
||||
height: 500,
|
||||
closeCB: () => {
|
||||
ga(GA_EventName.CloseAd);
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "icon_do_play",
|
||||
click: (event: MouseEvent, item: ListItem) => {
|
||||
ga(GA_EventName.GamePlayer);
|
||||
if (typeof cc !== "undefined") {
|
||||
cc.game.resume();
|
||||
}
|
||||
},
|
||||
visible: true,
|
||||
txt: "game play",
|
||||
contextmenu: () => {},
|
||||
},
|
||||
{
|
||||
icon: "icon_do_pause",
|
||||
visible: true,
|
||||
txt: "game pause",
|
||||
click: () => {
|
||||
ga(GA_EventName.GamePause);
|
||||
if (typeof cc !== "undefined") {
|
||||
cc.game.pause();
|
||||
}
|
||||
},
|
||||
contextmenu: () => {},
|
||||
},
|
||||
{
|
||||
icon: "icon_do_step",
|
||||
visible: true,
|
||||
txt: "game step",
|
||||
click: () => {
|
||||
ga(GA_EventName.GameStep);
|
||||
if (typeof cc !== "undefined") {
|
||||
cc.game.step();
|
||||
}
|
||||
},
|
||||
contextmenu: () => {},
|
||||
},
|
||||
{
|
||||
icon: "icon_target",
|
||||
txt: "Inspect Game",
|
||||
visible: true,
|
||||
click: () => {
|
||||
ga(GA_EventName.GameInspector);
|
||||
if (config.value.autoHide) {
|
||||
showBtns.value = false;
|
||||
}
|
||||
picking.value = true;
|
||||
if (typeof cc === "undefined") {
|
||||
testInspector();
|
||||
} else {
|
||||
const event = new CustomEvent(DocumentEvent.GameInspectorBegan);
|
||||
document.dispatchEvent(event);
|
||||
}
|
||||
},
|
||||
contextmenu: (event: MouseEvent) => {
|
||||
const arr = [
|
||||
{ name: "Inspect Label", type: typeof cc !== "undefined" ? cc.Label : "cc.Label" }, //
|
||||
{ name: "Inspect Sprite", type: typeof cc !== "undefined" ? cc.Sprite : "cc.Sprite" },
|
||||
{ name: "Inspect Button", type: typeof cc !== "undefined" ? cc.Button : "cc.Button" },
|
||||
{ name: "Inspect RichText", type: typeof cc !== "undefined" ? cc.RichText : "cc.RichText" },
|
||||
];
|
||||
const compMenu: IUiMenuItem[] = arr.map((item) => {
|
||||
return {
|
||||
name: item.name,
|
||||
enabled: inspectTarget.enabled,
|
||||
selected: inspectTarget.isContainInspectType(item.type),
|
||||
callback: (menu: IUiMenuItem) => {
|
||||
ga(GA_EventName.MouseMenu, menu.name);
|
||||
if (menu.selected) {
|
||||
inspectTarget.removeInspectType(item.type);
|
||||
} else {
|
||||
inspectTarget.addInspectType(item.type);
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
ccui.menu.showMenuByMouseEvent(event, [
|
||||
{
|
||||
name: "Clear",
|
||||
callback: (menu: IUiMenuItem) => {
|
||||
const event = new CustomEvent(DocumentEvent.InspectorClear);
|
||||
document.dispatchEvent(event);
|
||||
ga(GA_EventName.MouseMenu, menu.name);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Pick Top",
|
||||
selected: config.value.pickTop,
|
||||
callback: (menu: IUiMenuItem) => {
|
||||
config.value.pickTop = !config.value.pickTop;
|
||||
appStore().save();
|
||||
ga(GA_EventName.MouseMenu, menu.name);
|
||||
},
|
||||
},
|
||||
{ type: ccui.menu.MenuType.Separator },
|
||||
{
|
||||
name: "Filter Enabled",
|
||||
selected: inspectTarget.enabled,
|
||||
callback: (menu: IUiMenuItem) => {
|
||||
ga(GA_EventName.MouseMenu, menu.name);
|
||||
inspectTarget.enabled = !inspectTarget.enabled;
|
||||
},
|
||||
},
|
||||
...compMenu,
|
||||
]);
|
||||
},
|
||||
},
|
||||
]);
|
||||
document.addEventListener(DocumentEvent.GameInspectorEnd, () => {
|
||||
picking.value = false;
|
||||
});
|
||||
function testInspector() {
|
||||
const cursor = document.body.style.cursor;
|
||||
document.body.style.cursor = "zoom-in";
|
||||
function test(event: MouseEvent) {
|
||||
document.removeEventListener("mousedown", test, true);
|
||||
document.body.style.cursor = cursor;
|
||||
picking.value = false;
|
||||
}
|
||||
document.addEventListener("mousedown", test, true);
|
||||
}
|
||||
function recoverAssistantTop() {
|
||||
const top = toRaw(config.value.pos);
|
||||
updateAssistantTop(top);
|
||||
}
|
||||
|
||||
function updateAssistantTop(top: number) {
|
||||
const root = toRaw(rootEl.value) as HTMLDivElement;
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
if (top < 0) {
|
||||
top = 0;
|
||||
}
|
||||
const maxTop = document.body.clientHeight - root.clientHeight;
|
||||
if (top > maxTop) {
|
||||
top = maxTop;
|
||||
}
|
||||
root.style.top = `${top}px`;
|
||||
config.value.pos = top;
|
||||
appStore().save();
|
||||
}
|
||||
onMounted(async () => {
|
||||
recoverAssistantTop();
|
||||
window.addEventListener("resize", () => {
|
||||
const root = toRaw(rootEl.value) as HTMLDivElement;
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
updateAssistantTop(root.offsetTop);
|
||||
});
|
||||
return;
|
||||
});
|
||||
|
||||
const picking = ref(false);
|
||||
const rootEl = ref<HTMLDivElement>(null);
|
||||
const showBtns = ref(true);
|
||||
if (config.value.autoHide) {
|
||||
showBtns.value = false;
|
||||
}
|
||||
let autoHideTimer = null;
|
||||
let isDraging = false;
|
||||
return {
|
||||
showBtns,
|
||||
listArray,
|
||||
rootEl,
|
||||
picking,
|
||||
onMouseEnterCocosLogo() {
|
||||
clearTimeout(autoHideTimer);
|
||||
showBtns.value = true;
|
||||
},
|
||||
onCocosLogoClick() {
|
||||
showBtns.value = !showBtns.value;
|
||||
},
|
||||
onContextMenuRoot(event: MouseEvent) {
|
||||
const arr: IUiMenuItem[] = [
|
||||
{
|
||||
name: "auto hide",
|
||||
selected: config.value.autoHide,
|
||||
callback: (item) => {
|
||||
config.value.autoHide = !config.value.autoHide;
|
||||
appStore().save();
|
||||
ga(GA_EventName.MouseMenu, item.name);
|
||||
if (!config.value.autoHide) {
|
||||
clearTimeout(autoHideTimer);
|
||||
showBtns.value = true;
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
ccui.menu.showMenuByMouseEvent(event, arr);
|
||||
},
|
||||
onMouseLeaveRoot(event: MouseEvent) {
|
||||
if (isDraging) {
|
||||
return;
|
||||
}
|
||||
if (!config.value.autoHide) {
|
||||
return;
|
||||
}
|
||||
autoHideTimer = setTimeout(() => {
|
||||
showBtns.value = false;
|
||||
}, 500);
|
||||
},
|
||||
onMouseDown(event: MouseEvent) {
|
||||
const root = toRaw(rootEl.value) as HTMLDivElement;
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
const startY = event.pageY;
|
||||
const startTop = root.offsetTop;
|
||||
function onMouseMove(e: MouseEvent) {
|
||||
isDraging = true;
|
||||
const dy = e.pageY - startY;
|
||||
const top = startTop + dy;
|
||||
updateAssistantTop(top);
|
||||
}
|
||||
|
||||
function onMouseUp(e: MouseEvent) {
|
||||
isDraging = false;
|
||||
document.removeEventListener("mousemove", onMouseMove, true);
|
||||
document.removeEventListener("mouseup", onMouseUp, true);
|
||||
}
|
||||
document.addEventListener("mousemove", onMouseMove, true);
|
||||
document.addEventListener("mouseup", onMouseUp, true);
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
@x: 1px;
|
||||
@r: 8deg;
|
||||
@keyframes color-change {
|
||||
0% {
|
||||
color: #f00;
|
||||
transform: rotate(0) translateX(0px);
|
||||
}
|
||||
20% {
|
||||
transform: rotate(-@r) translateX(-@x);
|
||||
}
|
||||
40% {
|
||||
transform: rotate(@r) translateX(@x);
|
||||
}
|
||||
50% {
|
||||
color: #0f0;
|
||||
}
|
||||
60% {
|
||||
transform: rotate(-@r) translateX(-@x);
|
||||
}
|
||||
80% {
|
||||
transform: rotate(@r) translateX(@x);
|
||||
}
|
||||
100% {
|
||||
color: #f00;
|
||||
transform: rotate(0) translateX(0px);
|
||||
}
|
||||
}
|
||||
.ad {
|
||||
position: fixed;
|
||||
box-shadow: 0px 0px 6px 1px rgb(255, 255, 255);
|
||||
//z-index: 99999;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: white;
|
||||
border-top-left-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
// overflow: hidden;
|
||||
.title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
user-select: none;
|
||||
background-color: rgb(0, 0, 0);
|
||||
border: 1px solid black;
|
||||
color: white;
|
||||
padding: 2px 4px;
|
||||
border-top-left-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
|
||||
.btns {
|
||||
color: white;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
.list {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
color: white;
|
||||
user-select: none;
|
||||
&:hover {
|
||||
color: rgb(101, 163, 249);
|
||||
}
|
||||
&:active {
|
||||
color: rgb(255, 187, 0);
|
||||
}
|
||||
.icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
.ani_shop_cart {
|
||||
animation: color-change 2s infinite;
|
||||
}
|
||||
}
|
||||
}
|
||||
.cocos {
|
||||
cursor: move;
|
||||
font-size: 20px;
|
||||
color: rgb(85, 192, 224);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
122
src/scripts/inject-view/banner.vue
Normal file
122
src/scripts/inject-view/banner.vue
Normal file
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<div v-if="data" class="banner" :class="ani" @click="onClick" :title="data.tip" :style="getStyle()">
|
||||
<div class="text">
|
||||
<span v-if="data.name">
|
||||
{{ data.name }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, onUnmounted, PropType, ref, toRaw } from "vue";
|
||||
import { GA_EventName } from "../../ga/type";
|
||||
import { emitter, Msg } from "./const";
|
||||
import { AdItem } from "./loader";
|
||||
import { ga } from "./util";
|
||||
export default defineComponent({
|
||||
name: "banner",
|
||||
props: {
|
||||
data: {
|
||||
type: Object as PropType<AdItem>,
|
||||
default: () => new AdItem(),
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
function chageAd(v: AdItem) {
|
||||
console.log("show ad: ", JSON.stringify(v));
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
emitter.on(Msg.ChangeAd, chageAd);
|
||||
});
|
||||
onUnmounted(() => {
|
||||
emitter.off(Msg.ChangeAd, chageAd);
|
||||
});
|
||||
const ani = ref("");
|
||||
return {
|
||||
ani,
|
||||
getStyle() {
|
||||
const img = props.data.img;
|
||||
if (img) {
|
||||
return `background-image: url(${img})`;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
},
|
||||
onClick() {
|
||||
const url = toRaw(props.data.store);
|
||||
if (url) {
|
||||
window.open(url);
|
||||
ga(GA_EventName.ClickPluginLink, url);
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
@keyframes flip-out {
|
||||
0% {
|
||||
transform: rotateX(0);
|
||||
}
|
||||
100% {
|
||||
transform: rotateX(90deg);
|
||||
}
|
||||
}
|
||||
@keyframes flip-in {
|
||||
0% {
|
||||
transform: rotateX(90deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotateX(0);
|
||||
}
|
||||
}
|
||||
.banner-out {
|
||||
animation: flip-out 0.4s cubic-bezier(0.455, 0.03, 0.515, 0.955) both;
|
||||
}
|
||||
.banner-in {
|
||||
animation: flip-in 0.4s cubic-bezier(0.455, 0.03, 0.515, 0.955) both;
|
||||
}
|
||||
.banner {
|
||||
border: 2px solid #d2d2d2;
|
||||
background-color: #ffffff;
|
||||
background-position: center center;
|
||||
background-size: cover;
|
||||
overflow: hidden;
|
||||
min-width: 300px;
|
||||
max-width: 300px;
|
||||
min-height: 50px;
|
||||
max-height: 50px;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
text-align: center;
|
||||
align-items: flex-end;
|
||||
|
||||
&:hover {
|
||||
border: 2px solid #949494;
|
||||
|
||||
background-color: #d1d1d1;
|
||||
}
|
||||
.text {
|
||||
user-select: none;
|
||||
flex: 1;
|
||||
padding-bottom: 2px;
|
||||
font-size: 13px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
span {
|
||||
color: #000000c4;
|
||||
background-color: #afafaf6b;
|
||||
padding: 1px 4px;
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
5
src/scripts/inject-view/const.ts
Normal file
5
src/scripts/inject-view/const.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { TinyEmitter } from "tiny-emitter";
|
||||
export const Msg = {
|
||||
ChangeAd: "ChangeAd",
|
||||
};
|
||||
export const emitter = new TinyEmitter();
|
157
src/scripts/inject-view/github.ts
Normal file
157
src/scripts/inject-view/github.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
export interface MirrorInfo {
|
||||
/**
|
||||
* 请求的url
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* 上次请求成功的时间
|
||||
*/
|
||||
time: number;
|
||||
}
|
||||
class Config {
|
||||
private key = "cc-inspector-ad-config";
|
||||
private data: MirrorInfo[] = [];
|
||||
constructor() {
|
||||
const cfg = localStorage.getItem(this.key);
|
||||
if (cfg) {
|
||||
try {
|
||||
const ret = JSON.parse(cfg) as MirrorInfo[];
|
||||
if (ret) {
|
||||
ret.forEach((el) => {
|
||||
this.data.push({ name: el.name, time: el.time });
|
||||
});
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
save(name: string, time: number) {
|
||||
const ret = this.data.find((el) => el.name === name);
|
||||
if (ret) {
|
||||
ret.time = time;
|
||||
} else {
|
||||
this.data.push({ name: name, time: time } as MirrorInfo);
|
||||
}
|
||||
localStorage.setItem(this.key, JSON.stringify(this.data));
|
||||
}
|
||||
getTime(url: string) {
|
||||
const ret = this.data.find((el) => el.name === url);
|
||||
if (ret) {
|
||||
return ret.time;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
export class GithubMirror {
|
||||
owner: string = "tidys";
|
||||
repo: string = "cc-inspector-ad";
|
||||
branch: string = "main";
|
||||
/**
|
||||
* 上次请求成功的时间
|
||||
*/
|
||||
time: number = 0;
|
||||
/**
|
||||
* 镜像的名字
|
||||
*/
|
||||
name: string = "";
|
||||
private calcUrl: Function;
|
||||
constructor(name: string, cb) {
|
||||
this.name = name;
|
||||
this.time = cfg.getTime(name);
|
||||
this.calcUrl = cb;
|
||||
}
|
||||
public getUrl(file: string) {
|
||||
if (!file) {
|
||||
return "";
|
||||
}
|
||||
if (this.calcUrl) {
|
||||
return this.calcUrl(this.owner, this.repo, this.branch, file);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
public async getData(file: string) {
|
||||
const url = this.getUrl(file);
|
||||
if (url) {
|
||||
const data = await this.reqFecth(url);
|
||||
return data;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private reqFecth(url: string): Promise<Object | null> {
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log(`req ad: ${url}`);
|
||||
fetch(url)
|
||||
.then((res) => {
|
||||
return res.json();
|
||||
})
|
||||
.then((data) => {
|
||||
resolve(data);
|
||||
})
|
||||
.catch((e) => {
|
||||
resolve(null);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
const cfg = new Config();
|
||||
export class GithubMirrorMgr {
|
||||
mirrors: GithubMirror[] = [];
|
||||
constructor() {
|
||||
// 使用国内gitub镜像来达到下载远程配置文件的目的
|
||||
this.mirrors.push(
|
||||
new GithubMirror("github", (owner: string, repo: string, branch: string, file: string) => {
|
||||
return `https://raw.githubusercontent.com/${owner}/${repo}/refs/heads/${branch}/${file}`;
|
||||
})
|
||||
);
|
||||
this.mirrors.push(
|
||||
new GithubMirror("bgithub", (owner: string, repo: string, branch: string, file: string) => {
|
||||
return `https://raw.bgithub.xyz/${owner}/${repo}/refs/heads/${branch}/${file}`;
|
||||
})
|
||||
);
|
||||
this.mirrors.push(
|
||||
new GithubMirror("kkgithub", (owner: string, repo: string, branch: string, file: string) => {
|
||||
return `https://raw.kkgithub.com/${owner}/${repo}/refs/heads/${branch}/${file}`;
|
||||
})
|
||||
);
|
||||
|
||||
this.mirrors.push(
|
||||
new GithubMirror("xiaohei", (owner: string, repo: string, branch: string, file: string) => {
|
||||
return `https://raw-githubusercontent.xiaohei.me/${owner}/${repo}/refs/heads/${branch}/${file}`;
|
||||
})
|
||||
);
|
||||
|
||||
this.mirrors.push(
|
||||
new GithubMirror("gh-proxy", (owner: string, repo: string, branch: string, file: string) => {
|
||||
return `https://gh-proxy.com/raw.githubusercontent.com/${owner}/${repo}/refs/heads/${branch}/${file}`;
|
||||
})
|
||||
);
|
||||
this.mirrors.push(
|
||||
new GithubMirror("ghproxy", (owner: string, repo: string, branch: string, file: string) => {
|
||||
return `https://ghproxy.net/https://raw.githubusercontent.com/${owner}/${repo}/refs/heads/${branch}/${file}`;
|
||||
})
|
||||
);
|
||||
}
|
||||
async getData(file: string): Promise<Object | null> {
|
||||
this.mirrors.sort((a, b) => b.time - a.time);
|
||||
for (let i = 0; i < this.mirrors.length; i++) {
|
||||
const mirror = this.mirrors[i];
|
||||
const data = await mirror.getData(file);
|
||||
if (data) {
|
||||
const time = new Date().getTime();
|
||||
mirror.time = time;
|
||||
cfg.save(mirror.name, time);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
getFileUrl(file: string): string {
|
||||
if (!file) {
|
||||
return "";
|
||||
}
|
||||
this.mirrors.sort((a, b) => b.time - a.time);
|
||||
const url = this.mirrors[0].getUrl(file);
|
||||
return url;
|
||||
}
|
||||
}
|
||||
export const githubMirrorMgr = new GithubMirrorMgr();
|
116
src/scripts/inject-view/loader.ts
Normal file
116
src/scripts/inject-view/loader.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import CCPlugin from "../../../cc-plugin.config";
|
||||
import { githubMirrorMgr } from "./github";
|
||||
export class AdItem {
|
||||
/**
|
||||
* 广告的名字
|
||||
*/
|
||||
name: string = "";
|
||||
/**
|
||||
* 鼠标悬浮提示
|
||||
*/
|
||||
tip: string = "";
|
||||
/**
|
||||
* 插件的试用地址
|
||||
*/
|
||||
try: string = "";
|
||||
/**
|
||||
* 广告的store购买链接
|
||||
*/
|
||||
store: string = "";
|
||||
/**
|
||||
* 广告的展示时间,单位s
|
||||
*/
|
||||
duration: number = 0;
|
||||
/**
|
||||
* 广告的有效性
|
||||
*/
|
||||
valid: boolean = true;
|
||||
/**
|
||||
* 背景图
|
||||
*/
|
||||
img: string = "";
|
||||
parse(data: AdItem) {
|
||||
this.name = data.name;
|
||||
this.store = data.store || "";
|
||||
this.parseStore();
|
||||
|
||||
this.try = data.try || "";
|
||||
this.tip = data.tip || "";
|
||||
this.duration = data.duration || 0;
|
||||
this.valid = !!data.valid;
|
||||
const img = data.img || "";
|
||||
this.img = githubMirrorMgr.getFileUrl(img);
|
||||
return this;
|
||||
}
|
||||
parseStore() {
|
||||
const flag = "${git}";
|
||||
if (this.store.startsWith(flag)) {
|
||||
const file = this.store.split(flag)[1];
|
||||
this.store = githubMirrorMgr.getFileUrl(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
export class AdData {
|
||||
desc: string = "";
|
||||
/**
|
||||
* 是否启用广告
|
||||
*/
|
||||
valid: boolean = false;
|
||||
/**
|
||||
* 多少分钟不再展示,单位分钟,默认10分钟
|
||||
*/
|
||||
showDuration: number = 10;
|
||||
/**
|
||||
* 底部广告多少秒滚动一次
|
||||
*/
|
||||
scrollDuration: number = 3;
|
||||
/**
|
||||
* 将位置随机打乱,保证用户每次看到的插件数量不一样,提高转换率
|
||||
*/
|
||||
randomIndex: boolean = false;
|
||||
/**
|
||||
* 展示的广告数量,-1为所有
|
||||
*/
|
||||
showCount: number = -1;
|
||||
|
||||
data: Array<AdItem> = [];
|
||||
parse(data: AdData) {
|
||||
this.desc = data.desc;
|
||||
this.valid = !!data.valid;
|
||||
this.showDuration = data.showDuration || 10;
|
||||
this.scrollDuration = data.scrollDuration || 3;
|
||||
this.randomIndex = !!data.randomIndex;
|
||||
this.showCount = data.showCount || -1;
|
||||
|
||||
if (data.data) {
|
||||
if (this.randomIndex) {
|
||||
data.data.sort(() => Math.random() - 0.5);
|
||||
}
|
||||
data.data.forEach((el) => {
|
||||
if (this.showCount !== -1 && this.data.length >= this.showCount) {
|
||||
return;
|
||||
}
|
||||
const item = new AdItem().parse(el);
|
||||
if (!item.duration) {
|
||||
console.warn(`add failed, ad.duration is ${item.duration}, ${JSON.stringify(item)}`);
|
||||
return;
|
||||
}
|
||||
if (!item.valid) {
|
||||
console.warn(`add failed, ad is invalid, ${JSON.stringify(item)}`);
|
||||
return;
|
||||
}
|
||||
this.data.push(item);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAdData(): Promise<AdData | null> {
|
||||
const data = await githubMirrorMgr.getData(`ad-${CCPlugin.manifest.version}.json`);
|
||||
if (data) {
|
||||
const ad = new AdData();
|
||||
ad.parse(data as AdData);
|
||||
return ad;
|
||||
}
|
||||
return null;
|
||||
}
|
58
src/scripts/inject-view/memory-draw.ts
Normal file
58
src/scripts/inject-view/memory-draw.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
export class Memory {
|
||||
public time: number = 0;
|
||||
public jsHeapSizeLimit: number = 0;
|
||||
public totalJSHeapSize: number = 0;
|
||||
public usedJSHeapSize: number = 0;
|
||||
constructor() {
|
||||
this.update();
|
||||
}
|
||||
update() {
|
||||
const memory = window.performance["memory"];
|
||||
if (memory) {
|
||||
this.time = Date.now();
|
||||
this.jsHeapSizeLimit = memory.jsHeapSizeLimit;
|
||||
this.totalJSHeapSize = memory.totalJSHeapSize;
|
||||
this.usedJSHeapSize = memory.usedJSHeapSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class MemoryDraw {
|
||||
private canvas: HTMLCanvasElement;
|
||||
private ctx: CanvasRenderingContext2D;
|
||||
private width: number = 0;
|
||||
private height: number = 0;
|
||||
private memoryHistory: Memory[] = [];
|
||||
init(canvas: HTMLCanvasElement) {
|
||||
this.canvas = canvas;
|
||||
this.ctx = canvas.getContext("2d");
|
||||
this.width = canvas.width;
|
||||
this.height = canvas.height;
|
||||
this.clearBg();
|
||||
this.initTimer();
|
||||
}
|
||||
private initTimer() {
|
||||
setInterval(() => {
|
||||
const m = new Memory();
|
||||
this.memoryHistory.push(m);
|
||||
this.update();
|
||||
}, 300);
|
||||
}
|
||||
private update() {
|
||||
this.clearBg();
|
||||
for (let i = 0; i < this.memoryHistory.length; i++) {
|
||||
const m = this.memoryHistory[i];
|
||||
this.drawLine(i);
|
||||
}
|
||||
}
|
||||
private clearBg() {
|
||||
this.ctx.clearRect(0, 0, this.width, this.height);
|
||||
this.ctx.fillStyle = "rgba(0, 0, 0, 0.5)";
|
||||
this.ctx.fillRect(0, 0, this.width, this.height);
|
||||
}
|
||||
private lineWidth = 10;
|
||||
private drawLine(i: number) {
|
||||
this.ctx.fillStyle = "rgba(255, 37, 37, 0.5)";
|
||||
this.ctx.fillRect(0, 0, i * this.lineWidth, this.height / 2);
|
||||
}
|
||||
}
|
62
src/scripts/inject-view/memory.vue
Normal file
62
src/scripts/inject-view/memory.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div class="memory">
|
||||
<canvas class="canvas" ref="elCanvas"></canvas>
|
||||
<div class="info">
|
||||
<div class="txt">{{ transformSize(memory.usedJSHeapSize) }}</div>
|
||||
<div class="txt">{{ transformSize(memory.totalJSHeapSize) }}</div>
|
||||
<div class="txt">{{ transformSize(memory.jsHeapSizeLimit) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import ccui from "@xuyanfeng/cc-ui";
|
||||
import { defineComponent, onMounted, ref, toRaw } from "vue";
|
||||
import { Memory, MemoryDraw } from "./memory-draw";
|
||||
import { transformSize } from "./util";
|
||||
const { CCButton } = ccui.components;
|
||||
|
||||
export default defineComponent({
|
||||
name: "memory",
|
||||
components: { CCButton },
|
||||
setup(props, { emit }) {
|
||||
const memoryDraw = new MemoryDraw();
|
||||
const memory = ref<Memory>(new Memory());
|
||||
setInterval(() => {
|
||||
memory.value.update();
|
||||
}, 300);
|
||||
|
||||
onMounted(() => {
|
||||
const el = toRaw(elCanvas.value);
|
||||
if (el) {
|
||||
// memoryDraw.init(el as HTMLCanvasElement);
|
||||
}
|
||||
});
|
||||
const elCanvas = ref<HTMLCanvasElement>(null);
|
||||
return { memory, transformSize, elCanvas };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.memory {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.canvas {
|
||||
display: flex;
|
||||
height: 50px;
|
||||
}
|
||||
.info {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
.txt {
|
||||
user-select: none;
|
||||
margin: 0 3px;
|
||||
}
|
||||
:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
1
src/scripts/inject-view/res/close.svg
Normal file
1
src/scripts/inject-view/res/close.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1737262301480" class="icon" viewBox="0 0 1028 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2139" width="16.0625" height="16" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M597.54570376 512l219.97466684-219.97466683c24.44162965-24.44162965 24.44162965-61.10407412 0-85.54570377-24.44162965-24.44162965-61.10407412-24.44162965-85.54570377 0L512 426.45429624 292.02533317 206.4796294C267.58370352 182.03799974 230.92125905 182.03799974 206.4796294 206.4796294c-24.44162965 24.44162965-24.44162965 61.10407412 0 85.54570377L426.45429624 512l-219.97466684 219.97466683c-24.44162965 24.44162965-24.44162965 61.10407412 0 85.54570377 12.22081482 12.22081482 28.51523459 16.29441978 44.80965436 16.29441976s32.58883953-4.07360495 44.80965435-16.29441976L512 597.54570376l219.97466683 219.97466684c12.22081482 12.22081482 28.51523459 16.29441978 44.80965436 16.29441977s32.58883953-4.07360495 44.80965436-16.29441977c24.44162965-24.44162965 24.44162965-61.10407412 0-85.54570377L597.54570376 512z" fill="#cdcdcd" p-id="2140"></path></svg>
|
After Width: | Height: | Size: 1.2 KiB |
1
src/scripts/inject-view/res/left.svg
Normal file
1
src/scripts/inject-view/res/left.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1737262395969" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2346" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M704 868c-8.3 0-16.5-2.8-23.1-8.3l-384-320c-8.2-6.8-13-17-13-27.7s4.7-20.8 13-27.7l384-320c10.7-8.9 25.7-10.9 38.3-4.9C731.9 165.3 740 178 740 192v640c0 14-8.1 26.7-20.7 32.6-4.9 2.3-10.1 3.4-15.3 3.4z" fill="#dbdbdb" p-id="2347"></path></svg>
|
After Width: | Height: | Size: 574 B |
1
src/scripts/inject-view/res/right.svg
Normal file
1
src/scripts/inject-view/res/right.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1737262412557" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2507" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M304.7 864.6C292.1 858.7 284 846 284 832V192c0-14 8.1-26.7 20.7-32.6 12.6-5.9 27.6-4 38.3 4.9l384 320c8.2 6.8 13 17 13 27.7s-4.7 20.8-13 27.7l-384 320c-6.6 5.5-14.8 8.3-23.1 8.3-5.1 0-10.3-1.1-15.2-3.4z" fill="#dbdbdb" p-id="2508"></path></svg>
|
After Width: | Height: | Size: 575 B |
36
src/scripts/inject-view/store.ts
Normal file
36
src/scripts/inject-view/store.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { ref, toRaw } from "vue";
|
||||
import { defineStore } from "pinia";
|
||||
import profile from "cc-plugin/src/ccp/profile";
|
||||
import pluginConfig from "../../../cc-plugin.config";
|
||||
export class ConfigData {
|
||||
/**
|
||||
* 用户拖动的位置
|
||||
*/
|
||||
pos: number = 0;
|
||||
/**
|
||||
* 是否自动隐藏
|
||||
*/
|
||||
autoHide: boolean = true;
|
||||
/**
|
||||
* 是否只拾取顶部元素
|
||||
*/
|
||||
pickTop: boolean = true;
|
||||
}
|
||||
|
||||
export const appStore = defineStore("app", () => {
|
||||
const config = ref<ConfigData>(new ConfigData());
|
||||
return {
|
||||
config,
|
||||
init() {
|
||||
profile.init(new ConfigData(), pluginConfig);
|
||||
const data = profile.load(`${pluginConfig.manifest.name}-assistant.json`) as ConfigData;
|
||||
config.value.autoHide = data.autoHide;
|
||||
config.value.pos = data.pos;
|
||||
config.value.pickTop = data.pickTop;
|
||||
},
|
||||
save() {
|
||||
const cfg = toRaw(config.value);
|
||||
profile.save(cfg);
|
||||
},
|
||||
};
|
||||
});
|
29
src/scripts/inject-view/util.ts
Normal file
29
src/scripts/inject-view/util.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { GA_EventName } from "../../ga/type";
|
||||
import { DocumentEvent, GoogleAnalyticsData } from "../const";
|
||||
|
||||
export function ga(event: GA_EventName, params: string = "") {
|
||||
const detail = { event, params } as GoogleAnalyticsData;
|
||||
const e = new CustomEvent(DocumentEvent.GoogleAnalytics, { detail });
|
||||
document.dispatchEvent(e);
|
||||
}
|
||||
|
||||
export function transformSize(size: number) {
|
||||
if (!size) return "0B";
|
||||
size = parseInt(size.toString());
|
||||
if (size < 1024) {
|
||||
return size + "B";
|
||||
}
|
||||
size = size / 1024;
|
||||
if (size < 1024) {
|
||||
return size.toFixed(2) + "KB";
|
||||
}
|
||||
size = size / 1024;
|
||||
if (size < 1024) {
|
||||
return size.toFixed(2) + "MB";
|
||||
}
|
||||
size = size / 1024;
|
||||
if (size < 1024) {
|
||||
return size.toFixed(2) + "GB";
|
||||
}
|
||||
return size;
|
||||
}
|
19
src/scripts/inject-view/web-test.ts
Normal file
19
src/scripts/inject-view/web-test.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* 这个是web测试inject_view的入口,实际chrome插件不应该走这个界面
|
||||
*/
|
||||
import ccui from "@xuyanfeng/cc-ui";
|
||||
import "@xuyanfeng/cc-ui/dist/ccui.css";
|
||||
import "@xuyanfeng/cc-ui/iconfont/iconfont.css";
|
||||
import CCP from "cc-plugin/src/ccp/entry-render";
|
||||
import { createPinia } from "pinia";
|
||||
import { createApp } from "vue";
|
||||
import pluginConfig from "../../../cc-plugin.config";
|
||||
import App from "./app.vue";
|
||||
export default CCP.init(pluginConfig, {
|
||||
ready: function (rootElement: any, args: any) {
|
||||
const app = createApp(App);
|
||||
app.use(createPinia());
|
||||
app.use(ccui);
|
||||
app.mount(rootElement);
|
||||
},
|
||||
});
|
Reference in New Issue
Block a user