This commit is contained in:
Liwanyi 2023-06-02 19:34:48 +08:00
parent 7b661965a9
commit 302006530e
8 changed files with 237 additions and 92 deletions

View File

@ -88,7 +88,7 @@ onMounted(async () => {
</script> </script>
<template> <template>
<div v-loading="!ret"> <div v-if="ret">
<div v-if="msg" style="color: red">{{ msg }}</div> <div v-if="msg" style="color: red">{{ msg }}</div>
<el-text>飞行住院等非OK状态时该表格不可用</el-text> <el-text>飞行住院等非OK状态时该表格不可用</el-text>
<el-table v-if="ret" :data="ret" style="width: 100%"> <el-table v-if="ret" :data="ret" style="width: 100%">
@ -109,6 +109,7 @@ onMounted(async () => {
</el-table> </el-table>
<div>总计 ${{ toThousands(total) }}</div> <div>总计 ${{ toThousands(total) }}</div>
</div> </div>
<el-skeleton v-if="!ret" :rows="8" animated/>
</template> </template>
<style scoped> <style scoped>

View File

@ -13,7 +13,6 @@ const loading = ref(true);
const doFetch = () => fetch("https://www.torn.com/page.php", { const doFetch = () => fetch("https://www.torn.com/page.php", {
"headers": { "headers": {
"accept": "*/*", "accept": "*/*",
"content-type": "multipart/form-data; boundary=----WebKitFormBoundaryqnl8FNrJpu0CEGSU",
"sec-ch-ua-mobile": "?0", "sec-ch-ua-mobile": "?0",
"sec-fetch-dest": "empty", "sec-fetch-dest": "empty",
"sec-fetch-mode": "cors", "sec-fetch-mode": "cors",
@ -22,7 +21,11 @@ const doFetch = () => fetch("https://www.torn.com/page.php", {
}, },
"referrer": "https://www.torn.com/page.php?sid=events", "referrer": "https://www.torn.com/page.php?sid=events",
"referrerPolicy": "strict-origin-when-cross-origin", "referrerPolicy": "strict-origin-when-cross-origin",
"body": "------WebKitFormBoundaryqnl8FNrJpu0CEGSU\r\nContent-Disposition: form-data; name=\"sid\"\r\n\r\neventsData\r\n------WebKitFormBoundaryqnl8FNrJpu0CEGSU--\r\n", "body": (() => {
const data = new FormData();
data.append('sid', 'eventsData');
return data;
})(),
"method": "POST", "method": "POST",
"mode": "cors", "mode": "cors",
"credentials": "include" "credentials": "include"
@ -53,12 +56,13 @@ onMounted(async () => {
</script> </script>
<template> <template>
<el-timeline v-loading="loading"> <el-timeline v-if="!loading">
<el-timeline-item v-for="ev in events" :color="ev.isNew?'#0bbd87':''" <el-timeline-item v-for="ev in events" :color="ev.isNew?'#0bbd87':''"
:timestamp="DateTimeFormatter(ev.time*1000)"> :timestamp="DateTimeFormatter(ev.time*1000)">
<span v-html="ev.message"></span> <span v-html="ev.message"></span>
</el-timeline-item> </el-timeline-item>
</el-timeline> </el-timeline>
<el-skeleton v-if="loading" :rows="10" animated/>
</template> </template>
<style scoped> <style scoped>

View File

@ -1,6 +1,6 @@
<template> <template>
<el-config-provider :size="'default'" :z-index="1000000"> <el-config-provider :size="'default'" :z-index="1000000">
<el-dropdown :hide-on-click="false" size="small" style="z-index: 1000000" trigger="click"> <el-dropdown :hide-on-click="false" size="small" style="z-index: 1000000;user-select: none" trigger="click">
<el-button circle class="wh-menu-button"> <el-button circle class="wh-menu-button">
<el-icon> <el-icon>
<MoonNight/> <MoonNight/>
@ -61,6 +61,8 @@
{{ item.title }} {{ item.title }}
</el-dropdown-item> </el-dropdown-item>
</template> </template>
<el-dropdown-item @click="bazaarControl.method()">🫵 关闭店铺(双击开启)
</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
@ -68,6 +70,9 @@
v-model="drawer" v-model="drawer"
:destroy-on-close="true" :destroy-on-close="true"
:title="drawerTitle" :title="drawerTitle"
:fullscreen="isMobilePhone"
:lock-scroll="true"
width="65%"
> >
<component :is="drawerContent"/> <component :is="drawerContent"/>
</el-dialog> </el-dialog>
@ -75,7 +80,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { inject, ref, shallowRef } from 'vue'; import { inject, onMounted, ref, shallowRef } from 'vue';
import { LoggerKey } from "../ts/class/Logger"; import { LoggerKey } from "../ts/class/Logger";
import ForeignStock from "./ForeignStock.vue"; import ForeignStock from "./ForeignStock.vue";
import { MoonNight } from "@element-plus/icons-vue"; import { MoonNight } from "@element-plus/icons-vue";
@ -89,6 +94,7 @@ import EventsViewer from "./EventsViewer.vue";
import CityUItems from "./CityUItems.vue"; import CityUItems from "./CityUItems.vue";
import AutoLoginForm from "./AutoLoginForm.vue"; import AutoLoginForm from "./AutoLoginForm.vue";
import VirusProgramming from "./VirusProgramming.vue"; import VirusProgramming from "./VirusProgramming.vue";
import MarketHelper from "./MarketHelper.vue";
const logger = inject(LoggerKey); const logger = inject(LoggerKey);
const quickGymTrain = inject(QuickGymTrainKey); const quickGymTrain = inject(QuickGymTrainKey);
@ -134,7 +140,7 @@ const menuItemList = [
template: () => useItem(180), template: () => useItem(180),
}, },
{ {
title: '♻️ 能量续杯', title: '♻️ REFILL',
template: () => fetch(window.addRFC("https://www.torn.com/points.php?step=pointsbuy&action=energyrefill2"), { template: () => fetch(window.addRFC("https://www.torn.com/points.php?step=pointsbuy&action=energyrefill2"), {
"headers": { "headers": {
"accept": "text/plain, */*; q=0.01", "accept": "text/plain, */*; q=0.01",
@ -188,6 +194,10 @@ const menuItemList = [
title: '💻 PC', title: '💻 PC',
template: VirusProgramming, template: VirusProgramming,
}, },
{
title: '🛒 购物助手',
template: MarketHelper,
},
{ {
title: '⚙️ 插件配置', title: '⚙️ 插件配置',
template: Config, template: Config,
@ -196,6 +206,7 @@ const menuItemList = [
const drawer = ref(false); const drawer = ref(false);
const drawerTitle = ref(''); const drawerTitle = ref('');
const drawerContent = shallowRef(null); const drawerContent = shallowRef(null);
const isMobilePhone = ref(false);
// fast travel // fast travel
const travelData = [ const travelData = [
@ -213,7 +224,6 @@ const travelData = [
]; ];
const menuClick = (menuItem) => { const menuClick = (menuItem) => {
logger.info(menuItem);
if (typeof menuItem.template === 'function') { if (typeof menuItem.template === 'function') {
menuItem.template(); menuItem.template();
} else { } else {
@ -245,6 +255,62 @@ const travelConfirm = (destIndex, typeIndex) => {
}) })
.catch(() => null); .catch(() => null);
}; };
const bazaarControl = {
wait: (t) => new Promise(resolve => window.setTimeout(() => resolve(null), t)),
count: 0,
doGet: async (isClose) => {
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);
}
if (response.success) {
ElMessage.success({
dangerouslyUseHTMLString: true,
message: 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 (window.outerWidth < 800) {
isMobilePhone.value = true;
}
});
</script> </script>
<script lang="ts"> <script lang="ts">

View File

@ -2,11 +2,12 @@
<div v-if="commonUtils.getScriptEngine() === UserScriptEngine.RAW" class="wh-f-item"> <div v-if="commonUtils.getScriptEngine() === UserScriptEngine.RAW" class="wh-f-item">
<el-image :src="staticImageSrc"/> <el-image :src="staticImageSrc"/>
</div> </div>
<el-table v-else v-loading="loading" :data="tableData" style="width: 100%"> <el-table v-else-if="!loading" :data="tableData" style="width: 100%">
<el-table-column label="区域" prop="area"/> <el-table-column label="区域" prop="area"/>
<el-table-column label="更新时间" prop="updateTime"/> <el-table-column label="更新时间" prop="updateTime"/>
<el-table-column label="库存" prop="stock"/> <el-table-column label="库存" prop="stock"/>
</el-table> </el-table>
<el-skeleton v-if="loading" :rows="13" animated/>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

122
src/vue/MarketHelper.vue Normal file
View File

@ -0,0 +1,122 @@
<template>
<el-row>
<el-col :span="20">
<el-select-v2
v-model="itemName"
:options="itemNameList"
clearable
filterable
placeholder="物品名"
style="width: 99%"
/>
</el-col>
<el-col :span="4">
<el-button style="width: 100%" type="primary" @click="doGetIMarketList(itemName2IdMap[itemName])">查询
</el-button>
</el-col>
</el-row>
<div v-if="itemOnList">
<div v-for="item in itemOnList">
<div>{{ item.playerName }}[{{ item.playerId }}] ${{ toThousands(item.price) }} x {{ item.amount }}</div>
<el-input :model-value="item.amount"/>
<el-button @click="buy(item.playerId)">购买</el-button>
</div>
</div>
</template>
<script lang="ts">
export default {
name: "MarketHelper"
}
</script>
<script lang="ts" setup>
import { inject, onMounted, ref } from "vue";
import { LoggerKey } from "../ts/class/Logger";
import { ItemHelperKey } from "../ts/class/utils/ItemHelper";
import { itemNameDict } from "../ts/dictionary/translation";
import toThousands from "../ts/func/utils/toThousands";
const logger = inject(LoggerKey);
const itemHelper = inject(ItemHelperKey);
let itemName2IdMap: { [p: string]: number } = null;
const itemName = ref('');
const itemNameList = ref([]);
const html = ref('');
const itemOnList = ref<{ playerId: string, playerName: string, price: string, amount: number }[]>([]);
const doGetIMarketList = async (id) => {
const queryItemListHtml = await (await fetch(window.addRFC("https://www.torn.com/imarket.php"), {
"headers": {
"accept": "*/*",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"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/imarket.php",
"referrerPolicy": "strict-origin-when-cross-origin",
"body": "step=getShopList1&itemID=" + id,
"method": "POST",
"mode": "cors",
"credentials": "include"
})).text();
itemOnList.value = [];
const container = document.createElement('div');
container.innerHTML = queryItemListHtml;
const list = container.querySelectorAll('ul.items>li');
logger.info({ list });
list.forEach(li => {
let item = { playerId: null, playerName: null, price: null, amount: null };
item.playerId = li.querySelector("a.user.name").getAttribute('href').trim().split('XID=')[1];
item.playerName = li.querySelector("a.user.name span").innerText.trim();
item.price = Number(li.querySelector("span.cost-price").innerText.trim().replace('$', '').replaceAll(',', ''));
item.amount = Number(li.querySelector("span.cost-amount").innerText.trim().split(' ')[0].replace('(', ""))
itemOnList.value.push(item);
});
};
const buy = async (userId) => {
const bazaarItemList = await (await fetch(
`https://www.torn.com/bazaar.php?sid=bazaarData&step=getBazaarItems&start=0&ID=${ userId }&order=default&by=asc&categorised=0&limit=1000&searchname=`,
{
"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?userId=" + userId,
"referrerPolicy": "strict-origin-when-cross-origin",
"body": null,
"method": "GET",
"mode": "cors",
"credentials": "include"
})).json();
let bazaarId;
};
onMounted(async () => {
let map = itemHelper.getItemNameMap();
if (map.data) {
itemName2IdMap = map.data;
} else {
itemName2IdMap = await map.promise;
}
Object.keys(itemName2IdMap).forEach(itemName => {
itemNameList.value.push({
value: itemName,
label: itemNameDict[itemName] ? itemName + ` (${ itemNameDict[itemName] })` : itemName,
})
});
});
</script>
<style scoped>
</style>

View File

@ -1,53 +0,0 @@
<script lang="ts" setup>
import { useEventListener } from "../ts/useEventListener";
import { inject, shallowRef } from "vue";
import { LoggerKey } from "../ts/class/Logger";
const logger = inject(LoggerKey);
const props = defineProps<{
curPopupTitle: { title: string, },
}>();
// dom
const container = shallowRef();
useEventListener(container, 'click', (ev) => {
ev.preventDefault();
if (ev.target === container.value) {
props.curPopupTitle.title = null;
}
});
</script>
<template>
<div v-show="curPopupTitle.title" ref="container" class="popup-container">
<div class="border-round">
<div class="title-black top-round">
<p>{{ curPopupTitle.title || '芜湖助手' }}</p>
</div>
<div class="bottom-round cont-gray scroll-area scrollbar-transparent">
<slot></slot>
</div>
</div>
</div>
</template>
<script lang="ts">
export default {
name: "PopupWindow"
}
</script>
<style scoped>
.popup-container {
display: block;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #00000080;
z-index: 110001;
}
</style>

View File

@ -1,9 +1,8 @@
<template> <template>
<el-button @click="doCrime(15,'warehouse')">烧仓库</el-button> <el-button @click="doCrime(15,'warehouse')">烧仓库</el-button>
<el-button @click="doCrime(18,'hackbank')">做18</el-button> <el-button @click="doCrime(18,'hackbank')">做18</el-button>
<div v-if="results"> <div v-if="results" v-html="results"></div>
<div v-html="results"/> <el-skeleton v-if="loading" :rows="3" animated/>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
@ -13,34 +12,39 @@ export default {
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue'; import { inject, ref } from 'vue';
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { LoggerKey } from "../ts/class/Logger";
const logger = inject(LoggerKey);
const results = ref(""); const results = ref("");
const doCrime = (nerve, crime: "hackbank" | "warehouse") => { const loading = ref(false);
fetch(window.addRFC("https://www.torn.com/crimes.php?step=docrime4&timestamp=" + Date.now()), { const doCrime = async (nerve, crime: "hackbank" | "warehouse") => {
"headers": { loading.value = true;
"accept": "text/plain, */*; q=0.01", results.value = '';
"content-type": "application/x-www-form-urlencoded; charset=UTF-8", try {
"sec-ch-ua-mobile": "?0", results.value = await (await fetch(window.addRFC("https://www.torn.com/crimes.php?step=docrime4&timestamp=" + Date.now()), {
"sec-fetch-dest": "empty", "headers": {
"sec-fetch-mode": "cors", "accept": "text/plain, */*; q=0.01",
"sec-fetch-site": "same-origin", "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"x-requested-with": "XMLHttpRequest" "sec-ch-ua-mobile": "?0",
}, "sec-fetch-dest": "empty",
"referrer": "https://www.torn.com/crimes.php", "sec-fetch-mode": "cors",
"referrerPolicy": "strict-origin-when-cross-origin", "sec-fetch-site": "same-origin",
"body": `nervetake=${ nerve }&crime=${ crime }`, "x-requested-with": "XMLHttpRequest"
"method": "POST", },
"mode": "cors", "referrer": "https://www.torn.com/crimes.php",
"credentials": "include" "referrerPolicy": "strict-origin-when-cross-origin",
}) "body": `nervetake=${ nerve }&crime=${ crime }`,
.then(res => res.text()) "method": "POST",
.then(res => results.value = res) "mode": "cors",
.catch(e => ElMessage({ "credentials": "include"
message: e.toString, })).text();
type: 'error' } catch (e) {
})); logger.error(e.stack);
results.value = e.message;
}
loading.value = false;
}; };
</script> </script>

View File

@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<el-button type="primary" @click="doProgramVirus(false)">造简单病毒</el-button> <el-button :loading="loading" type="primary" @click="doProgramVirus(false)">造简单病毒</el-button>
<el-button type="danger" @click="doProgramVirus(true)">取消病毒编程</el-button> <el-button :loading="loading" type="danger" @click="doProgramVirus(true)">取消病毒编程</el-button>
</div> </div>
<div v-loading="loading" v-html="html"></div> <div v-loading="loading" v-html="html"></div>
</template> </template>