Merge branch 'master' into dev

# Conflicts:
#	src/stores/base.ts
This commit is contained in:
Zyronon
2025-12-16 01:23:49 +08:00
7 changed files with 254 additions and 116 deletions

View File

@@ -48,75 +48,76 @@
<!-- color-scheme 告诉浏览器支持亮/暗模式-->
<meta name="color-scheme" content="light dark"/>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-50T6DRD837"></script>
<script>
if (
!location.href.includes('localhost')
&& !location.href.includes('192.168')
&& !location.href.includes('172.16')
&& !location.href.includes('10.0')
) {
//51.la
(function () {
window.LA = window.LA || {
ids: [{id: "3OH8ITYRgwzo58L2", ck: "3OH8ITYRgwzo58L2"}],
id: "3OH8ITYRgwzo58L2",
ck: "3OH8ITYRgwzo58L2",
hashMode: true
};
const script = document.createElement('script');
script.src = `https://typewords.cc/libs/51.js`;
document.head.appendChild(script);
})();
</script>
// Cloudflare
(function () {
var cf = document.createElement("script");
cf.src = 'https://static.cloudflareinsights.com/beacon.min.js'
cf.setAttribute("data-cf-beacon", '{"token": "e5119992696d4155814400dd69781d68"}');
document.head.appendChild(cf);
})();
<script>
</script>
// google
(function () {
var ana = document.createElement("script");
ana.src = 'https://www.googletagmanager.com/gtag/js?id=G-50T6DRD837'
ana.onload = function () {
window.dataLayer = window.dataLayer || [];
<script>
if (!location.href.includes('localhost')
&& !location.href.includes('192.168')
&& !location.href.includes('172.16')
&& !location.href.includes('10.0')
) {
//https://51.la/
!function(p){"use strict";!function(t){var s=window,e=document,i=p,c="".concat("https:"===e.location.protocol?"https://":"http://","sdk.51.la/js-sdk-pro.min.js"),n=e.createElement("script"),r=e.getElementsByTagName("script")[0];n.type="text/javascript",n.setAttribute("charset","UTF-8"),n.async=!0,n.src=c,n.id="LA_COLLECT",i.d=n;var o=function(){s.LA.ids.push(i)};s.LA?s.LA.ids&&o():(s.LA=p,s.LA.ids=[],o()),r.parentNode.insertBefore(n,r)}()}({id:"3OH8ITYRgwzo58L2",ck:"3OH8ITYRgwzo58L2",autoTrack:true,hashMode:true});
function gtag() {
dataLayer.push(arguments);
}
// Cloudflare
(function () {
var cf = document.createElement("script");
cf.src = 'https://static.cloudflareinsights.com/beacon.min.js'
cf.setAttribute("data-cf-beacon", '{"token": "e5119992696d4155814400dd69781d68"}');
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(cf, s);
})();
// google
(function () {
var ana = document.createElement("script");
ana.src = 'https://www.googletagmanager.com/gtag/js?id=G-50T6DRD837'
ana.onload = function () {
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-50T6DRD837');
}
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(ana, s);
})();
gtag('js', new Date());
gtag('config', 'G-50T6DRD837');
}
document.head.appendChild(ana);
})();
// baidu
var _hmt = _hmt || [];
(function () {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?3dae52fcd5375a19905462e4ad3eb54e";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
// baidu
var _hmt = _hmt || [];
(function () {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?3dae52fcd5375a19905462e4ad3eb54e";
document.head.appendChild(hm);
})();
// umami
(function () {
var umami = document.createElement("script");
umami.src = 'https://typewords.cc/libs/s.js'
umami.setAttribute("data-website-id", "160308c9-7900-4b1d-a0b1-c3b25a9530f6");
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(umami, s);
})();
// umami
// (function () {
// var umami = document.createElement("script");
// umami.src = 'https://typewords.cc/libs/s.js'
// umami.setAttribute("data-website-id", "160308c9-7900-4b1d-a0b1-c3b25a9530f6");
// document.head.appendChild(umami);
// })();
// umami-saas
(function () {
var umami2 = document.createElement("script");
umami2.src = 'https://stat.typewords.cc/script.js'
umami2.setAttribute("data-website-id", "4d728ae3-5393-4efe-81dc-30dcb4f33c00");
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(umami2, s);
})();
}
// umami-saas
// (function () {
// var umami2 = document.createElement("script");
// umami2.src = 'https://stat.typewords.cc/script.js'
// umami2.setAttribute("data-website-id", "4d728ae3-5393-4efe-81dc-30dcb4f33c00");
// document.head.appendChild(umami2);
// })();
}
</script>
</head>
<body>
@@ -126,56 +127,56 @@
</noscript>
<div id="app"></div>
<script>
(function () {
var ua = navigator.userAgent || ''
var isIE = !!document.documentMode || /MSIE|Trident/i.test(ua)
if (!isIE) return
var style = document.createElement('style')
style.type = 'text/css'
style.appendChild(document.createTextNode(
'.ie-mask{position:fixed;left:0;top:0;right:0;bottom:0;background:rgba(0,0,0,.35);z-index:9998}' +
'.ie-dialog{position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);width:28rem;max-width:90vw;background:#fff;color:#111;border-radius:.6rem;box-shadow:0 10px 30px rgba(0,0,0,.15);z-index:9999;padding:1.2rem}' +
'.ie-dialog .title{font-size:1.2rem;font-weight:700;margin-bottom:.6rem}' +
'.ie-dialog .desc{font-size:.95rem;line-height:1.6;color:#555}' +
'.ie-dialog .actions{display:flex;justify-content:flex-end;margin-top:1rem}' +
'.ie-dialog .actions > * + *{margin-left:.6rem}' +
'.ie-dialog .btn{display:inline-flex;align-items:center;justify-content:center;height:2.2rem;padding:0 1rem;border-radius:.4rem;background:#0C8CE9;color:#fff;text-decoration:none}' +
'.ie-dialog .btn-secondary{display:inline-flex;align-items:center;justify-content:center;height:2.2rem;padding:0 .9rem;border-radius:.4rem;background:#eee;color:#333;border:1px solid #ddd}' +
'@media (prefers-color-scheme: dark){.ie-dialog{background:#1e1f22;color:#e6e6e6}.ie-dialog .desc{color:#c6c6c6}.ie-dialog .btn-secondary{background:#2a2b2f;color:#e6e6e6;border-color:#3a3b3f}}'
))
document.head.appendChild(style)
var mask = document.createElement('div')
mask.className = 'ie-mask'
var dialog = document.createElement('div')
dialog.className = 'ie-dialog'
dialog.innerHTML = '<div class="title">不支持 IE 浏览器</div>' +
'<div class="desc">Type Words 使用现代技术构建,请使用 Chrome、Edge、Firefox 或 Safari 等现代浏览器访问。</div>' +
'<div class="actions">' +
'<a class="btn" href="https://www.google.cn/chrome/" target="_blank" rel="noreferrer">下载 Chrome</a>' +
'<button class="btn-secondary" type="button">我知道了</button>' +
'</div>'
(function () {
var ua = navigator.userAgent || ''
var isIE = !!document.documentMode || /MSIE|Trident/i.test(ua)
if (!isIE) return
var style = document.createElement('style')
style.type = 'text/css'
style.appendChild(document.createTextNode(
'.ie-mask{position:fixed;left:0;top:0;right:0;bottom:0;background:rgba(0,0,0,.35);z-index:9998}' +
'.ie-dialog{position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);width:28rem;max-width:90vw;background:#fff;color:#111;border-radius:.6rem;box-shadow:0 10px 30px rgba(0,0,0,.15);z-index:9999;padding:1.2rem}' +
'.ie-dialog .title{font-size:1.2rem;font-weight:700;margin-bottom:.6rem}' +
'.ie-dialog .desc{font-size:.95rem;line-height:1.6;color:#555}' +
'.ie-dialog .actions{display:flex;justify-content:flex-end;margin-top:1rem}' +
'.ie-dialog .actions > * + *{margin-left:.6rem}' +
'.ie-dialog .btn{display:inline-flex;align-items:center;justify-content:center;height:2.2rem;padding:0 1rem;border-radius:.4rem;background:#0C8CE9;color:#fff;text-decoration:none}' +
'.ie-dialog .btn-secondary{display:inline-flex;align-items:center;justify-content:center;height:2.2rem;padding:0 .9rem;border-radius:.4rem;background:#eee;color:#333;border:1px solid #ddd}' +
'@media (prefers-color-scheme: dark){.ie-dialog{background:#1e1f22;color:#e6e6e6}.ie-dialog .desc{color:#c6c6c6}.ie-dialog .btn-secondary{background:#2a2b2f;color:#e6e6e6;border-color:#3a3b3f}}'
))
document.head.appendChild(style)
var mask = document.createElement('div')
mask.className = 'ie-mask'
var dialog = document.createElement('div')
dialog.className = 'ie-dialog'
dialog.innerHTML = '<div class="title">不支持 IE 浏览器</div>' +
'<div class="desc">Type Words 使用现代技术构建,请使用 Chrome、Edge、Firefox 或 Safari 等现代浏览器访问。</div>' +
'<div class="actions">' +
'<a class="btn" href="https://www.google.cn/chrome/" target="_blank" rel="noreferrer">下载 Chrome</a>' +
'<button class="btn-secondary" type="button">我知道了</button>' +
'</div>'
function close() {
try {
document.body.removeChild(mask)
} catch (e) {
}
try {
document.body.removeChild(dialog)
} catch (e) {
}
}
function close() {
try {
document.body.removeChild(mask)
} catch (e) {
}
try {
document.body.removeChild(dialog)
} catch (e) {
}
}
mask.addEventListener('click', close)
var btn = null
try {
btn = dialog.querySelector('.btn-secondary')
} catch (e) {
}
if (btn) btn.addEventListener('click', close)
document.body.appendChild(mask)
document.body.appendChild(dialog)
})()
mask.addEventListener('click', close)
var btn = null
try {
btn = dialog.querySelector('.btn-secondary')
} catch (e) {
}
if (btn) btn.addEventListener('click', close)
document.body.appendChild(mask)
document.body.appendChild(dialog)
})()
</script>
<script type="module" src="/src/main.ts"></script>
</body>

View File

@@ -383,6 +383,78 @@
}
</style>
<script>
if (
!location.href.includes('localhost')
&& !location.href.includes('192.168')
&& !location.href.includes('172.16')
&& !location.href.includes('10.0')
) {
//51.la
(function () {
window.LA = window.LA || {
ids: [{id: "3OH8ITYRgwzo58L2", ck: "3OH8ITYRgwzo58L2"}],
id: "3OH8ITYRgwzo58L2",
ck: "3OH8ITYRgwzo58L2",
hashMode: true
};
const script = document.createElement('script');
script.src = `https://typewords.cc/libs/51.js`;
document.head.appendChild(script);
})();
// Cloudflare
(function () {
var cf = document.createElement("script");
cf.src = 'https://static.cloudflareinsights.com/beacon.min.js'
cf.setAttribute("data-cf-beacon", '{"token": "e5119992696d4155814400dd69781d68"}');
document.head.appendChild(cf);
})();
// google
(function () {
var ana = document.createElement("script");
ana.src = 'https://www.googletagmanager.com/gtag/js?id=G-50T6DRD837'
ana.onload = function () {
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-50T6DRD837');
}
document.head.appendChild(ana);
})();
// baidu
var _hmt = _hmt || [];
(function () {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?3dae52fcd5375a19905462e4ad3eb54e";
document.head.appendChild(hm);
})();
// umami
// (function () {
// var umami = document.createElement("script");
// umami.src = 'https://typewords.cc/libs/s.js'
// umami.setAttribute("data-website-id", "160308c9-7900-4b1d-a0b1-c3b25a9530f6");
// document.head.appendChild(umami);
// })();
// umami-saas
// (function () {
// var umami2 = document.createElement("script");
// umami2.src = 'https://stat.typewords.cc/script.js'
// umami2.setAttribute("data-website-id", "4d728ae3-5393-4efe-81dc-30dcb4f33c00");
// document.head.appendChild(umami2);
// })();
}
</script>
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {

View File

@@ -129,8 +129,8 @@ async function refreshCDN(domain) {
async function main() {
const files = getAllFiles('./dist')
console.log(`📁 共找到 ${files.length} 个文件,开始上传...`)
// await uploadFilesWithClean(files, './dist', ['dicts', 'sound', 'libs','imgs])
await uploadFilesWithClean(files, './dist', ['sound','libs','imgs'])
await uploadFilesWithClean(files, './dist', ['dicts', 'sound', 'libs','imgs'])
// await uploadFilesWithClean(files, './dist', ['sound','libs','imgs'])
await refreshCDN('2study.top')
await refreshCDN('typewords.cc')
}

View File

@@ -21,7 +21,11 @@ const userStore = useUserStore()
const {setTheme} = useTheme()
let lastAudioFileIdList = []
let isInitializing = true // 标记是否正在初始化
watch(store.$state, (n: BaseState) => {
// 如果正在初始化,不保存数据,避免覆盖
if (isInitializing) return
console.log('watch')
let data = shakeCommonDict(n)
set(SAVE_DICT_KEY.key, JSON.stringify({val: data, version: SAVE_DICT_KEY.version}))
@@ -52,6 +56,7 @@ watch(store.$state, (n: BaseState) => {
})
watch(() => settingStore.$state, (n) => {
if (isInitializing) return
set(SAVE_SETTING_KEY.key, JSON.stringify({val: n, version: SAVE_SETTING_KEY.version}))
if (AppEnv.CAN_REQUEST) {
syncSetting(null, settingStore.$state)
@@ -59,10 +64,12 @@ watch(() => settingStore.$state, (n) => {
}, {deep: true})
async function init() {
isInitializing = true // 开始初始化
await userStore.init()
await store.init()
await settingStore.init()
store.load = true
isInitializing = false // 初始化完成,允许保存数据
setTheme(settingStore.theme)

View File

@@ -648,7 +648,7 @@ const currentPractice = inject('currentPractice', [])
@input="handleMobileInput"
/>
<header class="mb-4">
<div class="title word"><span class="font-family text-3xl">{{
<div class="title"><span class="font-family text-3xl">{{
store.sbook.lastLearnIndex + 1
}}.</span>{{ props.article.title }}
</div>
@@ -826,7 +826,7 @@ $article-lh: 2.4;
display: inline-block !important;
}
.translate{
color:black;
color: var(--color-reverse-black);
}
}

View File

@@ -245,6 +245,38 @@ async function onTyping(e: KeyboardEvent) {
} else {
right = letter === word[input.length]
}
//针对中文的特殊判断
if (e.shiftKey && (
'' === word[input.length] && e.code === 'Digit1' ||
'¥' === word[input.length] && e.code === 'Digit4' ||
'…' === word[input.length] && e.code === 'Digit6' ||
'' === word[input.length] && e.code === 'Digit9' ||
'—' === word[input.length] && e.code === 'Minus' ||
'' === word[input.length] && e.code === 'Slash' ||
'》' === word[input.length] && e.code === 'Period' ||
'《' === word[input.length] && e.code === 'Comma' ||
'“' === word[input.length] && e.code === 'Quote' ||
'' === word[input.length] && e.code === 'Semicolon' ||
'' === word[input.length] && e.code === 'Digit0')
) {
right = true
letter = word[input.length]
}
if (!e.shiftKey && (
'【' === word[input.length] && e.code === 'BracketLeft' ||
'、' === word[input.length] && e.code === 'Slash' ||
'。' === word[input.length] && e.code === 'Period' ||
'' === word[input.length] && e.code === 'Comma' ||
'' === word[input.length] && e.code === 'Quote' ||
'' === word[input.length] && e.code === 'Semicolon' ||
'【' === word[input.length] && e.code === 'BracketLeft' ||
'】' === word[input.length] && e.code === 'BracketRight'
)) {
right = true
letter = word[input.length]
}
console.log('e', e, e.code, e.shiftKey, word[input.length])
if (right) {
input += letter
wrong = ''
@@ -386,6 +418,7 @@ function checkCursorPosition() {
// 选中目标元素
const cursorEl = document.querySelector(`.cursor`);
const inputList = document.querySelectorAll(`.l`);
if (!typingWordRef) return;
const typingWordRect = typingWordRef.getBoundingClientRect();
if (inputList.length) {

View File

@@ -37,9 +37,15 @@ export function checkAndUpgradeSaveDict(val: any) {
} else {
data = val
}
if (!data.version) return defaultState
if (!data.version) {
console.warn('数据缺少版本号,返回默认状态')
return defaultState
}
let state: any = data.val
if (typeof state !== 'object') return defaultState
if (typeof state !== 'object') {
console.warn('数据格式无效,返回默认状态')
return defaultState
}
state.load = false
let version = Number(data.version)
// console.log('state', state)
@@ -53,10 +59,29 @@ export function checkAndUpgradeSaveDict(val: any) {
})
return defaultState
} else {
checkRiskKey(defaultState, state)
return defaultState
// 版本不匹配时,尽量保留数据而不是直接返回默认状态
console.warn(`数据版本不匹配: 当前版本 ${version}, 期望版本 ${SAVE_DICT_KEY.version},尝试保留数据`)
try {
checkRiskKey(defaultState, state)
// 尝试保留 bookList 数据
if (state.word && state.word.bookList && Array.isArray(state.word.bookList)) {
defaultState.word.bookList = state.word.bookList.map((v: any) => {
return getDefaultDict(checkRiskKey(getDefaultDict(), v))
})
}
if (state.article && state.article.bookList && Array.isArray(state.article.bookList)) {
defaultState.article.bookList = state.article.bookList.map((v: any) => {
return getDefaultDict(checkRiskKey(getDefaultDict(), v))
})
}
return defaultState
} catch (upgradeError) {
console.error('数据升级失败,返回默认状态', upgradeError)
return defaultState
}
}
} catch (e) {
console.error('数据解析异常,返回默认状态', e)
return defaultState
}
}