This commit is contained in:
Liwanyi 2023-06-07 16:20:52 +08:00
parent ccf30c5dca
commit 0c6ab02e8d
5 changed files with 120 additions and 66 deletions

4
.gitignore vendored
View File

@ -2,3 +2,7 @@
/.fleet /.fleet
/src/dist/bundle.min.js /src/dist/bundle.min.js
/src/dist/bundle.js /src/dist/bundle.js
/dist
auto-imports.d.ts
components.d.ts
aws.xml

View File

@ -5,6 +5,14 @@
# CHANGE # CHANGE
## 0.9.9
2023年06月07日
### 修改
- 购物助手错误修复及样式调整
## 0.9.8 ## 0.9.8
2023年06月06日 2023年06月06日

View File

@ -1,6 +1,6 @@
{ {
"name": "wuhu-torn-helper", "name": "wuhu-torn-helper",
"version": "0.9.8", "version": "0.9.9",
"description": "芜湖助手", "description": "芜湖助手",
"scripts": { "scripts": {
"release": "cross-env NODE_ENV=production rollup -c && node build.mjs", "release": "cross-env NODE_ENV=production rollup -c && node build.mjs",

File diff suppressed because one or more lines are too long

View File

@ -1,54 +1,53 @@
<template> <template>
<el-row> <el-row>
<el-col :span="20"> <el-col :span="22">
<el-select-v2 <el-select-v2 v-model="itemName" :options="itemNameList" clearable filterable placeholder="物品名"
v-model="itemName" style="width: 99%"/>
:options="itemNameList"
clearable
filterable
placeholder="物品名"
style="width: 99%"
/>
</el-col> </el-col>
<el-col :span="4"> <el-col :span="2">
<el-button style="width: 100%" type="primary" @click="doGetIMarketList(itemName2IdMap[itemName])">查询 <el-button :disabled="!itemName" :icon="Search" :loading="listLoading"
@click="doGetIMarketList(itemName2IdMap[itemName])">查询
</el-button> </el-button>
</el-col> </el-col>
</el-row> </el-row>
<div v-if="itemOnList.length > 0"> <el-skeleton :loading="listLoading" :rows="5" animated style="margin-top: 1em">
<span :style="{backgroundImage: 'url(/images/items/' + itemName2IdMap[itemName] + '/small.png)'}" <template #default>
style="display:inline-block;width:38px;height:19px;"/> <el-table :data="itemOnList" empty-text="暂无数据" style="width: 100%" table-layout="auto">
<el-table :data="itemOnList" style="width: 100%" table-layout="auto"> <el-table-column label="卖家">
<el-table-column label="卖家"> <template #default="scope">
<template #default="scope"> <el-link :href="'/bazaar.php?userId=' + scope.row.playerId" :underline="false" target="_blank"
<el-link type="primary" v-text="scope.row.playerName + `[${scope.row.playerId}]`"/>
:href="'/bazaar.php?userId=' + scope.row.playerId" :underline="false" </template>
target="_blank" </el-table-column>
type="primary" <el-table-column label="单价">
v-text="scope.row.playerName + `[${scope.row.playerId}]`" <template #default="scope">
/> <div style="display: flex; align-items: center"
</template> v-text="'$' + toThousands(scope.row.price)"></div>
</el-table-column> </template>
<el-table-column label="单价"> </el-table-column>
<template #default="scope"> <el-table-column label="在售" prop="amount"/>
<div style="display: flex; align-items: center" v-text="'$' + toThousands(scope.row.price)"></div> <el-table-column label="">
</template> <template #default="scope">
</el-table-column> <el-input v-model="scope.row.inputAmount" style="width: 12em">
<el-table-column label="在售" prop="amount"/> <template #append>
<el-table-column label="批量购买"> <el-button :icon="ShoppingCart" :loading="scope.row.onBuying" type="primary"
<template #default="scope"> @click="buy(scope.row)">购买
<el-input v-model="scope.row.inputAmount" style="width: 12em"> </el-button>
<template #append> </template>
<el-button :loading="scope.row.onBuying" </el-input>
type="primary" @click="buy(itemName, itemName2IdMap[itemName], scope.row)">购买 </template>
</el-button> </el-table-column>
</template> </el-table>
</el-input> <el-row v-if="queryTs">
</template> <el-col :span="2"><span><el-icon><Timer/></el-icon>{{ timePastFormat(currentTs - queryTs) }}</span>
</el-table-column> </el-col>
</el-table> <el-col :span="3">
</div> <span :style="{ backgroundImage: 'url(/images/items/' + itemId + '/small.png)' }"
<el-skeleton v-if="listLoading" animated count="5" style="margin-top: 1em"/> style="display:inline-block;width:38px;height:19px;"></span>
</el-col>
</el-row>
</template>
</el-skeleton>
</template> </template>
<script lang="ts"> <script lang="ts">
@ -59,12 +58,13 @@ export default {
<script lang="ts" setup> <script lang="ts" setup>
import { inject, onMounted, ref } from "vue"; import { Search, ShoppingCart, Timer } from '@element-plus/icons-vue';
import { ElMessage } from "element-plus";
import { inject, onBeforeUnmount, onMounted, ref } from "vue";
import { LoggerKey } from "../ts/class/Logger"; import { LoggerKey } from "../ts/class/Logger";
import { ItemHelperKey } from "../ts/class/utils/ItemHelper"; import { ItemHelperKey } from "../ts/class/utils/ItemHelper";
import { itemNameDict } from "../ts/dictionary/translation"; import { itemNameDict } from "../ts/dictionary/translation";
import toThousands from "../ts/func/utils/toThousands"; import toThousands from "../ts/func/utils/toThousands";
import { ElMessage } from "element-plus";
const logger = inject(LoggerKey); const logger = inject(LoggerKey);
const itemHelper = inject(ItemHelperKey); const itemHelper = inject(ItemHelperKey);
@ -72,7 +72,8 @@ const itemHelper = inject(ItemHelperKey);
let itemName2IdMap: { [p: string]: number } = null; let itemName2IdMap: { [p: string]: number } = null;
const itemName = ref<string>(''); const itemName = ref<string>('');
const itemNameList = ref<{ value: string, label: string }[]>([]); const itemNameList = ref<{ value: string, label: string }[]>([]);
const html = ref<string>(''); const itemId = ref<number>();
const queryTs = ref<number>(0);
type ItemInfo = { type ItemInfo = {
playerId: string, playerId: string,
playerName: string, playerName: string,
@ -81,13 +82,36 @@ type ItemInfo = {
inputAmount: number, inputAmount: number,
// index: number, // index: number,
onBuying: boolean, onBuying: boolean,
itemId: string,
}; };
const itemOnList = ref<ItemInfo[]>([]); const itemOnList = ref<ItemInfo[]>([]);
const listLoading = ref<boolean>(false); const listLoading = ref<boolean>(false);
const currentTs = ref<number>(Date.now());
const doGetIMarketList = async (id) => { const timePastFormat = (ts: number): string => {
//
if (ts < 1000) {
return ts + 'ms';
}
//
else if (ts < 60000) {
return (ts / 1000 | 0) + 's';
}
//
else if (ts < 3600000) {
return (ts / 60000 | 0) + 'm';
}
//
else {
return (ts / 3600000 | 0) + 'h';
}
};
const doGetIMarketList = async (id: number) => {
itemOnList.value = []; itemOnList.value = [];
listLoading.value = true; listLoading.value = true;
queryTs.value = 0;
itemId.value = id;
const queryItemListHtml = await (await fetch(window.addRFC("https://www.torn.com/imarket.php"), { const queryItemListHtml = await (await fetch(window.addRFC("https://www.torn.com/imarket.php"), {
"headers": { "headers": {
"accept": "*/*", "accept": "*/*",
@ -105,8 +129,22 @@ const doGetIMarketList = async (id) => {
"mode": "cors", "mode": "cors",
"credentials": "include" "credentials": "include"
})).text(); })).text();
queryTs.value = Date.now();
let resError = '';
try {
const res = JSON.parse(queryItemListHtml);
if (!res.success) {
resError = res.error || res.text;
ElMessage.error(resError);
}
} catch (e) {
}
listLoading.value = false; listLoading.value = false;
// bazaar html
if (resError) {
throw new Error(resError);
}
let container = document.createElement('div'); let container = document.createElement('div');
container.innerHTML = queryItemListHtml; container.innerHTML = queryItemListHtml;
const list = container.querySelectorAll('ul.items li.private-bazaar'); const list = container.querySelectorAll('ul.items li.private-bazaar');
@ -119,22 +157,24 @@ const doGetIMarketList = async (id) => {
amount: null, amount: null,
inputAmount: null, inputAmount: null,
index, index,
onBuying: false onBuying: false,
itemId: null,
}; };
item.playerId = li.querySelector("a.user.name").getAttribute('href').trim().split('XID=')[1]; item.playerId = li.querySelector("a.user.name").getAttribute('href').trim().split('XID=')[1];
item.playerName = li.querySelector("a.user.name span").innerText.trim(); item.playerName = li.querySelector("a.user.name span").innerText.trim();
item.price = Number(li.querySelector("span.cost-price").innerText.trim().replace('$', '').replaceAll(',', '')); 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('(', "")) item.amount = Number(li.querySelector("span.cost-amount").innerText.trim().split(' ')[0].replace('(', "").replaceAll(',', ''));
item.inputAmount = item.amount; item.inputAmount = item.amount;
item.itemId = id;
itemOnList.value.push(item); itemOnList.value.push(item);
}); });
container = null; container = null;
}; };
const buy = async (itemName: string, itemId: number, itemInfo: ItemInfo) => { const buy = async (itemInfo: ItemInfo) => {
itemInfo.onBuying = true; itemInfo.onBuying = true;
try { try {
// id bazaar // id bazaar
let bazaarId; let bazaarId: string;
const bazaarItemList: { list: { bazaarID: string, itemID: string }[] } = await (await fetch( const bazaarItemList: { list: { bazaarID: string, itemID: string }[] } = await (await fetch(
`https://www.torn.com/bazaar.php?sid=bazaarData&step=getBazaarItems&start=0&ID=${ itemInfo.playerId }&order=default&by=asc&categorised=0&limit=1000&searchname=`, `https://www.torn.com/bazaar.php?sid=bazaarData&step=getBazaarItems&start=0&ID=${ itemInfo.playerId }&order=default&by=asc&categorised=0&limit=1000&searchname=`,
{ {
@ -154,14 +194,14 @@ const buy = async (itemName: string, itemId: number, itemInfo: ItemInfo) => {
"credentials": "include" "credentials": "include"
})).json(); })).json();
for (let i = 0; i < bazaarItemList.list.length; i++) { for (let i = 0; i < bazaarItemList.list.length; i++) {
if (bazaarItemList.list[i].itemID === itemId.toString()) { if (bazaarItemList.list[i].itemID === itemInfo.itemId.toString()) {
bazaarId = bazaarItemList.list[i].bazaarID; bazaarId = bazaarItemList.list[i].bazaarID;
break; break;
} }
} }
if (!bazaarId) { if (!bazaarId) {
ElMessage.error('上架商品未找到'); ElMessage.error('上架商品未找到');
logger.error('物品id未找到对应bazaar项目', bazaarItemList, itemId); logger.error('物品id未找到对应bazaar项目', bazaarItemList, itemInfo.itemId);
(() => { (() => {
throw new Error('bazaarId为空') throw new Error('bazaarId为空')
})(); })();
@ -183,7 +223,7 @@ const buy = async (itemName: string, itemId: number, itemInfo: ItemInfo) => {
const data = new FormData(); const data = new FormData();
data.append('userID', itemInfo.playerId); data.append('userID', itemInfo.playerId);
data.append('id', bazaarId); data.append('id', bazaarId);
data.append('itemID', itemId.toString()); data.append('itemID', itemInfo.itemId.toString());
data.append('amount', itemInfo.inputAmount.toString()); data.append('amount', itemInfo.inputAmount.toString());
data.append('price', itemInfo.price.toString()); data.append('price', itemInfo.price.toString());
data.append('beforeval', (itemInfo.price * itemInfo.inputAmount).toString()); data.append('beforeval', (itemInfo.price * itemInfo.inputAmount).toString());
@ -203,7 +243,9 @@ const buy = async (itemName: string, itemId: number, itemInfo: ItemInfo) => {
itemInfo.onBuying = false; itemInfo.onBuying = false;
}; };
let intervalId = 0;
onMounted(async () => { onMounted(async () => {
intervalId = window.setInterval(() => currentTs.value = Date.now(), 100);
let map = itemHelper.getItemNameMap(); let map = itemHelper.getItemNameMap();
if (map.data) { if (map.data) {
itemName2IdMap = map.data; itemName2IdMap = map.data;
@ -217,8 +259,8 @@ onMounted(async () => {
}) })
}); });
}); });
onBeforeUnmount(() => window.clearInterval(intervalId));
</script> </script>
<style scoped> <style scoped></style>
</style>