steam商店中的价格转换为人民币
// ==UserScript== // @name steam价格转换 // @namespace https://github.com/marioplus/steam-price-converter // @version 2.5.4 // @author marioplus // @description steam商店中的价格转换为人民币 // @license GPL-3.0-or-later // @icon https://vitejs.dev/logo.svg // @homepage https://github.com/marioplus // @match https://store.steampowered.com/* // @match https://steamcommunity.com/* // @match https://checkout.steampowered.com/checkout/* // @require https://cdn.jsdelivr.net/npm/[email protected]/mdui.global.min.js // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.global.prod.js // @resource mdui/mdui.css https://cdn.jsdelivr.net/npm/[email protected]/mdui.css // @connect api.augmentedsteam.com // @connect store.steampowered.com // @connect cdn.jsdelivr.net // @grant GM_addStyle // @grant GM_addValueChangeListener // @grant GM_cookie // @grant GM_deleteValue // @grant GM_getResourceText // @grant GM_getValue // @grant GM_info // @grant GM_registerMenuCommand // @grant GM_setValue // @grant GM_xmlhttpRequest // @grant unsafeWindow // ==/UserScript== (e=>{if(typeof GM_addStyle=="function"){GM_addStyle(e);return}const t=document.createElement("style");t.textContent=e,document.head.append(t)})(" div.home_tabs_content .tab_item_discount{width:185px}.search_r###lt_row .col.search_name{width:240px}.search_r###lt_row .col.search_released{width:105px}.search_r###lt_row .col.search_discount_and_price .discount_block{width:165px}#popularItemsTable .market_listing_table_header .market_listing_their_price{width:130px}#popularItemsTable .market_listing_row_link>.market_listing_row .market_listing_right_cell.market_listing_their_price{width:130px}.setting-item[data-v-34f99f99]{color:rgb(var(--mdui-color-on-surface));padding:1em .75em;min-width:33em}.setting-item-title[data-v-34f99f99]{display:flex;align-items:center;padding:8px 0}.setting-item-title label[data-v-34f99f99]{display:inline-block;padding-right:.4em;font-size:var(--mdui-typescale-body-large-size);font-weight:var(--mdui-typescale-body-medium-weight);letter-spacing:var(--mdui-typescale-body-medium-tracking);line-height:var(--mdui-typescale-body-medium-line-height)}.setting-item-title .setting-region-title-icon[data-v-34f99f99]{font-size:var(--mdui-typescale-body-large-size)}mdui-select[data-v-34f99f99]::part(menu){max-height:256px;overflow:auto}mdui-text-field[data-v-34f99f99]::part(suffix){color:#00f} "); (function (mdui, vue) { 'use strict'; var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => { __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; const cssLoader = (e) => { const t = GM_getResourceText(e); return GM_addStyle(t), t; }; cssLoader("mdui/mdui.css"); const STORAGE_KEY_PREFIX = "Storage:"; const STORAGE_KEY_RATE_CACHES = STORAGE_KEY_PREFIX + "RateCache"; const STORAGE_KEY_SETTING = STORAGE_KEY_PREFIX + "Setting"; const IM_MENU_SETTING = "设置"; class Attrs { } __publicField(Attrs, "STATUS_KEY", "data-spc-status"); __publicField(Attrs, "STATUS_CONVERTED", "converted"); class AbstractConverter { /** * 匹配到的元素,是否匹配这个 exchanger * @param elementSnap 选择器选择到的元素快照 */ match(elementSnap) { if (!elementSnap || !elementSnap.element) { return false; } const status = elementSnap.element.getAttribute(Attrs.STATUS_KEY); const converted = Attrs.STATUS_CONVERTED === status; if (converted) { return false; } const content = elementSnap.textContext; if (!content) { return false; } if (content.match(/\d/) === null) { return false; } if (/^[,.\d\s]+$/.test(content)) { return false; } const parent = elementSnap.element.parentElement; if (!parent) { return false; } for (const selector of this.getCssSelectors()) { const element = parent.querySelector(selector); if (element && element === elementSnap.element) { elementSnap.selector = selector; return true; } } return false; } /** * 替换之后的操作 * @param elementSnap 选择器选择到的元素快照 */ // @ts-ignore afterConvert(elementSnap) { elementSnap.element.setAttribute(Attrs.STATUS_KEY, Attrs.STATUS_CONVERTED); } } class Setting { constructor() { /** * 目标国家代码,默认CN */ __publicField(this, "countyCode", "CN"); /** * 目标货币符号,默认 ¥ */ __publicField(this, "currencySymbol", "¥"); /** * 符号位置在首 */ __publicField(this, "currencySymbolBeforeValue", true); /** * 汇率获取时间,默认1小时 */ __publicField(this, "rateCacheExpired", 1e3 * 60 * 60); /** * 使用自定义汇率 */ __publicField(this, "useCustomRate", false); /** * 自定义汇率 */ __publicField(this, "customRate", 1); /** * 前一个版本 */ __publicField(this, "oldVersion", "0.0.0"); /** * 当前版本 */ __publicField(this, "currVersion", "0.0.0"); /** * 日志级别 */ __publicField(this, "logLevel", "info"); } } const countyObjs = [ { code: "AE", currencyCode: "AED", name: "阿联酋", nameEn: "United Arab Emirates" }, { code: "AU", currencyCode: "AUD", name: "#大利亚", nameEn: "Australia" }, { code: "BR", currencyCode: "BRL", name: "巴西", nameEn: "Brazil" }, { code: "CA", currencyCode: "CAD", name: "加拿大", nameEn: "Canada" }, { code: "CH", currencyCode: "CHF", name: "瑞士", nameEn: "Switzerland" }, { code: "CL", currencyCode: "CLP", name: "智利", nameEn: "Chile" }, { code: "CN", currencyCode: "CNY", name: "##", nameEn: "#####" }, { code: "CO", currencyCode: "COP", name: "哥伦比亚", nameEn: "Colombia" }, { code: "CR", currencyCode: "CRC", name: "哥斯达黎加", nameEn: "Costa Rica" }, { code: "AD", currencyCode: "EUR", name: "安道尔", nameEn: "Andorra" }, { code: "DE", currencyCode: "EUR", name: "德国", nameEn: "Germany" }, { code: "EE", currencyCode: "EUR", name: "#沙#亚", nameEn: "Estonia" }, { code: "ES", currencyCode: "EUR", name: "西班牙", nameEn: "Spain" }, { code: "FR", currencyCode: "EUR", name: "法国", nameEn: "France" }, { code: "IT", currencyCode: "EUR", name: "意大利", nameEn: "Italy" }, { code: "LT", currencyCode: "EUR", name: "立陶宛", nameEn: "Lithuania" }, { code: "LU", currencyCode: "EUR", name: "卢森堡", nameEn: "Luxembourg" }, { code: "LV", currencyCode: "EUR", name: "拉脱维亚", nameEn: "Latvia" }, { code: "MC", currencyCode: "EUR", name: "摩#哥", nameEn: "Monaco" }, { code: "ME", currencyCode: "EUR", name: "黑山", nameEn: "Montenegro" }, { code: "SI", currencyCode: "EUR", name: "斯洛文#亚", nameEn: "Slovenia" }, { code: "SK", currencyCode: "EUR", name: "斯洛伐克", nameEn: "Slovakia" }, { code: "SM", currencyCode: "EUR", name: "圣马力诺", nameEn: "San Marino" }, { code: "VA", currencyCode: "EUR", name: "梵蒂冈", nameEn: "Holy See" }, { code: "GB", currencyCode: "GBP", name: "英国", nameEn: "United Kingdom" }, { code: "HK", currencyCode: "HKD", name: "####", nameEn: "#### Kong" }, { code: "ID", currencyCode: "IDR", name: "印度#西亚", nameEn: "Indonesia" }, { code: "IL", currencyCode: "ILS", name: "以色列", nameEn: "Israel" }, { code: "IN", currencyCode: "INR", name: "印度", nameEn: "India" }, { code: "JP", currencyCode: "JPY", name: "日本", nameEn: "Japan" }, { code: "KR", currencyCode: "KRW", name: "韩国", nameEn: "South Korea" }, { code: "KW", currencyCode: "KWD", name: "科#特", nameEn: "Kuwait" }, { code: "KZ", currencyCode: "KZT", name: "哈萨克斯#", nameEn: "Kazakhstan" }, { code: "MX", currencyCode: "MXN", name: "墨西哥", nameEn: "Mexico" }, { code: "MY", currencyCode: "MYR", name: "马来西亚", nameEn: "Malaysia" }, { code: "NO", currencyCode: "NOK", name: "挪#", nameEn: "Norway" }, { code: "NZ", currencyCode: "NZD", name: "新西兰", nameEn: "New Zealand" }, { code: "PE", currencyCode: "PEN", name: "秘鲁", nameEn: "Peru" }, { code: "PH", currencyCode: "PHP", name: "菲律宾", nameEn: "Philippines" }, { code: "PL", currencyCode: "PLN", name: "波兰", nameEn: "Poland" }, { code: "QA", currencyCode: "QAR", name: "卡塔尔", nameEn: "Qatar" }, { code: "RU", currencyCode: "RUB", name: "俄罗斯", nameEn: "Russia" }, { code: "SA", currencyCode: "SAR", name: "沙特阿拉伯", nameEn: "Saudi Arabia" }, { code: "SG", currencyCode: "SGD", name: "新加坡", nameEn: "Singapore" }, { code: "TH", currencyCode: "THB", name: "泰国", nameEn: "Thailand" }, { code: "TW", currencyCode: "TWD", name: "####", nameEn: "######" }, { code: "UA", currencyCode: "UAH", name: "乌克兰", nameEn: "Ukraine" }, { code: "AR", currencyCode: "USD", name: "阿根廷", nameEn: "Argentina" }, { code: "AZ", currencyCode: "USD", name: "阿塞拜疆", nameEn: "Azerbaijan" }, { code: "PK", currencyCode: "USD", name: "巴基斯#", nameEn: "Pakistan" }, { code: "TR", currencyCode: "USD", name: "土耳其", nameEn: "Turkey" }, { code: "US", currencyCode: "USD", name: "美国", nameEn: "United States" }, { code: "ZA", currencyCode: "USD", name: "南非", nameEn: "South Africa" }, { code: "UY", currencyCode: "UYU", name: "乌拉圭", nameEn: "Uruguay" }, { code: "VN", currencyCode: "VND", name: "越南", nameEn: "Vietnam" } ]; class Jsons { /** * 将对象转换为普通 JSON 对象 */ static toJson(obj) { return { ...obj }; } /** * 将对象转换为 JSON 字符串 */ static toString(obj) { return JSON.stringify(this.toJson(obj)); } /** * 将普通 JSON 对象解析为指定类型,支持嵌套处理,包括 Map 和 Record 中的 class */ static readJson(json, cls) { if (!cls) { if (typeof json !== "object" || json === null) { throw new Error("Invalid JSON input"); } return json; } const instance = new cls(); if (instance instanceof Map) { return this.handleMap(json, instance); } for (const key of Reflect.ownKeys(json)) { const value = json[key]; if (value === null || value === void 0) { instance[key] = value; continue; } const fieldValue = instance[key]; if (fieldValue !== null && typeof fieldValue === "object" && !(fieldValue instanceof Array)) { if (fieldValue instanceof Map) { instance[key] = this.handleMap(value, fieldValue); } else if (fieldValue instanceof Object) { instance[key] = this.readJson(value, fieldValue.constructor); } } else if (Array.isArray(fieldValue)) { instance[key] = value.map( (item) => { var _a; return typeof item === "object" ? this.readJson(item, (_a = fieldValue[0]) == null ? void 0 : _a.constructor) : item; } ); } else { instance[key] = value; } } return instance; } /** * 处理 Map 类型的转换,其中 V 可以是一个 class */ static handleMap(value, mapInstance) { const map = /* @__PURE__ */ new Map(); if (value && typeof value === "object") { for (const key of Object.keys(value)) { const mapValue = value[key]; if (mapValue === null || mapValue === void 0) { map.set(key, mapValue); continue; } const existingValue = mapInstance.get(key); if (this.isObject(mapValue) && existingValue) { map.set(key, this.readJson(mapValue, existingValue.constructor)); } else { map.set(key, mapValue); } } } return map; } static isObject(value) { return value !== null && typeof value === "object"; } /** * 将 JSON 字符串解析为指定类型,支持嵌套处理,包括 Map 和 Record 中的 class */ static readString(jsonString, cls) { const json = JSON.parse(jsonString); return this.readJson(json, cls); } } const countyInfos = Jsons.readJson(countyObjs, Array); const countyCode2Info = new Map(countyInfos.map((v) => [v.code, v])); var _GM_addValueChangeListener = /* @__PURE__ */ (() => typeof GM_addValueChangeListener != "undefined" ? GM_addValueChangeListener : void 0)(); var _GM_cookie = /* @__PURE__ */ (() => typeof GM_cookie != "undefined" ? GM_cookie : void 0)(); var _GM_deleteValue = /* @__PURE__ */ (() => typeof GM_deleteValue != "undefined" ? GM_deleteValue : void 0)(); var _GM_getValue = /* @__PURE__ */ (() => typeof GM_getValue != "undefined" ? GM_getValue : void 0)(); var _GM_info = /* @__PURE__ */ (() => typeof GM_info != "undefined" ? GM_info : void 0)(); var _GM_registerMenuCommand = /* @__PURE__ */ (() => typeof GM_registerMenuCommand != "undefined" ? GM_registerMenuCommand : void 0)(); var _GM_setValue = /* @__PURE__ */ (() => typeof GM_setValue != "undefined" ? GM_setValue : void 0)(); var _GM_xmlhttpRequest = /* @__PURE__ */ (() => typeof GM_xmlhttpRequest != "undefined" ? GM_xmlhttpRequest : void 0)(); var _unsafeWindow = /* @__PURE__ */ (() => typeof unsafeWindow != "undefined" ? unsafeWindow : void 0)(); function initializeLogTitle() { const isInIFrame = window.parent !== window || window.frames.length > 0; return isInIFrame ? `steam-price-convertor iframe(${(/* @__PURE__ */ new Date()).getMilliseconds()})` : "steam-price-convertor"; } function createLogStyle(color) { return ` background: ${color}; color: white; padding: 1px 3px; border-radius: 2px; `; } function composeLogHint() { return `%c${initializeLogTitle()}`; } const LogDefinitions = { debug: { index: 0, color: "#009688", label: "debug", bindMethod: console.info }, info: { index: 1, color: "#2196f3", label: "info", bindMethod: console.info }, warn: { index: 2, color: "#ffc107", label: "warn", bindMethod: console.warn }, error: { index: 3, color: "#e91e63", label: "error", bindMethod: console.error }, off: { index: 4, color: "", label: "off", bindMethod: () => { } } }; const Logger = { debug: noopLog.bind(null), info: noopLog.bind(null), warn: noopLog.bind(null), error: noopLog.bind(null) }; function noopLog() { } let currLogLevel = LogDefinitions.info; function refreshBinding() { const hint = composeLogHint(); Object.entries(LogDefinitions).forEach(([label, def]) => { if (def.index >= currLogLevel.index) { const logStyle = createLogStyle(def.color); Logger[label.toLowerCase()] = def.bindMethod.bind(console, hint, logStyle); } else { Logger[label.toLowerCase()] = noopLog.bind(null); } }); } function setLogLevel(levelLabel) { const newLevel = LogDefinitions[levelLabel]; if (newLevel) { currLogLevel = newLevel; refreshBinding(); } else { console.error(`Invalid log level: ${levelLabel}`); } } refreshBinding(); class Strings { static format(format, ...args) { args = args || []; let message = format; for (let arg of args) { message = message.replace("%s", arg); } return message; } } class GmUtils { static getValue(cls, key, defaultValue) { const value = _GM_getValue(key); return value ? Jsons.readString(value, cls) : defaultValue; } static setValue(key, value) { _GM_setValue(key, Jsons.toString(value)); } static deleteValue(key) { _GM_deleteValue(key); } static registerMenuCommand(caption, onClick, accessKey) { const key = `GM_registerMenuCommand@${caption}`; _GM_registerMenuCommand( caption, (event) => { this.setValue(key, true); Logger.debug("点击菜单:" + caption); this.setValue(key, false); if (onClick) { onClick(event); } }, accessKey ); } static addMenuClickEventListener(caption, onClick) { const key = `GM_registerMenuCommand@${caption}`; _GM_addValueChangeListener(key, (name, oldValue, newValue) => { if (newValue) { onClick(); } }); } } const _SettingManager = class _SettingManager { constructor() { __publicField(this, "setting"); this.setting = this.loadSetting(); } loadSetting() { const setting = GmUtils.getValue(Setting, STORAGE_KEY_SETTING, new Setting()); setting.oldVersion = setting.currVersion; setting.currVersion = _GM_info.script.version; if (setting.oldVersion === setting.currVersion) { Logger.info("读取设置", setting); } else { Logger.debug(Strings.format(`版本更新重置设置:%s -> %s`, setting.oldVersion, setting.currVersion)); this.saveSetting(setting); } return setting; } /** * 保存设置 * @param setting 设置 */ saveSetting(setting) { Logger.info("保存设置", setting); this.setting = setting; GmUtils.setValue(STORAGE_KEY_SETTING, setting); } setCountyCode(countyCode) { const county = countyCode2Info.get(countyCode); if (!county) { throw Error(`国家代码不存在:${countyCode}`); } this.setting.countyCode = countyCode; this.saveSetting(this.setting); } setCurrencySymbol(currencySymbol) { this.setting.currencySymbol = currencySymbol; this.saveSetting(this.setting); } setCurrencySymbolBeforeValue(isCurrencySymbolBeforeValue) { this.setting.currencySymbolBeforeValue = isCurrencySymbolBeforeValue; this.saveSetting(this.setting); } reset() { this.saveSetting(new Setting()); } setRateCacheExpired(rateCacheExpired) { this.setting.rateCacheExpired = rateCacheExpired; this.saveSetting(this.setting); } setUseCustomRate(isUseCustomRate) { this.setting.useCustomRate = isUseCustomRate; this.saveSetting(this.setting); } setCustomRate(customRate) { this.setting.customRate = customRate; this.saveSetting(this.setting); } }; __publicField(_SettingManager, "instance", new _SettingManager()); let SettingManager = _SettingManager; function parsePrice(content) { let priceStr = content.replace(/руб\./g, "").replace(/pуб\./g, "").replace(/\s/g, "").replace(/^[^0-9]+/, "").replace(/[^0-9,.]+$/, "").replace(/,(?=\d\d\d)/g, ""); const numberStr = priceStr.replace(/\D/g, ""); let price = Number.parseInt(numberStr) ?? 0; if (priceStr.match(/\D/)) { price = price / 100; } return price; } function convertPrice(price, rate) { return Number.parseFloat((price / rate).toFixed(2)); } function convertPriceContent(originalContent, rate) { const safeContent = originalContent.trim().replaceAll(/\(.+$/g, "").trim(); const price = parsePrice(safeContent); const convertedPrice = convertPrice(price, rate); const setting = SettingManager.instance.setting; let finalContent = setting.currencySymbolBeforeValue ? `${safeContent}(${setting.currencySymbol}${convertedPrice})` : `${safeContent}(${convertedPrice}${setting.currencySymbol})`; const message = `转换前文本:${safeContent}; 提取到的价格:${price}; 转换后的价格:${convertedPrice}; 转换后文本:${finalContent}`; Logger.debug(message); return finalContent; } class ElementConverter extends AbstractConverter { getCssSelectors() { const home = [ // 大图 ".discount_prices > .discount_final_price" ]; const category = [ // 原价 ".Wh0L8EnwsPV_8VAu8TOYr", "._3j4dI1yA7cRfCvK8h406OB", // 折扣价 "._1EKGZBnKFWOr3RqVdnLMRN", "._3fFFsvII7Y2KXNLDk_krOW" ]; const account = [ "div.accountData.price a" ]; const wishlist = [ // 右上角钱包 "div.Hxi-pnf9Xlw- > div._79DIT7RUQ5g-", // 当前价格 "div.ME2eMO7C1Tk- > div.DOnsaVcV0Is-", // 原价 "div.ME2eMO7C1Tk- > div.ywNldZ-YzEE-" ]; const inventory = [ '#iteminfo1_item_market_actions div[id^="market_item_action_buyback_at_price_"]' ]; const cart = [ // 原价 ".Panel.Focusable ._3-o3G9jt3lqcvbRXt8epsn.StoreOriginalPrice", // 折扣价 ".Panel.Focusable .pk-LoKoNmmPK4GBiC9DR8", // 总额 "._2WLaY5TxjBGVyuWe_6KS3N" ]; const cartCheckout = [ // 列表 "#checkout_review_cart_area .checkout_review_item_price > .price", // 小计 "#review_subtotal_value.price", // 合计 "#review_total_value.price" ]; const selectors = [ // 商店 // 首页 ".discount_original_price", ".discount_final_price", ".col.search_price.responsive_secondrow strike", // 头像旁边 "#header_wallet_balance > span.tooltip", // 愿望单总价值 ".esi-wishlist-stat > .num", // 新版卡片 ".salepreviewwidgets_StoreOriginalPrice_1EKGZ", ".salepreviewwidgets_StoreSalePriceBox_Wh0L8", // 分类查看游戏 ".contenthubshared_OriginalPrice_3hBh3", ".contenthubshared_FinalPrice_F_tGv", ".salepreviewwidgets_StoreSalePriceBox_Wh0L8:not(.salepreviewwidgets_StoreSalePrepurchaseLabel_Wxeyn)", // 市场 // 总余额 "#marketWalletBalanceAmount", // 列表 "span.normal_price[data-price]", "span.sale_price", // 求购、求售统计 ".market_commodity_orders_header_promote:nth-child(even)", // 求购、求售列表 ".market_commodity_orders_table td:nth-child(odd)", // 详情列表 ".market_table_value > span", ".jqplot-highlighter-tooltip", // 消费记录 "tr.wallet_table_row > td.wht_total", "tr.wallet_table_row > td.wht_wallet_change.wallet_column", "tr.wallet_table_row > td.wht_wallet_balance.wallet_column", // 捆绑包 ".package_totals_row > .price:not(.bundle_discount)", "#package_savings_bar > .savings.bundle_savings", // 低于xxx 分类标题 ".home_page_content_title a.btn_small_tall > span" ]; selectors.push(...home); selectors.push(...category); selectors.push(...account); selectors.push(...wishlist); selectors.push(...inventory); selectors.push(...cart); selectors.push(...cartCheckout); return selectors; } convert(elementSnap, rate) { elementSnap.element.textContent = convertPriceContent(elementSnap.textContext, rate); return true; } } class TextNodeConverter extends AbstractConverter { constructor() { super(...arguments); // @ts-ignore __publicField(this, "parseFirstChildTextNodeFn", (el) => el.firstChild); // 购物车 __publicField(this, "cart", /* @__PURE__ */ new Map([ // 卡牌获取进度 [ ".Panel.Focusable ._18eO4-XadW5jmTpgdATkSz", [(el) => el.childNodes[1]] ] ])); // @ts-ignore __publicField(this, "targets", new Map([ [ ".col.search_price.responsive_secondrow", [ // @ts-ignore (el) => el.firstChild.nextSibling.nextSibling.nextSibling, this.parseFirstChildTextNodeFn ] ], ["#header_wallet_balance", [this.parseFirstChildTextNodeFn]], // iframe [".game_purchase_price.price", [this.parseFirstChildTextNodeFn]], // 低于xxx 分类标题 [".home_page_content_title", [this.parseFirstChildTextNodeFn]], // dlc 中没有折扣 [".game_area_dlc_row > .game_area_dlc_price", [ (el) => el, this.parseFirstChildTextNodeFn ]], ...this.cart ])); } getCssSelectors() { return [...this.targets.keys()]; } convert(elementSnap, rate) { const selector = elementSnap.selector; this.targets.get(selector); const parseNodeFns = this.targets.get(selector) || []; if (!parseNodeFns) { return false; } const textNode = this.safeParseNode(selector, elementSnap.element, parseNodeFns); if (!textNode) { return false; } const content = textNode.nodeValue; if (!content || content.trim().length === 0) { return false; } textNode.nodeValue = convertPriceContent(content, rate); return true; } safeParseNode(selector, el, fns) { for (let fn of fns) { try { const node = fn(el); if (node.nodeName === "#text" && node.nodeValue && node.nodeValue.length > 0) { return node; } } catch (e) { console.debug("获取文本节点失败,但不确定该节点是否一定会出现。selector:" + selector); } } return null; } } const _ConverterManager = class _ConverterManager { constructor() { __publicField(this, "converters"); this.converters = [ new ElementConverter(), new TextNodeConverter() ]; } getSelector() { return this.converters.map((exchanger) => exchanger.getCssSelectors()).flat(1).join(", "); } convert(elements, rate) { if (!elements) { return; } elements.forEach((element) => { const elementSnap = { element, textContext: element.textContent, classList: element.classList, attributes: element.attributes }; this.converters.filter((converter) => converter.match(elementSnap)).forEach((converter) => { try { const exchanged = converter.convert(elementSnap, rate); if (exchanged) { converter.afterConvert(elementSnap); } } catch (e) { console.group("转换失败"); console.error(e); console.error("转换失败请将下列内容反馈给开发者,右键 > 复制(copy) > 复制元素(copy element)"); console.error("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); console.error(element); console.error("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); console.groupEnd(); } }); }); } }; __publicField(_ConverterManager, "instance", new _ConverterManager()); let ConverterManager = _ConverterManager; class RateCache { constructor(from, to, rate, createdAt) { __publicField(this, "from"); __publicField(this, "to"); __publicField(this, "createdAt"); __publicField(this, "rate"); this.from = from; this.to = to; this.createdAt = createdAt || 0; this.rate = rate || 0; } } class RateCaches { constructor() { __publicField(this, "caches", /* @__PURE__ */ new Map()); } getCache(from, to) { return this.caches.get(this.buildCacheKey(from, to)); } setCache(cache) { this.caches.set(this.buildCacheKey(cache.from, cache.to), cache); } buildCacheKey(from, to) { return `${from}:${to}`; } } class Http { static get(cls, url, details) { if (!details) { details = { url }; } details.method = "GET"; return this.request(cls, details); } static post(url, cls, details) { if (!details) { details = { url }; } details.method = "POST"; return this.request(cls, details); } static request(cls, details) { return new Promise((resolve, reject) => { details.onload = (response) => { if (cls.name === String.name) { resolve(response.response); } else { const json = JSON.parse(response.response); resolve(Jsons.readJson(json, cls)); } }; details.onerror = (error) => reject(error); _GM_xmlhttpRequest(details); }); } } class SpcContext { constructor(setting, targetCountyInfo, targetCountyCode) { __publicField(this, "_setting"); __publicField(this, "_targetCountyInfo"); __publicField(this, "_currentCountyInfo"); this._setting = setting; this._targetCountyInfo = targetCountyInfo; this._currentCountyInfo = targetCountyCode; } static getContext() { return _unsafeWindow.spcContext; } get setting() { return this._setting; } get targetCountyInfo() { return this._targetCountyInfo; } get currentCountyInfo() { return this._currentCountyInfo; } } class AugmentedSteamRateApi { getName() { return "AugmentedSteamRateApi"; } async getRate() { const context = SpcContext.getContext(); Logger.info(Strings.format( "通过 AugmentedSteam 获取汇率 %s(%s) -> %s(%s)...", context.currentCountyInfo.currencyCode, context.currentCountyInfo.name, context.targetCountyInfo.currencyCode, context.targetCountyInfo.name )); const url = `https://api.augmentedsteam.com/rates/v1?to=${context.currentCountyInfo.currencyCode}`; let rate = await Http.get(Map, url).then((res) => res.get(context.targetCountyInfo.currencyCode)[context.currentCountyInfo.currencyCode]).catch((err) => Logger.error("通过 AugmentedSteam 获取汇率失败", err)); if (rate) { return rate; } throw new Error(`通过 ${this.getName()} 获取汇率失败。`); } } const _RateManager = class _RateManager { constructor() { __publicField(this, "rateApis"); __publicField(this, "rateCaches", new RateCaches()); this.rateApis = [ new AugmentedSteamRateApi() ]; } getName() { return "RateManager"; } async getRate4Remote() { Logger.info("远程获取汇率..."); let rate; for (let rateApi of this.rateApis) { try { rate = await rateApi.getRate(); } catch (e) { Logger.error(`使用实现(${rateApi.getName()})获取汇率失败`); } if (rate) { return rate; } } throw Error("所有汇率获取实现获取汇率均失败"); } async getRate() { const context = SpcContext.getContext(); if (context.setting.useCustomRate) { Logger.info("使用自定义汇率"); return context.setting.customRate; } this.rateCaches = this.loadRateCache(); let cache = this.rateCaches.getCache(context.currentCountyInfo.code, context.targetCountyInfo.code); const now = (/* @__PURE__ */ new Date()).getTime(); const expired = context.setting.rateCacheExpired; if (!cache || !cache.rate || now > cache.createdAt + expired) { Logger.info(`本地缓存已过期`); cache = new RateCache(context.currentCountyInfo.code, context.targetCountyInfo.code); cache.rate = await this.getRate4Remote(); cache.createdAt = (/* @__PURE__ */ new Date()).getTime(); this.rateCaches.setCache(cache); this.saveRateCache(); } return cache.rate; } loadRateCache() { const setting = SpcContext.getContext().setting; if (setting.oldVersion !== setting.currVersion) { Logger.info(`脚本版本发生变化需要刷新汇率缓存`); this.clear(); return new RateCaches(); } Logger.info(`读取汇率缓存`); return GmUtils.getValue(RateCaches, STORAGE_KEY_RATE_CACHES, new RateCaches()); } saveRateCache() { Logger.info("保存汇率缓存", this.rateCaches); GmUtils.setValue(STORAGE_KEY_RATE_CACHES, this.rateCaches); } clear() { GmUtils.deleteValue(STORAGE_KEY_RATE_CACHES); } }; __publicField(_RateManager, "instance", new _RateManager()); let RateManager = _RateManager; async function main() { const context = SpcContext.getContext(); if (context.currentCountyInfo.code === context.targetCountyInfo.code) { Logger.info(`${context.currentCountyInfo.name}无需转换`); return; } const rate = await RateManager.instance.getRate(); if (!rate) { throw Error("获取汇率失败"); } Logger.info(Strings.format(`汇率 %s -> %s:%s`, context.currentCountyInfo.currencyCode, context.targetCountyInfo.currencyCode, rate)); await convert(rate); } async function convert(rate) { const exchangerManager = ConverterManager.instance; const elements = document.querySelectorAll(exchangerManager.getSelector()); exchangerManager.convert(elements, rate); const selector = exchangerManager.getSelector(); const priceObserver = new MutationObserver((mutations) => { mutations.forEach((mutation) => { const target = mutation.target; let priceEls = target.querySelectorAll(selector); if (!priceEls || priceEls.length === 0) { return; } exchangerManager.convert(priceEls, rate); }); }); priceObserver.observe(document.body, { childList: true, subtree: true }); } const _withScopeId = (n) => (vue.pushScopeId("data-v-34f99f99"), n = n(), vue.popScopeId(), n); const _hoisted_1 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("link", { href: "https://fonts.googleapis.com/icon?family=Material+Icons", rel: "stylesheet" }, null, -1)); const _hoisted_2 = { "close-on-overlay-click": "", class: "setting" }; const _hoisted_3 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("span", { slot: "headline" }, "设置", -1)); const _hoisted_4 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("span", { slot: "description" }, "随心所欲设置 steam-price-converter", -1)); const _hoisted_5 = { class: "setting-item" }; const _hoisted_6 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "setting-item-title" }, [ /* @__PURE__ */ vue.createElementVNode("label", null, "目标区域"), /* @__PURE__ */ vue.createElementVNode("mdui-tooltip", { content: "将价格转换为此区域的货币", placement: "right" }, [ /* @__PURE__ */ vue.createElementVNode("mdui-icon", { name: "error", class: "setting-region-title-icon" }) ]) ], -1)); const _hoisted_7 = { class: "setting-item-content" }; const _hoisted_8 = ["value"]; const _hoisted_9 = ["value"]; const _hoisted_10 = { class: "setting-item" }; const _hoisted_11 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "setting-item-title" }, [ /* @__PURE__ */ vue.createElementVNode("label", null, "货币符号"), /* @__PURE__ */ vue.createElementVNode("mdui-tooltip", { content: "转换后的价格的货币符号", placement: "right" }, [ /* @__PURE__ */ vue.createElementVNode("mdui-icon", { name: "error", class: "setting-region-title-icon" }) ]) ], -1)); const _hoisted_12 = { class: "setting-item-content" }; const _hoisted_13 = ["value"]; const _hoisted_14 = { class: "setting-item" }; const _hoisted_15 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "setting-item-title" }, [ /* @__PURE__ */ vue.createElementVNode("label", null, "货币符号展示位置"), /* @__PURE__ */ vue.createElementVNode("mdui-tooltip", { content: "控制转换后的价格货币符号的位置", placement: "right" }, [ /* @__PURE__ */ vue.createElementVNode("mdui-icon", { name: "error", class: "setting-region-title-icon" }) ]) ], -1)); const _hoisted_16 = { class: "setting-item-content" }; const _hoisted_17 = ["value"]; const _hoisted_18 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("mdui-menu-item", { value: "true" }, "价格之前", -1)); const _hoisted_19 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("mdui-menu-item", { value: "false" }, "价格之后", -1)); const _hoisted_20 = [ _hoisted_18, _hoisted_19 ]; const _hoisted_21 = { class: "setting-item" }; const _hoisted_22 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "setting-item-title" }, [ /* @__PURE__ */ vue.createElementVNode("label", null, "汇率缓存有效期"), /* @__PURE__ */ vue.createElementVNode("mdui-tooltip", { content: "获取汇率后进行缓存,在此时间内将使用缓存汇率", placement: "right" }, [ /* @__PURE__ */ vue.createElementVNode("mdui-icon", { name: "error", class: "setting-region-title-icon" }) ]) ], -1)); const _hoisted_23 = { class: "setting-item-content" }; const _hoisted_24 = ["value"]; const _hoisted_25 = { class: "setting-item" }; const _hoisted_26 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "setting-item-title" }, [ /* @__PURE__ */ vue.createElementVNode("label", null, "使用自定义汇率"), /* @__PURE__ */ vue.createElementVNode("mdui-tooltip", { content: "使用自定义汇率进行价格转换,不再获取区域,不再根据区域获取汇率", placement: "right" }, [ /* @__PURE__ */ vue.createElementVNode("mdui-icon", { name: "error", class: "setting-region-title-icon" }) ]) ], -1)); const _hoisted_27 = { class: "setting-item-content" }; const _hoisted_28 = ["value"]; const _hoisted_29 = { class: "setting-item" }; const _hoisted_30 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "setting-item-title" }, [ /* @__PURE__ */ vue.createElementVNode("label", null, "自定义汇率"), /* @__PURE__ */ vue.createElementVNode("mdui-tooltip", { content: "开启“使用自定义汇率”后使用此汇率进行转换", placement: "right" }, [ /* @__PURE__ */ vue.createElementVNode("mdui-icon", { name: "error", class: "setting-region-title-icon" }) ]) ], -1)); const _hoisted_31 = { class: "setting-item-content" }; const _hoisted_32 = ["value"]; const _hoisted_33 = { class: "setting-item" }; const _hoisted_34 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "setting-item-title" }, [ /* @__PURE__ */ vue.createElementVNode("label", null, "日志等级"), /* @__PURE__ */ vue.createElementVNode("mdui-tooltip", { content: "日志等级 debug > info > warn > error > off", placement: "right" }, [ /* @__PURE__ */ vue.createElementVNode("mdui-icon", { name: "error", class: "setting-region-title-icon" }) ]) ], -1)); const _hoisted_35 = { class: "setting-item-content" }; const _hoisted_36 = ["value"]; const _hoisted_37 = ["value"]; const _hoisted_38 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("mdui-button", { class: "setting-btn-canal", slot: "action", variant: "text" }, "取消", -1)); const _hoisted_39 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("mdui-button", { class: "setting-btn-save", slot: "action", variant: "tonal" }, "保存", -1)); const _sfc_main = /* @__PURE__ */ vue.defineComponent({ __name: "App", setup(__props) { const vueCountyInfos = countyInfos; const setting = Object.assign({}, SettingManager.instance.setting); vue.onMounted(() => { var _a, _b; const dialog = document.querySelector(".setting"); (_a = dialog.querySelector(".setting-btn-canal")) == null ? void 0 : _a.addEventListener("click", () => dialog.open = false); (_b = dialog.querySelector(".setting-btn-save")) == null ? void 0 : _b.addEventListener("click", () => { SettingManager.instance.saveSetting(setting); dialog.open = false; }); GmUtils.addMenuClickEventListener(IM_MENU_SETTING, () => dialog.open = true); }); function getSelected(target) { var _a; return (_a = target.querySelector("*[selected]")) == null ? void 0 : _a.value; } return (_ctx, _cache) => { return vue.openBlock(), vue.createElementBlock(vue.Fragment, null, [ _hoisted_1, vue.createElementVNode("mdui-dialog", _hoisted_2, [ _hoisted_3, _hoisted_4, vue.createElementVNode("div", _hoisted_5, [ _hoisted_6, vue.createElementVNode("div", _hoisted_7, [ vue.createElementVNode("mdui-select", { value: vue.unref(setting).countyCode, placement: "bottom", onChange: _cache[0] || (_cache[0] = ($event) => vue.unref(setting).countyCode = getSelected($event.target)) }, [ (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(vue.unref(vueCountyInfos), (countyInfo) => { return vue.openBlock(), vue.createElementBlock("mdui-menu-item", { value: countyInfo.code }, vue.toDisplayString(countyInfo.name) + " (" + vue.toDisplayString(countyInfo.code) + ") ", 9, _hoisted_9); }), 256)) ], 40, _hoisted_8) ]) ]), vue.createElementVNode("div", _hoisted_10, [ _hoisted_11, vue.createElementVNode("div", _hoisted_12, [ vue.createElementVNode("mdui-text-field", { value: vue.unref(setting).currencySymbol, onChange: _cache[1] || (_cache[1] = ($event) => vue.unref(setting).currencySymbol = $event.target.value) }, null, 40, _hoisted_13) ]) ]), vue.createElementVNode("div", _hoisted_14, [ _hoisted_15, vue.createElementVNode("div", _hoisted_16, [ vue.createElementVNode("mdui-select", { value: vue.unref(setting).currencySymbolBeforeValue.toString(), placement: "bottom", onChange: _cache[2] || (_cache[2] = ($event) => vue.unref(setting).currencySymbolBeforeValue = getSelected($event.target) === "true") }, _hoisted_20, 40, _hoisted_17) ]) ]), vue.createElementVNode("div", _hoisted_21, [ _hoisted_22, vue.createElementVNode("div", _hoisted_23, [ vue.createElementVNode("mdui-text-field", { type: "number", value: vue.unref(setting).rateCacheExpired / (60 * 60 * 1e3), suffix: "h", onChange: _cache[3] || (_cache[3] = ($event) => vue.unref(setting).rateCacheExpired = $event.target.value * (60 * 60 * 1e3)) }, null, 40, _hoisted_24) ]) ]), vue.createElementVNode("div", _hoisted_25, [ _hoisted_26, vue.createElementVNode("div", _hoisted_27, [ vue.createElementVNode("mdui-switch", { slot: "end-icon", value: vue.unref(setting).useCustomRate, onChange: _cache[4] || (_cache[4] = ($event) => vue.unref(setting).useCustomRate = $event.target.checked) }, null, 40, _hoisted_28) ]) ]), vue.createElementVNode("div", _hoisted_29, [ _hoisted_30, vue.createElementVNode("div", _hoisted_31, [ vue.createElementVNode("mdui-text-field", { type: "number", value: vue.unref(setting).customRate, onChange: _cache[5] || (_cache[5] = ($event) => vue.unref(setting).customRate = $event.target.value) }, null, 40, _hoisted_32) ]) ]), vue.createElementVNode("div", _hoisted_33, [ _hoisted_34, vue.createElementVNode("div", _hoisted_35, [ vue.createElementVNode("mdui-select", { value: vue.unref(setting).logLevel, onChange: _cache[6] || (_cache[6] = ($event) => vue.unref(setting).logLevel = getSelected($event.target)) }, [ (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(vue.unref(LogDefinitions), (def) => { return vue.openBlock(), vue.createElementBlock("mdui-menu-item", { value: def.label }, vue.toDisplayString(def.label), 9, _hoisted_37); }), 256)) ], 40, _hoisted_36) ]) ]), _hoisted_38, _hoisted_39 ]) ], 64); }; } }); const _export_sfc = (sfc, props) => { const target = sfc.__vccOpts || sfc; for (const [key, val] of props) { target[key] = val; } return target; }; const App = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-34f99f99"]]); class CookieCountyInfoGetter { name() { return "cookie"; } match() { return true; } async getCountyCode() { return new Promise(async (resolve, reject) => { const cookies = await _GM_cookie.list({ name: "steamCountry" }); if (cookies && cookies.length > 0) { const match = cookies[0].value.match(/^[a-zA-Z][a-zA-Z]/); if (match) { resolve(match[0]); } } reject(); }); } } class RequestStorePageCountyCodeGetter { name() { return "请求商店页面"; } match() { return !window.location.href.includes("store.steampowered.com"); } async getCountyCode() { return new Promise(async (resolve, reject) => { try { const storeHtml = await Http.get(String, "https://store.steampowered.com/"); const match = storeHtml.match(new RegExp("(?<=GDynamicStore.Init\\(.+')[A-Z][A-Z](?=',)")); if (match) { return resolve(match[0]); } } catch (err) { Logger.error(err); } reject(); }); } } class StorePageCountyCodeGetter { name() { return "商店页面"; } match() { return window.location.href.includes("store.steampowered.com"); } getCountyCode() { return new Promise((resolve, reject) => { var _a, _b; try { let countyCode = (_b = (_a = GStoreItemData == null ? void 0 : GStoreItemData.rgNavParams) == null ? void 0 : _a.__page_default_obj) == null ? void 0 : _b.countrycode; if (countyCode) { resolve(countyCode); } } catch (e) { Logger.warn("读取商店页面区域代码变量失败: " + e.message); } document.querySelectorAll("script").forEach((scriptEl) => { const scriptInnerText = scriptEl.innerText; if (scriptInnerText.includes("$J( InitMiniprofileHovers );") || scriptInnerText.includes(`$J( InitMiniprofileHovers( 'https%3A%2F%2Fstore.steampowered.com%2F' ) );`)) { const matcher = new RegExp("(?<=')[A-Z]{2}(?!=')", "g"); const match = scriptInnerText.match(matcher); if (match) { const countyCode = match.toString(); resolve(countyCode); } } }); reject(); }); } } class MarketPageCountyCodeGetter { name() { return "市场页面"; } match() { return window.location.href.includes("steamcommunity.com"); } getCountyCode() { return new Promise((resolve, reject) => { try { const code = g_strCountryCode; if (code) return resolve(code); } catch (err) { Logger.error(err); } reject(); }); } } class UserConfigCountyInfoGetter { async getCountyCode() { return new Promise(async (resolve, reject) => { var _a; const code = _unsafeWindow.userConfig ? (_a = _unsafeWindow.userConfig) == null ? void 0 : _a.country_code : await this.getCountyCodeForDev(); if (code) { resolve(code); } else { reject(); } }); } match() { return true; } name() { return "window.UserConfig"; } async getCountyCodeForDev() { const html = await Http.get(String, window.location.href); const match = html.match(/,"country_code":"([A-Z]{2})"/); if (match) { return match[1]; } return void 0; } } const _CountyCodeGetterManager = class _CountyCodeGetterManager { constructor() { __publicField(this, "getters"); this.getters = [ new UserConfigCountyInfoGetter(), new StorePageCountyCodeGetter(), new MarketPageCountyCodeGetter(), new RequestStorePageCountyCodeGetter(), new CookieCountyInfoGetter() ]; } match() { return true; } async getCountyCode() { Logger.info("尝试获取区域代码"); for (let getter of this.getters) { if (!getter.match()) { continue; } Logger.debug(`尝试通过[${getter.name()}]获取区域代码`); try { const countyCode = await getter.getCountyCode(); Logger.info(`通过[${getter.name()}]获取区域代码成功`); return countyCode; } catch (e) { Logger.error(`通过[${getter.name()}]获取区域代码失败`); } } throw new Error("所有获取区域代码策略都获取失败"); } }; __publicField(_CountyCodeGetterManager, "instance", new _CountyCodeGetterManager()); let CountyCodeGetterManager = _CountyCodeGetterManager; const _SpcManager = class _SpcManager { constructor() { } setCountyCode(code) { SettingManager.instance.setCountyCode(code); } setCurrencySymbol(symbol) { SettingManager.instance.setCurrencySymbol(symbol); } setCurrencySymbolBeforeValue(isCurrencySymbolBeforeValue) { SettingManager.instance.setCurrencySymbolBeforeValue(isCurrencySymbolBeforeValue); } setRateCacheExpired(expired) { SettingManager.instance.setRateCacheExpired(expired); } resetSetting() { SettingManager.instance.reset(); } clearCache() { RateManager.instance.clear(); } setUseCustomRate(isUseCustomRate) { SettingManager.instance.setUseCustomRate(isUseCustomRate); } setCustomRate(customRate) { SettingManager.instance.setCustomRate(customRate); } }; __publicField(_SpcManager, "instance", new _SpcManager()); let SpcManager = _SpcManager; class ReactUtils { static waitForReactInit(callback, checkInterval = 500, timeout = 1e4) { return new Promise((resolve, reject) => { const start = Date.now(); const interval = setInterval(() => { const root = document.documentElement; for (const prop in root) { if (prop.startsWith("__react")) { clearInterval(interval); console.log(`React initialized with property: ${prop}`); Promise.resolve(callback(root, prop)).then(() => resolve()).catch(reject); return; } } if (Date.now() - start > timeout) { clearInterval(interval); reject(new Error("React initialization timeout exceeded.")); } }, checkInterval); }); } static useReact() { return !!document.querySelector("div[data-react-nav-root]"); } } (async () => { if (ReactUtils.useReact()) { await ReactUtils.waitForReactInit(async (root, reactProp) => { console.log("React is ready!", { root, reactProp }); await initContext(); initApp(); registerMenu(); await main(); }); } else { console.log("React is not detected!"); await initContext(); initApp(); registerMenu(); await main(); } })(); function initApp() { vue.createApp(App).mount( (() => { const app = document.createElement("div"); app.setAttribute("id", "spc-menu"); document.body.append(app); return app; })() ); } function registerMenu() { GmUtils.registerMenuCommand(IM_MENU_SETTING); } async function initContext() { const setting = SettingManager.instance.setting; setLogLevel(setting.logLevel); let targetCountyInfo = countyCode2Info.get(setting.countyCode); if (!targetCountyInfo) { Logger.warn(`获取转换后的国家(${setting.countyCode})信息失败,默认为美国:` + setting.countyCode); targetCountyInfo = countyCode2Info.get("US"); } Logger.info("目标区域:", targetCountyInfo); const currCountyCode = await CountyCodeGetterManager.instance.getCountyCode(); const currCountInfo = countyCode2Info.get(currCountyCode); if (!currCountyCode) { throw Error("缺少当前国家的信息映射:county: " + currCountyCode); } Logger.info("当前区域:", currCountInfo); _unsafeWindow.SpcManager = SpcManager.instance; _unsafeWindow.spcContext = new SpcContext(setting, targetCountyInfo, currCountInfo); } })(mdui, Vue);