wuhu-torn-helper/src/vue/FloatMenu.vue

515 lines
15 KiB
Vue

<template>
<el-config-provider :size="'default'" :z-index="1000000">
<el-button-group class="wh-menu-button" style="z-index: 1000000;">
<el-button circle @click="expanded = !expanded">
<el-icon>
<MoonNight/>
</el-icon>
</el-button>
<el-button v-for="item in globVars.buttons" @click="item.func">{{ item.txt }}</el-button>
<el-button v-if="editableTabs.length > 0" circle @click="showDrawer">
<el-badge :value="editableTabs.length" type="primary">
<el-icon>
<CopyDocument/>
</el-icon>
</el-badge>
</el-button>
</el-button-group>
<el-dialog v-model="drawer" :fullscreen="isMobilePhone" :lock-scroll="true" width="65%">
<el-tabs v-model="editableTabsValue" closable style="margin-top: -1em" type="border-card" @tab-remove="removeTab">
<el-tab-pane v-for="item in editableTabs" :key="item.name" :label="item.title" :name="item.name">
<component :is="item.content"/>
</el-tab-pane>
</el-tabs>
</el-dialog>
<el-drawer v-model="expanded" :show-close="false" :size="isMobilePhone ? '85%' : '30%'" class="whDrawer"
:with-header="false" direction="rtl">
<el-card :body-style="{ 'padding': '0' }" class="innerCard" style="margin-bottom: 0.5em">
<template #header>
<div>快捷动作</div>
</template>
<el-menu :unique-opened="true">
<el-sub-menu index="1">
<template #title>
<el-icon>✈️</el-icon>
<span>起飞</span>
</template>
<el-sub-menu v-for="(item, i) in travelData" :index="'1-' + (i + 1)">
<template #title>{{ item.cName }}</template>
<el-menu-item :index="'1-' + (i + 1) + '1'" @click="travelConfirm(item.index, 0)">
❌ 普通飞机
</el-menu-item>
<el-menu-item :index="'1-' + (i + 1) + '2'" @click="travelConfirm(item.index, 1)">
✅ PI飞机
</el-menu-item>
<el-menu-item :index="'1-' + (i + 1) + '3'" @click="travelConfirm(item.index, 2)">
👍 股票飞机
</el-menu-item>
<el-menu-item :index="'1-' + (i + 1) + '4'" @click="travelConfirm(item.index, 3)">
🥵 商务飞机(机票或内衣店)
</el-menu-item>
</el-sub-menu>
</el-sub-menu>
<el-sub-menu index="2">
<template #title>
<el-icon>💪</el-icon>
<span>锻炼</span>
</template>
<el-menu-item @click="quickGymTrain.doTrain(BATTLE_STAT.STR)">力量 STR
</el-menu-item>
<el-menu-item @click="quickGymTrain.doTrain(BATTLE_STAT.DEF)">防御 DEF
</el-menu-item>
<el-menu-item @click="quickGymTrain.doTrain(BATTLE_STAT.SPD)">速度 SPD
</el-menu-item>
<el-menu-item @click="quickGymTrain.doTrain(BATTLE_STAT.DEX)">闪避 DEX
</el-menu-item>
</el-sub-menu>
<el-sub-menu index="3">
<template #title>
<el-icon>♻️</el-icon>
<span>REFILL</span>
</template>
<el-menu-item @click="handleRefil('refillEnergy')">能量E
</el-menu-item>
<el-menu-item @click="handleRefil('refillNerve')">犯罪N
</el-menu-item>
<el-menu-item @click="handleRefil('refillCasinoTokens')">赌场代币
</el-menu-item>
</el-sub-menu>
</el-menu>
</el-card>
<el-card :body-style="{ 'padding': '4px' }" class="innerCard">
<template #header>
<div>快捷功能</div>
</template>
<el-row style="display: flex;">
<el-col v-for="item in menuItemList" class="featureCol">
<el-card :body-class="'featureMenuBody'" class="featureMenu" shadow="never" @click="menuClick(item)">
<div>
<el-icon class="icon">{{ item.title.slice(0, 2) }}</el-icon>
</div>
<div class="title">
<span>{{ item.title.slice(2, item.title.length) }}</span>
</div>
</el-card>
</el-col>
</el-row>
</el-card>
<div style="margin-top: 1em; text-align: center">
<el-row>
<el-col :span="24">
<el-tooltip content="更新?" placement="bottom-start">
<el-button link @click="menuClick({ title: '关于助手', template: UpdateDate })">芜湖助手
<el-icon>
<Refresh/>
</el-icon>
</el-button>
</el-tooltip>
<el-tag size="small" type="info">{{
globVars.version.startsWith('$') ? 'dev' : 'v' + globVars.version
}}
</el-tag>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-text v-if="_globVars.loadTime" size="small">
{{ _globVars.loadTime }}ms
</el-text>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-text size="small">
2021-{{ new Date().getFullYear() }}
</el-text>
</el-col>
</el-row>
</div>
</el-drawer>
</el-config-provider>
</template>
<script lang="ts" setup>
import { CopyDocument, MoonNight, Refresh } from "@element-plus/icons-vue"
import { ElMessage, ElMessageBox } from "element-plus"
import { Component, inject, onMounted, ref, shallowRef, triggerRef } from 'vue'
import { LoggerKey } from "../ts/class/Logger"
import { QuickGymTrainKey } from "../ts/class/action/QuickGymTrain"
import { QuickFlyBtnHandlerKey } from "../ts/class/handler/QuickFlyBtnHandler"
import SettingsHandler from "../ts/class/handler/SettingsHandler"
import { BATTLE_STAT } from "../ts/class/utils/NetHighLvlWrapper"
import adHelper from "../ts/func/module/adHelper"
import safeKeeper from "../ts/func/module/safeKeeper"
import getSidebarData from "../ts/func/utils/getSidebarData"
import useItem from "../ts/func/utils/useItem"
import globVars from "../ts/globVars"
import AutoLoginForm from "./AutoLoginForm.vue"
import ChangeLogView from "./ChangeLogView.vue"
import CityUItems from "./CityUItems.vue"
import CompanyWithdraw from "./CompanyWithdraw.vue"
import EventsViewer from "./EventsViewer.vue"
import ForeignStock from "./ForeignStock.vue"
import InventoryView from "./InventoryView.vue"
import MarketHelper from "./MarketHelper.vue"
import MonitorMgrView from "./MonitorMgrView.vue"
import PTMarketView from "./PTMarketView.vue"
import QuickCrime from "./QuickCrime.vue"
import UpdateDate from "./UpdateScript.vue"
import VirusProgramming from "./VirusProgramming.vue"
import PropertyVault from "./PropertyVault.vue";
import BSEstView from "./BSEstView.vue";
const logger = inject(LoggerKey)
const quickGymTrain = inject(QuickGymTrainKey)
const quickFlyBtnHandler = inject(QuickFlyBtnHandlerKey)
type MenuItem = { title: string, template?: Component, handler?: Function }
const menuItemList: MenuItem[] = [
{
title: '💊 吃 XAN',
handler: () => {
useItem('206')
const sidebarData = getSidebarData()
const cd = ((sidebarData.statusIcons.icons.drug_cooldown?.timerExpiresAt - sidebarData.statusIcons.icons.drug_cooldown?.serverTimestamp) / 36 | 0) / 100
ElMessage({ message: '毒 CD: ' + cd + 'h', showClose: true })
},
},
{
title: '🚓 快速犯罪',
template: QuickCrime,
},
{
title: '📢 浏览通知',
template: EventsViewer,
},
{
title: '🚮 地图垃圾',
template: CityUItems,
},
{
title: '🛒 购物助手',
template: MarketHelper,
},
{
title: '🅿️ PT购买',
template: PTMarketView,
},
{
title: '💰 公司存钱',
template: CompanyWithdraw,
},
{
title: '💰 PI存钱',
template: PropertyVault,
},
{
title: '📦 物品',
template: InventoryView,
},
{
title: '🎯 BS估算',
template: BSEstView,
},
{
title: '🫵 关闭店铺(双击开启)',
handler: () => bazaarControl.method(),
},
{
title: '🌸 飞花库存',
template: ForeignStock,
},
{
title: '💻 PC',
template: VirusProgramming,
},
{
title: '🩼 配置自动登陆',
template: AutoLoginForm,
},
{
title: '🖥 监控',
template: MonitorMgrView,
},
{
title: '📜️ 传单助手',
handler: () => {
expanded.value = false
adHelper()
},
},
{
title: '🛡️ 守望者',
handler: () => {
expanded.value = false
safeKeeper()
},
},
{
title: '🌲 寻找木桩',
handler: () => window.location.replace('https://www.torn.com/item.php?temp=4#xunzhaomuzhuang'),
},
{
title: '🚀 更新历史',
template: ChangeLogView
},
{
title: '⚙️ 助手设置',
handler: () => {
expanded.value = false
SettingsHandler.clickFunc()
},
},
]
const drawer = ref(false)
const isMobilePhone = ref(false)
const documentHeight = ref(0)
const expanded = ref(false)
const _globVars = ref(globVars)
// tabs
const editableTabsValue = ref('')
const editableTabs = shallowRef([])
// refil
const handleRefil = (method: 'refillEnergy' | 'refillNerve' | 'refillCasinoTokens') => {
fetch(window.addRFC("https://www.torn.com/page.php?sid=pointsBuildingExchange"), {
"headers": {
"accept": "*/*",
"accept-language": "zh-CN,zh;q=0.9",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"x-requested-with": "XMLHttpRequest"
},
"referrer": "https://www.torn.com/page.php?sid=points",
"referrerPolicy": "strict-origin-when-cross-origin",
"body": (() => {
const data = new FormData();
data.append('key', method);
return data;
})(),
"method": "POST",
"mode": "cors",
"credentials": "include"
})
.then(res => res.json())
.catch(e => ElMessage.error('REFILL异常: ' + e.toString))
.then(res => {
ElMessage({
message: res.success ?
`<p>成功</p><p>剩余次数: ${ res["specialRefills"] } 剩余点数: ${ res['points'] }</p>` :
`REFILL异常: ${ res.error }`,
type: res.success ? 'success' : 'error',
dangerouslyUseHTMLString: true
})
throw new TypeError('REFILL异常: ' + res.error)
})
}
// fast travel
const travelData = [
{ cName: "🇲🇽 墨西哥", index: 0 },
{ cName: "🇰🇾 开曼", index: 1 },
{ cName: "🇨🇦 加拿大", index: 2 },
{ cName: "🌺 夏威夷", index: 3 },
{ cName: "🇬🇧 嘤国", index: 4 },
{ cName: "🇦🇷 阿根廷", index: 5 },
{ cName: "🇨🇭 瑞士 (解毒)", index: 6 },
{ cName: "🇯🇵 立本", index: 7 },
{ cName: "🇨🇳 祖国", index: 8 },
{ cName: "🇦🇪 阿联酋 (UAE)", index: 9 },
{ cName: "🇿🇦 南非", index: 10 },
]
const menuClick = (menuItem: MenuItem) => {
if (menuItem.handler) {
menuItem.handler();
} else if (menuItem.template) {
drawer.value = true;
addTab(menuItem);
}
}
const showDrawer = () => {
if (editableTabs.value.length < 1) {
logger.error('页面还没有打开任何功能标签');
throw new Error('页面还没有打开任何功能标签');
}
drawer.value = true;
};
const addTab = (menuItem: MenuItem) => {
for (let i = 0; i < editableTabs.value.length; i++) {
if (editableTabs.value[i].name === menuItem.title) {
editableTabsValue.value = menuItem.title;
// drawerTitle.value = menuItem.title;
return;
}
}
editableTabs.value.push({
title: menuItem.title,
name: menuItem.title,
content: menuItem.template!,
});
triggerRef(editableTabs);
editableTabsValue.value = menuItem.title;
};
const removeTab = (targetName: string) => {
const tabs = editableTabs.value
let activeName = editableTabsValue.value
logger.info({
activeName,
targetName
})
if (activeName === targetName) {
tabs.forEach((tab, index) => {
if (tab.name === targetName) {
const nextTab = tabs[index + 1] || tabs[index - 1]
if (nextTab) {
activeName = nextTab.name
}
}
})
}
editableTabsValue.value = activeName
editableTabs.value = tabs.filter((tab) => tab.name !== targetName)
logger.info({
activeName,
targetName
})
if (editableTabs.value.length < 1) {
drawer.value = false;
}
};
const travelConfirm = (destIndex: number, typeIndex: number) => {
const destName = travelData[destIndex].cName;
const typeName = ['普通飞机', 'PI飞机', '股票飞机', '商务飞机(机票或内衣店)'][typeIndex];
ElMessageBox.confirm(
'即将使用「' + typeName + '」飞往「' + destName + '」',
'确认',
{
confirmButtonText: '好',
cancelButtonText: '算了',
type: 'info',
}
)
.then(async () => {
await quickFlyBtnHandler.directFly(destIndex, typeIndex);
ElMessageBox.confirm('是否转跳页面?', '确认', {
confirmButtonText: '好',
cancelButtonText: '算了',
type: 'info'
}
).then(() => window.location.href = 'https://www.torn.com')
.catch(() => null);
})
.catch(() => null);
};
const bazaarControl = {
wait: (t: number) => new Promise(resolve => window.setTimeout(() => resolve(null), t)),
count: 0,
doGet: async (isClose: boolean) => {
let response;
try {
response = await (await fetch(
"https://www.torn.com/bazaar.php?sid=bazaarData&step=" + (isClose ? "closeBazaar" : "openBazaar"),
{
"headers": {
"accept": "*/*",
"sec-ch-ua-mobile": "?0",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"x-requested-with": "XMLHttpRequest"
},
"referrer": "https://www.torn.com/bazaar.php",
"referrerPolicy": "strict-origin-when-cross-origin",
"body": null,
"method": "GET",
"mode": "cors",
"credentials": "include"
}
)).json();
} catch (e) {
logger.error(e.stack);
ElMessage.error(e.message);
throw e;
}
if (response.success) {
ElMessage.success({
dangerouslyUseHTMLString: true,
message: response.text,
});
} else {
ElMessage.error(response.text);
}
},
method: async () => {
bazaarControl.count++;
if (bazaarControl.count === 1) await bazaarControl.wait(600);
if (bazaarControl.count === 1) {
bazaarControl.count = 0;
await bazaarControl.doGet(true);
}
if (bazaarControl.count === 2) {
bazaarControl.count = 0;
await bazaarControl.doGet(false);
}
},
};
onMounted(() => {
if (document.documentElement.scrollWidth < 600) {
isMobilePhone.value = true;
}
documentHeight.value = document.documentElement.scrollHeight;
});
</script>
<style scoped>
.wh-menu-button {
position: fixed;
top: 32px;
left: 10px;
}
.featureCol {
max-width: 33.33%;
}
.featureMenu {
cursor: pointer;
margin: 4px;
border: none;
background-color: transparent;
}
.featureMenu div {
text-align: center;
}
.featureMenu .icon {
font-size: 28px;
}
.featureMenu .title {
font-size: 14px;
height: 32px;
margin-left: -0.5em;
margin-right: -0.5em;
}
.featureMenu:hover {
background-color: #E9EEF6;
}
</style>