This commit is contained in:
Liwanyi 2023-02-28 18:25:34 +08:00
parent 791567bf37
commit 10708e7f14
19 changed files with 288 additions and 92 deletions

22
package-lock.json generated
View File

@ -17,6 +17,7 @@
"@types/jquery": "^3.5.14",
"@types/node": "^18.0.6",
"@vitejs/plugin-vue": "^4.0.0",
"@vue/tsconfig": "^0.1.3",
"cross-env": "^7.0.3",
"npm": "^8.19.2",
"rollup": "^2.79.0",
@ -876,6 +877,20 @@
"integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==",
"dev": true
},
"node_modules/@vue/tsconfig": {
"version": "0.1.3",
"resolved": "https://registry.npmmirror.com/@vue/tsconfig/-/tsconfig-0.1.3.tgz",
"integrity": "sha512-kQVsh8yyWPvHpb8gIc9l/HIDiiVUy1amynLNpCy8p+FoCiZXCo6fQos5/097MmnNZc9AtseDsCrfkhqCrJ8Olg==",
"dev": true,
"peerDependencies": {
"@types/node": "*"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
}
}
},
"node_modules/acorn": {
"version": "8.8.2",
"resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.8.2.tgz",
@ -5807,6 +5822,13 @@
"integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==",
"dev": true
},
"@vue/tsconfig": {
"version": "0.1.3",
"resolved": "https://registry.npmmirror.com/@vue/tsconfig/-/tsconfig-0.1.3.tgz",
"integrity": "sha512-kQVsh8yyWPvHpb8gIc9l/HIDiiVUy1amynLNpCy8p+FoCiZXCo6fQos5/097MmnNZc9AtseDsCrfkhqCrJ8Olg==",
"dev": true,
"requires": {}
},
"acorn": {
"version": "8.8.2",
"resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.8.2.tgz",

View File

@ -16,6 +16,7 @@
"@types/jquery": "^3.5.14",
"@types/node": "^18.0.6",
"@vitejs/plugin-vue": "^4.0.0",
"@vue/tsconfig": "^0.1.3",
"cross-env": "^7.0.3",
"npm": "^8.19.2",
"rollup": "^2.79.0",

View File

@ -3,4 +3,5 @@ import rollupConfig from "./rollup.config";
rollupConfig.plugins.push(terser());
rollupConfig.output.file = 'dist/bundle.min.js';
rollupConfig.output.name = 'bundle.min.js';
export default rollupConfig;

View File

@ -11,10 +11,12 @@ let node_env = process.env.NODE_ENV;
let vuePath = node_env === 'production' ?
'vue/dist/vue.runtime.esm-browser.prod.js' : 'vue/dist/vue.runtime.esm-browser.js';
export default {
input: 'src/ts/index.ts',
// input: 'src/ts/index.ts',
input: 'src/ts/class/Application.ts',
output: {
file: 'dist/bundle.js',
format: 'iife',
name: 'bundle.js',
},
plugins: [
json(),

View File

@ -6,7 +6,9 @@ import UrlPattern from "./UrlMatch";
import WuhuConfig from "./WuhuConfig";
import translateMain from "../func/translate/translateMain";
import CommonUtils from "./utils/CommonUtils";
import EntryPoint from "./provider/EntryPoint";
@EntryPoint
export default class Application {
public static main(): void {

View File

@ -5,7 +5,7 @@ import Log from "./Log";
export default class WuhuBase extends Provider {
public static glob: IGlobal = null;
protected readonly className: string = 'WuhuBase';
readonly className: string = 'WuhuBase';
/**
* localStorage wh_trans_settings (json)

View File

@ -0,0 +1,18 @@
import Log from "../Log";
import ZhongIcon from "../ZhongIcon";
export default function EntryPoint(T: { main: () => void }) {
if (window.WHTRANS) throw '退出, 已运行次数' + window.WHTRANS;
window.WHTRANS = window.WHTRANS === undefined ? 1 : window.WHTRANS++;
let started = performance.now();
try {
T.main();
} catch (e) {
Log.error('[Starter]加载出错信息: ' + e.stack || e.message);
}
let runTime: number = (performance.now() - started) | 0;
Log.info(`芜湖脚本完成加载, 耗时${ runTime }ms`);
if (ZhongIcon.ZhongNode && ZhongIcon.ZhongNode.initTimer)
ZhongIcon.ZhongNode.initTimer.innerHTML = `加载时间 ${ runTime }ms`;
}

View File

@ -1,11 +1,12 @@
import Log from "../Log";
import Timer from "../utils/Timer";
import ClassWithName from "../../interface/ClassWithName";
/**
*
*/
export default class Provider {
protected readonly className: string = 'Provider';
export default class Provider implements ClassWithName {
readonly className: string = 'Provider';
private static instance;
private static readonly pool = {};

View File

@ -1,21 +1,21 @@
import Log from "../Log";
import ZhongIcon from "../ZhongIcon";
export default class Starter {
public static run(T): void {
if (window.WHTRANS) throw '退出, 已运行次数' + window.WHTRANS;
window.WHTRANS = window.WHTRANS === undefined ? 1 : window.WHTRANS++;
let started = performance.now();
try {
T.main();
} catch (e) {
Log.error('[Starter]加载出错信息: ' + e.stack || e.message);
}
let runTime: number = (performance.now() - started) | 0;
Log.info(`芜湖脚本完成加载, 耗时${ runTime }ms`);
if (ZhongIcon.ZhongNode && ZhongIcon.ZhongNode.initTimer)
ZhongIcon.ZhongNode.initTimer.innerHTML = `加载时间 ${ runTime }ms`;
}
}
// import Log from "../Log";
// import ZhongIcon from "../ZhongIcon";
//
// export default class Starter {
// public static run(T): void {
//
// if (window.WHTRANS) throw '退出, 已运行次数' + window.WHTRANS;
// window.WHTRANS = window.WHTRANS === undefined ? 1 : window.WHTRANS++;
//
// let started = performance.now();
// try {
// T.main();
// } catch (e) {
// Log.error('[Starter]加载出错信息: ' + e.stack || e.message);
// }
// let runTime: number = (performance.now() - started) | 0;
// Log.info(`芜湖脚本完成加载, 耗时${ runTime }ms`);
// if (ZhongIcon.ZhongNode && ZhongIcon.ZhongNode.initTimer)
// ZhongIcon.ZhongNode.initTimer.innerHTML = `加载时间 ${ runTime }ms`;
// }
// }

View File

@ -3,9 +3,15 @@ import InventoryItemInfo from "../../interface/responseType/InventoryItemInfo";
import FetchUtils from "./FetchUtils";
import CommonUtils from "./CommonUtils";
import Log from "../Log";
import PriceData from "../../interface/PriceData";
import Asyncable from "../../interface/Asyncable";
export default class ItemHelper extends WuhuBase {
className = "ItemHelper";
/** 保存物品名-ID对应关系 */
private itemNameMap: { [name: string]: number } = null;
itemValueMap: { [k: string]: Partial<InventoryItemInfo> } = {
'Glass of Beer': {
itemName: '一杯啤酒',
@ -18,9 +24,11 @@ export default class ItemHelper extends WuhuBase {
" <div class=\"t-green bold item-effect m-top10\">效果: 犯罪 + 2增幅CD + 1h。</div>",
},
};
itemPriceMap = {
205: { name: 'Vicodin', price: 1300 },
};
// 缓存过期时间 分钟
private readonly priceTimeout = 720;
@ -29,11 +37,28 @@ export default class ItemHelper extends WuhuBase {
super();
}
/** TODO 通过 name 查询 */
public async getItemData(idOrName: string): Promise<Partial<InventoryItemInfo>> {
let _itemId: number = null;
try {
_itemId = parseInt(idOrName);
} finally {
}
let itemId: number;
if (this.itemNameMap) {
itemId = _itemId || this.itemNameMap[idOrName];
} else {
itemId = (await this.getItemNameMap().promise)[idOrName];
}
return await (await FetchUtils.getInstance().ajaxFetch({
url: `/inventory.php?step=info&itemID=${ idOrName }&armouryID=0&asObject=true`,
method: 'GET',
referrer: 'bazaar.php',
url: '/inventory.php',
method: 'POST',
body: JSON.stringify({
step: 'info',
itemID: itemId,
armouryID: 0
}),
referrer: 'displaycase.php',
})).json();
}
@ -58,10 +83,11 @@ export default class ItemHelper extends WuhuBase {
return (await this.getItemData(idOrName)).itemValue.replaceAll(/[,$]/, '');
}
public getLocalPriceData(): { priceData: PriceData, promise: Promise<PriceData> } {
/** 返回本地物品价格数据id对应物品名和价格 */
public getLocalPriceData(): Asyncable<PriceData["data"]> {
// 获取本地缓存
let localStore = localStorage.getItem('WHItemPrice');
let res = { priceData: null, promise: null };
let res = { data: null, promise: null };
let data: PriceData = null;
// 无缓存
if (!localStore) {
@ -77,16 +103,58 @@ export default class ItemHelper extends WuhuBase {
}
// 缓存超时
if (Date.now() - data.timestamp > this.priceTimeout * 60000) {
res.priceData = null;
res.data = null;
res.promise = this.fetchPriceData();
} else {
res.priceData = data;
res.data = data.data;
res.promise = null;
}
return res;
}
private async fetchPriceData(): Promise<PriceData> {
/** 返回物品名与id对应关系 */
public getItemNameMap(): Asyncable<{ [key: string]: number }> {
let localData = this.getLocalPriceData();
let ret: Asyncable<{ [key: string]: number }> = {
data: null,
promise: null
};
// 有缓存
if (localData.data || this.itemNameMap) {
// 内存缓存
if (this.itemNameMap) {
ret.data = this.itemNameMap;
}
// 本地缓存
else {
ret.data = {};
Object.keys(localData.data).forEach((k) => {
ret.data[localData.data[k].name] = parseInt(k);
});
this.itemNameMap = ret.data;
}
}
// 无缓存异步返回
else {
ret.promise = new Promise(async resolve => {
let response = await localData.promise;
Log.info({ response });
let promiseRet = {};
Object.keys(response).forEach(k => {
promiseRet[response[k].name] = k;
});
this.itemNameMap = promiseRet;
resolve(promiseRet);
});
}
return ret;
}
/**
* fetch方法返回格式: 物品id ->
* `{name: string, price: number}`
*/
private async fetchPriceData(): Promise<PriceData["data"]> {
let res = null;
// 获取在线价格
try {
@ -104,13 +172,3 @@ export default class ItemHelper extends WuhuBase {
return res;
}
}
interface PriceData {
timestamp: number
data: {
[k: number]: {
name: string
price: number
}
}
}

View File

@ -253,7 +253,7 @@ export default function christmasTownHelper() {
const now = new Date();
dropHist[hist_key] = {
pos: `[${ nearby_item.x },${ nearby_item.y }]`,
map: $ct_title.firstChild.nodeValue.trim(),
data: $ct_title.firstChild.nodeValue.trim(),
last: `${ now.getFullYear() }-${ now.getMonth() + 1 }-${ now.getDate() } ${ now.getHours() }:${ now.getMinutes() }:${ now.getSeconds() }`,
name: item_name,
id: Object.keys(dropHist).length,

View File

@ -109,12 +109,12 @@ export default function cityFinder(_base: TornStyleBlock): void {
// }
let priceData = itemHelper.getLocalPriceData();
if (priceData.priceData) {
items = priceData.priceData.data;
if (priceData.data) {
items = priceData.data;
displayNamePrice();
} else {
window.setTimeout(async () => {
items = (await priceData.promise).data;
items = (await priceData.promise);
displayNamePrice();
}, 0);
}

View File

@ -1,4 +1,4 @@
import Starter from "./class/provider/Starter";
import Application from "./class/Application";
Starter.run(Application);
// import Starter from "./class/provider/Starter";
// import Application from "./class/Application";
//
// Starter.run(Application);

View File

@ -0,0 +1,5 @@
/** 有同步数据来源时返回data否则返回promise对象 */
export default interface Asyncable<T> {
data?: T
promise?: Promise<T>
}

View File

@ -0,0 +1,3 @@
export default interface BazaarApiResponse {
bazaar: { ID: number, cost: number, quantity: number }[]
}

View File

@ -0,0 +1,3 @@
export default interface ClassWithName {
readonly className: string
}

View File

@ -0,0 +1,11 @@
export default interface PriceData {
timestamp: number
data: {
[k: number]: ItemPrice
}
}
export interface ItemPrice {
name: string
price: number
}

View File

@ -10,34 +10,9 @@ export default class Test extends WuhuBase {
className = 'Test';
public test(): void {
let popup = new Popup(
// '<input v-model="itemNameInput" placeholder="输入物品名(英)" /><br/>' +
// '<select v-if="itemNameInput" v-on:select=""><option v-for="item in filteredItems">{{ item }}</option></select>' +
// '<p v-if="itemInfo"></p>' +
''
);
let popup = new Popup('');
popup.getElement()['__POOL__'] = Test.getPool();
Log.info({ NET: globVars.WH_NET_LOG });
// let vueApp = createApp({
// data() {
// return {
// itemNameInput: "",
// itemInfo:{}
// }
// },
// computed: {
// filteredItems() {
// let arr = ['123', '456', '789', '135', '246', '357', '579'];
// let out = [];
// if (this.itemNameInput) {
// arr.forEach(v => {
// v.includes(this.itemNameInput) && out.push(v);
// })
// }
// return out;
// }
// }
// });
let vueApp = createApp(ItemPrice);
vueApp.mount('#wh-popup-cont');
popup.setOnClosing(() => vueApp.unmount());
@ -46,7 +21,6 @@ export default class Test extends WuhuBase {
private case1() {
// FetchUtils.getInstance().ajaxFetch({
// url: '/inventory.php?step=info&itemID=205&armouryID=0&asObject=true',
//
// method: 'GET',
// referrer: 'bazaar.php',
// });

View File

@ -1,46 +1,141 @@
<template>
<input v-model="itemNameInput" placeholder="输入物品名(英)"/>
<select v-if="itemNameInput" v-on:select="">
<select v-if="itemNameInput" v-model="itemSelected" @change="onSelectChange">
<option disabled value="">{{ optDefault }}</option>
<option v-for="item in filteredItems">{{ item }}</option>
</select>
<p v-if="itemInfo">
<span>估价 (缓存){{ itemInfo.value }}</span>
<span>实时市价 (API)<button @click="onApiPricePress"/></span>
</p>
<div v-if="itemInfo.id !== -1">
<p>物品名{{ itemInfo.name }}<img :src="`/images/items/${itemInfo.id}/medium.png`" alt="itemImg"/></p>
<p>估价 (缓存){{ itemInfo.price }}</p>
<div>
实时市价 (API)
<span v-if="apiPriceResult.status === -1">
<button @click="(ev) => onApiPricePress(itemInfo.name, ev)">API查询</button>
</span>
<div v-else>
<ul>
<li v-for="item in apiPriceResult.data">{{ moneyFormat(item.cost) }} x {{ item.quantity }}</li>
</ul>
</div>
<button @click="(ev) => onApiPricePress(itemInfo.name, ev)">API查询</button>
</div>
<p>昨日市值 (Value)
<span v-if="itemInfo.value">{{ itemInfo.value }}</span>
<span v-else>...</span>
</p>
<p>
</p>
<p><a :href="'/imarket.php#/p=shop&step=shop&type=&searchname='+itemInfo.name.toString().replaceAll(' ','+')"
target="_blank">前往市场</a></p>
</div>
</template>
<script lang="ts">
import Log from "../class/Log";
import Hello from "./Hello.vue";
import ItemHelper from "../class/utils/ItemHelper";
import toThousands from "../func/utils/toThousands";
import FetchUtils from "../class/utils/FetchUtils";
import Alert from "../class/utils/Alert";
import Global from "../class/Global";
export default {
name: "ItemPrice",
components: { Hello },
data() {
return {
itemNameInput: "",
itemNameInput: '',
itemInfo: {
value: 100
}
}
id: -1,
name: 0,
price: 0,
value: '',
},
// mounted-id
itemNameMap: null,
optDefault: '加载中...',
itemSelected: '',
apiPriceResult: {
status: -1,
data: []
},
};
},
computed: {
filteredItems() {
let arr = ['123', '456', '789', '135', '246', '357', '579'];
if (!this.itemNameMap) return [];
// let arr = ['123', '456', '789', '135', '246', '357', '579'];
let arr = Object.keys(this.itemNameMap);
let out = [];
if (this.itemNameInput) {
arr.forEach(v => {
v.includes(this.itemNameInput) && out.push(v);
v.toLowerCase().includes(this.itemNameInput.toLowerCase()) && out.push(v);
})
}
return out;
}
},
methods: {
onApiPricePress() {
Log.info('test');
async onApiPricePress(itemName, ev) {
ev.target.disabled = true;
// Log.info({ itemName });
// this.apiPriceResult.status = -99;
// BazaarApiResponse
let res = null;
let apiKey = Global.getInstance().isPDA ? Global.getInstance().PDA_APIKey : localStorage.getItem('APIKey');
try {
if (!apiKey) return new Error('无APIKEY');
res = JSON.parse(
await FetchUtils
.getInstance()
.fetchText(`//api.torn.com/market/${ this.itemNameMap[itemName] }?selections=bazaar&key=${ apiKey }`)
).bazaar.slice(0, 5);
} catch (e) {
Log.error(e.stack || e.message || e);
new Alert(e.message || e.toString());
ev.target.disabled = false;
}
this.apiPriceResult.status = 0;
this.apiPriceResult.data = res;
ev.target.disabled = false;
},
onSelectChange() {
this.apiPriceResult.status = -1;
this.itemInfo.value = '';
let priceData = ItemHelper.getInstance().getLocalPriceData();
let update = (data) => {
this.itemInfo.name = this.itemSelected;
this.itemInfo.price = '$' + toThousands(data[this.itemNameMap[this.itemSelected]].price);
this.itemInfo.id = this.itemNameMap[this.itemSelected];
};
if (priceData.data) {
update(priceData.data);
} else {
window.setTimeout(async () => {
let data = await priceData.promise;
update(data);
}, 0);
}
ItemHelper.getInstance().getItemData(this.itemSelected).then((data) => {
this.itemInfo.value = data.itemValue;
});
},
moneyFormat(input) {
return '$' + toThousands(input);
},
},
mounted() {
let itemName = ItemHelper.getInstance().getItemNameMap();
if (itemName.data) {
this.itemNameMap = itemName.data;
this.optDefault = '-';
} else {
window.setTimeout(async () => {
this.itemNameMap = await itemName.promise;
this.optDefault = '-';
}, 0);
}
}
},
}
</script>