diff --git a/src/apis/index.js b/src/apis/index.js index 1e57ff6..7a57c22 100644 --- a/src/apis/index.js +++ b/src/apis/index.js @@ -3,10 +3,8 @@ import { fetchData } from "../libs/fetch"; import { URL_CACHE_TRAN, KV_SALT_SYNC, - OPT_LANGS_BAIDU, - OPT_LANGS_TENCENT, - OPT_LANGS_SPECIAL, - OPT_LANGS_MICROSOFT, + OPT_LANGS_TO_SPEC, + OPT_LANGS_SPEC_DEFAULT, API_SPE_TYPES, DEFAULT_API_SETTING, } from "../config"; @@ -41,6 +39,13 @@ export const apiSyncData = async (url, key, data) => */ export const apiFetch = (url) => fetchData(url); +/** + * Microsoft token + * @returns + */ +export const apiMsAuth = async () => + fetchData("https://edge.microsoft.com/translate/auth"); + /** * Google语言识别 * @param {*} text @@ -78,7 +83,7 @@ export const apiGoogleLangdetect = async (text) => { * @returns */ export const apiMicrosoftLangdetect = async (text) => { - const [token] = await msAuth(); + const token = await msAuth(); const input = "https://api-edge.cognitive.microsofttranslator.com/detect?api-version=3.0"; const init = { @@ -93,9 +98,9 @@ export const apiMicrosoftLangdetect = async (text) => { useCache: true, }); - if (res[0].language) { + if (res?.[0]?.language) { await putHttpCachePolyfill(input, init, res); - return OPT_LANGS_MICROSOFT.get(res[0].language) ?? res[0].language; + return res[0].language; } return ""; @@ -119,9 +124,9 @@ export const apiBaiduLangdetect = async (text) => { }; const res = await fetchData(input, init, { useCache: true }); - if (res.error === 0) { + if (res?.error === 0) { await putHttpCachePolyfill(input, init, res); - return OPT_LANGS_BAIDU.get(res.lan) ?? res.lan; + return res.lan; } return ""; @@ -145,7 +150,7 @@ export const apiBaiduSuggest = async (text) => { }; const res = await fetchData(input, init, { useCache: true }); - if (res.errno === 0) { + if (res?.errno === 0) { await putHttpCachePolyfill(input, init, res); return res.data; } @@ -175,21 +180,26 @@ export const apiTencentLangdetect = async (text) => { const body = JSON.stringify({ header: { fn: "text_analysis", + client_key: + "browser-chrome-110.0.0-Mac OS-df4bd4c5-a65d-44b2-a40f-42f34f3535f2-1677486696487", }, text, }); const init = { headers: { "Content-type": "application/json", + "user-agent": + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36", + referer: "https://transmart.qq.com/zh-CN/index", }, method: "POST", body, }; const res = await fetchData(input, init, { useCache: true }); - if (res.language) { + if (res?.language) { await putHttpCachePolyfill(input, init, res); - return OPT_LANGS_TENCENT.get(res.language) ?? res.language; + return res.language; } return ""; @@ -202,7 +212,7 @@ export const apiTencentLangdetect = async (text) => { */ export const apiTranslate = async ({ text, - fromLang, + fromLang = "auto", toLang, apiSetting = DEFAULT_API_SETTING, docInfo = {}, @@ -214,8 +224,8 @@ export const apiTranslate = async ({ } const { apiType, apiSlug, useBatchFetch } = apiSetting; - const langMap = OPT_LANGS_SPECIAL[apiType]; - const from = langMap.get(fromLang) ?? langMap.get("auto"); + const langMap = OPT_LANGS_TO_SPEC[apiType] || OPT_LANGS_SPEC_DEFAULT; + const from = langMap.get(fromLang); const to = langMap.get(toLang); if (!to) { kissLog(`target lang: ${toLang} not support`); @@ -283,8 +293,7 @@ export const apiTranslate = async ({ } } - // const isSame = srLang && (to.includes(srLang) || srLang.includes(to)); - const isSame = srLang && srLang.slice(0, 2) === to.slice(0, 2); + const isSame = fromLang !== "auto" && srLang === to; // 插入缓存 if (useCache && trText) { diff --git a/src/apis/trans.js b/src/apis/trans.js index b1b620a..23127e0 100644 --- a/src/apis/trans.js +++ b/src/apis/trans.js @@ -686,7 +686,7 @@ export const parseTransRes = async ( case OPT_TRANS_MICROSOFT: return res?.map((item) => [ item.translations.map((item) => item.text).join(" "), - item.detectedLanguage.language, + item.detectedLanguage?.language, ]); case OPT_TRANS_DEEPL: return res?.translations?.map((item) => [ @@ -811,7 +811,7 @@ export const handleTranslate = async ({ let token = ""; if (apiType === OPT_TRANS_MICROSOFT) { - [token] = await msAuth(); + token = await msAuth(); } const [input, init, userMsg] = await genTransReq({ diff --git a/src/background.js b/src/background.js index 79a818f..cfee181 100644 --- a/src/background.js +++ b/src/background.js @@ -23,10 +23,9 @@ import { import { getSettingWithDefault, tryInitDefaultData } from "./libs/storage"; import { trySyncSettingAndRules } from "./libs/sync"; import { fetchHandle } from "./libs/fetch"; -import { getHttpCache, putHttpCache } from "./libs/cache"; +import { tryClearCaches, getHttpCache, putHttpCache } from "./libs/cache"; import { sendTabMsg } from "./libs/msg"; import { trySyncAllSubRules } from "./libs/subRules"; -import { tryClearCaches } from "./libs"; import { saveRule } from "./libs/rules"; import { getCurTabId } from "./libs/msg"; import { injectInlineJs, injectInternalCss } from "./libs/injector"; diff --git a/src/config/api.js b/src/config/api.js index 43cbf23..b8c7f58 100644 --- a/src/config/api.js +++ b/src/config/api.js @@ -180,48 +180,61 @@ export const OPT_LANGS_TO = [ ["uk", "Ukrainian - Українська"], ["vi", "Vietnamese - Tiếng Việt"], ]; +export const OPT_LANGS_LIST = OPT_LANGS_TO.map(([lang]) => lang); export const OPT_LANGS_FROM = [["auto", "Auto-detect"], ...OPT_LANGS_TO]; -export const OPT_LANGS_SPECIAL = { - [OPT_TRANS_GOOGLE]: new Map(OPT_LANGS_FROM.map(([key]) => [key, key])), - [OPT_TRANS_GOOGLE_2]: new Map(OPT_LANGS_FROM.map(([key]) => [key, key])), +export const OPT_LANGS_MAP = new Map(OPT_LANGS_TO); + +// CODE->名称 +export const OPT_LANGS_SPEC_NAME = new Map( + OPT_LANGS_FROM.map(([key, val]) => [key, val.split(" - ")[0]]) +); +export const OPT_LANGS_SPEC_DEFAULT = new Map( + OPT_LANGS_FROM.map(([key]) => [key, key]) +); +export const OPT_LANGS_SPEC_DEFAULT_UC = new Map( + OPT_LANGS_FROM.map(([key]) => [key, key.toUpperCase()]) +); +export const OPT_LANGS_TO_SPEC = { + [OPT_TRANS_GOOGLE]: OPT_LANGS_SPEC_DEFAULT, + [OPT_TRANS_GOOGLE_2]: OPT_LANGS_SPEC_DEFAULT, [OPT_TRANS_MICROSOFT]: new Map([ - ...OPT_LANGS_FROM.map(([key]) => [key, key]), + ...OPT_LANGS_SPEC_DEFAULT, ["auto", ""], ["zh-CN", "zh-Hans"], ["zh-TW", "zh-Hant"], ]), [OPT_TRANS_DEEPL]: new Map([ - ...OPT_LANGS_FROM.map(([key]) => [key, key.toUpperCase()]), + ...OPT_LANGS_SPEC_DEFAULT_UC, ["auto", ""], ["zh-CN", "ZH"], ["zh-TW", "ZH"], ]), [OPT_TRANS_DEEPLFREE]: new Map([ - ...OPT_LANGS_FROM.map(([key]) => [key, key.toUpperCase()]), + ...OPT_LANGS_SPEC_DEFAULT_UC, ["auto", "auto"], ["zh-CN", "ZH"], ["zh-TW", "ZH"], ]), [OPT_TRANS_DEEPLX]: new Map([ - ...OPT_LANGS_FROM.map(([key]) => [key, key.toUpperCase()]), + ...OPT_LANGS_SPEC_DEFAULT_UC, ["auto", "auto"], ["zh-CN", "ZH"], ["zh-TW", "ZH"], ]), [OPT_TRANS_NIUTRANS]: new Map([ - ...OPT_LANGS_FROM.map(([key]) => [key, key]), + ...OPT_LANGS_SPEC_DEFAULT, ["auto", "auto"], ["zh-CN", "zh"], ["zh-TW", "cht"], ]), [OPT_TRANS_VOLCENGINE]: new Map([ - ...OPT_LANGS_FROM.map(([key]) => [key, key]), + ...OPT_LANGS_SPEC_DEFAULT, ["auto", "auto"], ["zh-CN", "zh"], ["zh-TW", "zh-Hant"], ]), [OPT_TRANS_BAIDU]: new Map([ - ...OPT_LANGS_FROM.map(([key]) => [key, key]), + ...OPT_LANGS_SPEC_DEFAULT, ["zh-CN", "zh"], ["zh-TW", "cht"], ["ar", "ara"], @@ -269,62 +282,34 @@ export const OPT_LANGS_SPECIAL = { ["id", "id"], ["vi", "vi"], ]), - [OPT_TRANS_OPENAI]: new Map( - OPT_LANGS_FROM.map(([key, val]) => [key, val.split(" - ")[0]]) - ), - [OPT_TRANS_GEMINI]: new Map( - OPT_LANGS_FROM.map(([key, val]) => [key, val.split(" - ")[0]]) - ), - [OPT_TRANS_GEMINI_2]: new Map( - OPT_LANGS_FROM.map(([key, val]) => [key, val.split(" - ")[0]]) - ), - [OPT_TRANS_CLAUDE]: new Map( - OPT_LANGS_FROM.map(([key, val]) => [key, val.split(" - ")[0]]) - ), - [OPT_TRANS_OLLAMA]: new Map( - OPT_LANGS_FROM.map(([key, val]) => [key, val.split(" - ")[0]]) - ), - [OPT_TRANS_OPENROUTER]: new Map( - OPT_LANGS_FROM.map(([key, val]) => [key, val.split(" - ")[0]]) - ), - [OPT_TRANS_CLOUDFLAREAI]: new Map([ - ["auto", ""], - ["zh-CN", "chinese"], - ["zh-TW", "chinese"], - ["en", "english"], - ["ar", "arabic"], - ["de", "german"], - ["ru", "russian"], - ["fr", "french"], - ["pt", "portuguese"], - ["ja", "japanese"], - ["es", "spanish"], - ["hi", "hindi"], - ]), - [OPT_TRANS_CUSTOMIZE]: new Map([ - ...OPT_LANGS_FROM.map(([key]) => [key, key]), - ]), + [OPT_TRANS_OPENAI]: OPT_LANGS_SPEC_DEFAULT, + [OPT_TRANS_GEMINI]: OPT_LANGS_SPEC_DEFAULT, + [OPT_TRANS_GEMINI_2]: OPT_LANGS_SPEC_DEFAULT, + [OPT_TRANS_CLAUDE]: OPT_LANGS_SPEC_DEFAULT, + [OPT_TRANS_OLLAMA]: OPT_LANGS_SPEC_DEFAULT, + [OPT_TRANS_OPENROUTER]: OPT_LANGS_SPEC_DEFAULT, + [OPT_TRANS_CLOUDFLAREAI]: OPT_LANGS_SPEC_DEFAULT, + [OPT_TRANS_CUSTOMIZE]: OPT_LANGS_SPEC_DEFAULT, }; -export const OPT_LANGS_LIST = OPT_LANGS_TO.map(([lang]) => lang); -export const OPT_LANGS_MICROSOFT = new Map( - Array.from(OPT_LANGS_SPECIAL[OPT_TRANS_MICROSOFT].entries()).map(([k, v]) => [ - v, - k, - ]) -); -export const OPT_LANGS_BAIDU = new Map( - Array.from(OPT_LANGS_SPECIAL[OPT_TRANS_BAIDU].entries()).map(([k, v]) => [ - v, - k, - ]) -); -export const OPT_LANGS_TENCENT = new Map( - Array.from(OPT_LANGS_SPECIAL[OPT_TRANS_TENCENT].entries()).map(([k, v]) => [ - v, - k, - ]) -); -OPT_LANGS_TENCENT.set("zh", "zh-CN"); + +const specToCode = (m) => + new Map( + Array.from(m.entries()).map(([k, v]) => { + if (v === "") { + return ["auto", "auto"]; + } + if (v === "zh" || v === "ZH") { + return [v, "zh-CN"]; + } + return [v, k]; + }) + ); + +// 名称->CODE +export const OPT_LANGS_TO_CODE = {}; +Object.entries(OPT_LANGS_TO_SPEC).forEach(([t, m]) => { + OPT_LANGS_TO_CODE[t] = specToCode(m); +}); const defaultSystemPrompt = `Act as a translation API. Output a single raw JSON object only. No extra text or fences. diff --git a/src/hooks/Rules.js b/src/hooks/Rules.js index 9972d20..e436de8 100644 --- a/src/hooks/Rules.js +++ b/src/hooks/Rules.js @@ -8,7 +8,7 @@ import { useCallback } from "react"; * @returns */ export function useRules() { - const { data: list, save } = useStorage( + const { data: list = [], save } = useStorage( STOKEY_RULES, DEFAULT_RULES, KV_RULES_KEY diff --git a/src/libs/auth.js b/src/libs/auth.js index d4e01e4..4279a06 100644 --- a/src/libs/auth.js +++ b/src/libs/auth.js @@ -1,6 +1,6 @@ import { getMsauth, setMsauth } from "./storage"; -import { fetchData } from "./fetch"; import { kissLog } from "./log"; +import { apiMsAuth } from "../apis"; const parseMSToken = (token) => { try { @@ -16,28 +16,55 @@ const parseMSToken = (token) => { * @returns */ const _msAuth = () => { - let { token, exp } = {}; + let tokenPromise = null; + const EXPIRATION_MS = 1000; + + const fetchNewToken = async () => { + try { + const now = Date.now(); + + // 1. 查询storage缓存 + const storageToken = await getMsauth(); + if (storageToken) { + const storageExp = parseMSToken(storageToken); + const storageExpiresAt = storageExp * 1000; + if (storageExpiresAt > now + EXPIRATION_MS) { + return { token: storageToken, expiresAt: storageExpiresAt }; + } + } + + // 2. 缓存没有或失效,查询接口 + const apiToken = await apiMsAuth(); + if (!apiToken) { + throw new Error("Failed to fetch ms token"); + } + + const apiExp = parseMSToken(apiToken); + const apiExpiresAt = apiExp * 1000; + await setMsauth(apiToken); + return { token: apiToken, expiresAt: apiExpiresAt }; + } catch (error) { + kissLog("get msauth failed", error); + throw error; + } + }; return async () => { - // 查询内存缓存 - const now = Date.now(); - if (token && exp * 1000 > now + 1000) { - return [token, exp]; + // 检查是否有缓存的 Promise + if (tokenPromise) { + try { + const cachedResult = await tokenPromise; + if (cachedResult.expiresAt > Date.now() + EXPIRATION_MS) { + return cachedResult.token; + } + } catch (error) { + // + } } - // 查询storage缓存 - const res = await getMsauth(); - token = res?.token; - exp = res?.exp; - if (token && exp * 1000 > now + 1000) { - return [token, exp]; - } - - // 缓存没有或失效,查询接口 - token = await fetchData("https://edge.microsoft.com/translate/auth"); - exp = parseMSToken(token); - await setMsauth({ token, exp }); - return [token, exp]; + tokenPromise = fetchNewToken(); + const result = await tokenPromise; + return result.token; }; }; diff --git a/src/libs/cache.js b/src/libs/cache.js index 28f19df..78e20d8 100644 --- a/src/libs/cache.js +++ b/src/libs/cache.js @@ -10,6 +10,17 @@ import { isBg } from "./browser"; import { sendBgMsg } from "./msg"; import { blobToBase64 } from "./utils"; +/** + * 清除缓存数据 + */ +export const tryClearCaches = async () => { + try { + caches.delete(CACHE_NAME); + } catch (err) { + kissLog("clean caches", err); + } +}; + /** * 构造缓存 request * @param {*} input diff --git a/src/libs/index.js b/src/libs/detect.js similarity index 54% rename from src/libs/index.js rename to src/libs/detect.js index 2d2e288..d6af90e 100644 --- a/src/libs/index.js +++ b/src/libs/detect.js @@ -1,9 +1,10 @@ import { - CACHE_NAME, OPT_TRANS_GOOGLE, OPT_TRANS_MICROSOFT, OPT_TRANS_BAIDU, OPT_TRANS_TENCENT, + OPT_LANGS_TO_CODE, + OPT_LANGS_MAP, } from "../config"; import { browser } from "./browser"; import { @@ -14,52 +15,51 @@ import { } from "../apis"; import { kissLog } from "./log"; -const langdetectMap = { +const langdetectFns = { [OPT_TRANS_GOOGLE]: apiGoogleLangdetect, [OPT_TRANS_MICROSOFT]: apiMicrosoftLangdetect, [OPT_TRANS_BAIDU]: apiBaiduLangdetect, [OPT_TRANS_TENCENT]: apiTencentLangdetect, }; -/** - * 清除缓存数据 - */ -export const tryClearCaches = async () => { - try { - caches.delete(CACHE_NAME); - } catch (err) { - kissLog("clean caches", err); - } -}; - /** * 语言识别 - * @param {*} q + * @param {*} text * @returns */ export const tryDetectLang = async ( - q, - useRemote = false, + text, + useRemote = "false", langDetector = OPT_TRANS_MICROSOFT ) => { - let lang = ""; + let deLang = ""; - if (useRemote) { + // 远程识别 + if (useRemote === "true" && langDetector) { try { - lang = await langdetectMap[langDetector](q); + const lang = await langdetectFns[langDetector](text); + if (lang) { + deLang = OPT_LANGS_TO_CODE[langDetector].get(lang) || ""; + } } catch (err) { kissLog("detect lang remote", err); } } - if (!lang) { + // 本地识别 + if (!deLang) { try { - const res = await browser?.i18n?.detectLanguage(q); - lang = res?.languages?.[0]?.language; + const res = await browser?.i18n?.detectLanguage(text); + const lang = res?.languages?.[0]?.language; + if (OPT_LANGS_MAP.has(lang)) { + deLang = lang; + } else if (lang.startsWith("zh")) { + deLang = "zh-CN"; + } } catch (err) { kissLog("detect lang local", err); } } - return lang; + return deLang; }; diff --git a/src/libs/translator.js b/src/libs/translator.js index 8249e99..34290e2 100644 --- a/src/libs/translator.js +++ b/src/libs/translator.js @@ -24,7 +24,7 @@ import { clearAllBatchQueue } from "./batchQueue"; import { genTextClass } from "./style"; import { loadingSvg } from "./svg"; import { shortcutRegister } from "./shortcut"; -import { tryDetectLang } from "."; +import { tryDetectLang } from "./detect"; /** * @class Translator @@ -436,6 +436,7 @@ export class Translator { } } + // todo: 利用AI总结 #getDocDescription() { try { const meta = document.querySelector('meta[name="description"]'); @@ -738,19 +739,29 @@ export class Translator { } // 提前进行语言检测 - const { detectRemote, toLang, skipLangs = [] } = this.#rule; - const { langDetector } = this.#setting; - const deLang = await tryDetectLang( - node.textContent, + let deLang = ""; + const { detectRemote, - langDetector - ); - // console.log("deLang", deLang, toLang); - if ( - deLang && - (toLang.slice(0, 2) === deLang.slice(0, 2) || skipLangs.includes(deLang)) - ) { - return; + fromLang = "auto", + toLang, + skipLangs = [], + } = this.#rule; + if (fromLang === "auto") { + const { langDetector } = this.#setting; + deLang = await tryDetectLang( + node.textContent, + detectRemote, + langDetector + ); + if ( + deLang && + (toLang.slice(0, 2) === deLang.slice(0, 2) || + skipLangs.includes(deLang)) + ) { + // 保留处理状态,不做删除 + // this.#processedNodes.delete(node); + return; + } } let nodeGroup = []; @@ -762,13 +773,13 @@ export class Translator { if (!shouldBreak && shouldGroup) { nodeGroup.push(child); } else if (shouldBreak && nodeGroup.length) { - this.#translateNodeGroup(nodeGroup, node); + this.#translateNodeGroup(nodeGroup, node, deLang); nodeGroup = []; } }); if (nodeGroup.length) { - this.#translateNodeGroup(nodeGroup, node); + this.#translateNodeGroup(nodeGroup, node, deLang); } } @@ -834,7 +845,7 @@ export class Translator { } // 翻译内联节点 - async #translateNodeGroup(nodes, hostNode) { + async #translateNodeGroup(nodes, hostNode, deLang) { const { transTag, textStyle, @@ -901,8 +912,10 @@ export class Translator { // return; // } - const [translatedText, isSameLang] = - await this.#translateFetch(processedString); + const [translatedText, isSameLang] = await this.#translateFetch( + processedString, + deLang + ); // console.log("translatedText", translatedText); if (isSameLang || this.#runId !== currentRunId) { wrapper.remove(); @@ -1060,7 +1073,7 @@ export class Translator { } // 发起翻译请求 - #translateFetch(text) { + #translateFetch(text, deLang = "") { const { apiSlug, fromLang, toLang } = this.#rule; const apiSetting = this.#setting.transApis.find((api) => api.apiSlug === apiSlug) || @@ -1068,7 +1081,7 @@ export class Translator { return apiTranslate({ text, - fromLang, + fromLang: deLang || fromLang, toLang, apiSetting, docInfo: this.#docInfo, @@ -1295,17 +1308,23 @@ export class Translator { this.#init(); } - // 翻译页面标题 if (this.#rule.transTitle === "true") { - const title = document.title; - this.#docInfo.title = title; - this.#translateFetch(title) - .then(([trText]) => { - document.title = trText || title; - }) - .catch((err) => { - kissLog("tanslate title", err); - }); + this.#translateTitle(); + } + } + + // 翻译页面标题 + async #translateTitle() { + const title = document.title; + this.#docInfo.title = title; + if (!title) return; + + try { + const deLang = await tryDetectLang(title); + const [translatedTitle] = await this.#translateFetch(title, deLang); + document.title = translatedTitle || title; + } catch (err) { + kissLog("tanslate title", err); } } diff --git a/src/views/Options/Rules.js b/src/views/Options/Rules.js index a1c78ab..e548a72 100644 --- a/src/views/Options/Rules.js +++ b/src/views/Options/Rules.js @@ -63,24 +63,30 @@ import { kissLog } from "../../libs/log"; import { useApiList } from "../../hooks/Api"; import ShowMoreButton from "./ShowMoreButton"; +const calculateInitialValues = (rule) => { + const base = rule?.pattern === "*" ? GLOBLA_RULE : DEFAULT_RULE; + return { ...base, ...(rule || {}) }; +}; + function RuleFields({ rule, rules, setShow, setKeyword }) { - const initFormValues = useMemo( - () => ({ - ...(rule?.pattern === "*" ? GLOBLA_RULE : DEFAULT_RULE), - ...(rule || {}), - }), - [rule] - ); const editMode = useMemo(() => !!rule, [rule]); const i18n = useI18n(); const [disabled, setDisabled] = useState(editMode); const [errors, setErrors] = useState({}); - const [formValues, setFormValues] = useState(initFormValues); + const [initialFormValues, setInitialFormValues] = useState(() => + calculateInitialValues(rule) + ); + const [formValues, setFormValues] = useState(initialFormValues); const [showMore, setShowMore] = useState(!rules); - const [isModified, setIsModified] = useState(false); const { enabledApis } = useApiList(); + useEffect(() => { + const newInitialValues = calculateInitialValues(rule); + setInitialFormValues(newInitialValues); + setFormValues(newInitialValues); + }, [rule]); + const { pattern, selector, @@ -116,12 +122,9 @@ function RuleFields({ rule, rules, setShow, setKeyword }) { // transRemoveHook = "", } = formValues; - useEffect(() => { - if (!initFormValues) return; - const hasChanged = - JSON.stringify(initFormValues) !== JSON.stringify(formValues); - setIsModified(hasChanged); - }, [initFormValues, formValues]); + const isModified = useMemo(() => { + return JSON.stringify(initialFormValues) !== JSON.stringify(formValues); + }, [initialFormValues, formValues]); const hasSamePattern = (str) => { for (const item of rules.list) { @@ -163,7 +166,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) { setShow(false); } setErrors({}); - setFormValues(initFormValues); + setFormValues(initialFormValues); }; const handleRestore = (e) => { @@ -199,7 +202,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) { // 添加 rules.add(formValues); setShow(false); - setFormValues(initFormValues); + setFormValues(initialFormValues); } }; @@ -1205,11 +1208,15 @@ function SubRules({ subRules }) { } function GlobalRule({ rules }) { - if (!rules.list) { + const globalRule = useMemo( + () => rules.list[rules.list.length - 1], + [rules.list] + ); + + if (!globalRule) { return; } - const globalRule = rules.list[rules.list.length - 1]; return (