feat:重构代码
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<h1 align="center">
|
||||
Typing Word
|
||||
Type Words
|
||||
</h1>
|
||||
|
||||
<p align="center">
|
||||
|
||||
2
components.d.ts
vendored
2
components.d.ts
vendored
@@ -7,7 +7,7 @@ export {}
|
||||
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
BackIcon: typeof import('./src/components/icon/BackIcon.vue')['default']
|
||||
BackIcon: typeof import('./src/components/BackIcon.vue')['default']
|
||||
BaseButton: typeof import('./src/components/BaseButton.vue')['default']
|
||||
BaseIcon: typeof import('./src/components/BaseIcon.vue')['default']
|
||||
Close: typeof import('./src/components/icon/Close.vue')['default']
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8"/>
|
||||
<link rel="icon" type="image/svg+xml" href="/logo.jpg"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Typing Word</title>
|
||||
<title>Type Words</title>
|
||||
<script>
|
||||
;(function () {
|
||||
var src = '//cdn.jsdelivr.net/npm/eruda';
|
||||
|
||||
3
js_node/all.json
Normal file
3
js_node/all.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{"word":"tow","phonetic0":"/ təʊ /","phonetic1":"/ toʊ /","trans":[{"pos":"v.","cn":"拖,牵引(车、船等);(人)牵引,拖带(某人,某物)"},{"pos":"n.","cn":"(对车船等的)牵引,拖;牵引绳,拖链"},{"pos":"","cn":"【名】 (Tow)道(人名)"}],"sentences":[{"v":"They threatened to tow away my car.","tran":"他们威胁说要拖走我的车。"},{"v":"He had been using the vehicle to tow his work trailer..","tran":"他一直用那辆车来拖他的活动工作室。"},{"v":"The car broke down and we had to get somebody to give us a tow.","tran":"汽车抛锚了,我们只得让人拖走。"}],"phrases":[{"v":"in tow","tran":"拖着;在一起"},{"v":"tow truck","tran":"n. 拖车;牵引车"},{"v":"on tow","tran":"(车辆等)被拖(带)着"}],"synos":[{"pos":"n.","tran":"拖;[纺]麻的粗纤维;拖曳所用之绳","ws":["pull","drag"]},{"pos":"vt.","tran":"拖;牵引;曳","ws":["pull","trail"]}],"relWords":[],"memory":""},
|
||||
{"word":"toward","phonetic0":"/ təˈwɔːd /","phonetic1":"/ tɔːrd /","trans":[{"pos":"prep.","cn":"向;趋向;对于;接近(时间);靠近;用于,为了(同towards)"},{"pos":"adj.","cn":"即将来到的,进行中的"},{"pos":"n.","cn":"(Toward)(美、加、沙特)特沃德"}],"sentences":[{"v":"She turned back toward home.","tran":"她折了回来往家走去。"},{"v":"I took a step toward him.","tran":"我朝他迈出了一步。"},{"v":"He headed downhill toward the river.","tran":"他朝山脚下的小河走去。"}],"phrases":[{"v":"go toward","tran":"朝...方向走去"},{"v":"make toward","tran":"向…延伸;向…前进"}],"synos":[{"pos":"prep.","tran":"向;对于;为了;接近","ws":["unto","upon","out"]},{"pos":"adj.","tran":"即将来到的,进行中的","ws":["going","underway"]}],"relWords":[],"memory":""},
|
||||
{"word":"take","phonetic0":"/ teɪk /","phonetic1":"/ teɪk /","trans":[{"pos":"v.","cn":"携带,拿走;带去,引领;使达到,提升;拿,取;移走,拿开;偷走,误拿;取材于,收集;攻占,控制;选中,买下;订阅(报纸等);吃,服用;减去;记录,摘录;照相,摄影;量取,测定;就(座);以…...为例;接受,收取;接纳,接待(顾客、患者等);遭受,经受;忍受,容忍;(以某种方式)对待,处理;理解,考虑;误以为;赢得(比赛、竞赛等);产生(感情),持有(看法);采取(措施),采用(方法);做,拥有;采用(形式),就任(职位);花费,占用(时间); 需要,要求;使用;穿(特定尺码的鞋或衣物);容纳;授课;学习,选修(课程);参加(考试或测验);走(路线),乘坐(交通工具);跨过,跳过;踢,掷;举行投票,进行民意调查;成功,奏效;(语法)需带有(某种结构)"},{"pos":"n.","cn":"(一次拍摄的)镜头,场景;收入量;看法,态度;(印刷)一次排版量"}],"sentences":[{"v":"I'll take my coat upstairs. Shall I take yours, Roberta?","tran":"我将把我的外套拿到楼上去。要我把你的拿上去吗,罗伯塔?"},{"v":"She can't take criticism.","tran":"她受不了批评。"},{"v":"We take the 'Express'.","tran":"我们订阅的是《快报》。"}],"phrases":[{"v":"take some","tran":"不大容易"},{"v":"take care of oneself","tran":"照顾自己;颐养"},{"v":"take part","tran":"参与, 参加"}],"synos":[{"pos":"vt.","tran":"拿,取;采取;吃;接受","ws":["carry","adopt","have","eat","assume"]},{"pos":"vi.","tran":"拿;获得","ws":["pick up","get access to"]}],"relWords":[],"memory":""},
|
||||
156
js_node/crawl.js
Normal file
156
js_node/crawl.js
Normal file
@@ -0,0 +1,156 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { chromium } = require('playwright');
|
||||
|
||||
const SOURCE_DIR = path.join(__dirname, 'source');
|
||||
const RESULT_DIR = path.join(__dirname, 'result');
|
||||
const TOTAL_RESULT_FILE = path.join(__dirname, 'all.json');
|
||||
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
const MAX_COUNT = 3; // ✅ 设定最大爬取数(调试用)
|
||||
|
||||
let crawlCount = 0;
|
||||
const allResults = [];
|
||||
|
||||
// 创建 result 目录(如无)
|
||||
if (!fs.existsSync(RESULT_DIR)) {
|
||||
fs.mkdirSync(RESULT_DIR);
|
||||
}
|
||||
|
||||
// 追加写入总文件
|
||||
function appendToAll(result) {
|
||||
fs.appendFileSync(TOTAL_RESULT_FILE, JSON.stringify(result) + ',\n', 'utf-8');
|
||||
}
|
||||
|
||||
async function crawlWord(word, page, retry = 0) {
|
||||
const data = {
|
||||
word: word,
|
||||
phonetic0: '',
|
||||
phonetic1: '',
|
||||
trans: [],
|
||||
sentences: [],
|
||||
phrases: [],
|
||||
synos: [],
|
||||
relWords: [],
|
||||
memory: '',
|
||||
};
|
||||
|
||||
const url = `https://www.youdao.com/result?word=${encodeURIComponent(word)}&lang=en`;
|
||||
|
||||
try {
|
||||
await page.goto(url, { waitUntil: 'networkidle', timeout: 15000 });
|
||||
|
||||
// word
|
||||
const titleEl = await page.locator('.title').first();
|
||||
data.word = await titleEl.evaluate(el => el.firstChild?.nodeValue || '');
|
||||
|
||||
// phonetic
|
||||
const phones = await page.$$('.per-phone .phonetic');
|
||||
if (phones[0]) data.phonetic0 = (await phones[0].textContent())?.trim() || '';
|
||||
if (phones[1]) data.phonetic1 = (await phones[1].textContent())?.trim() || '';
|
||||
|
||||
// trans
|
||||
const trans = await page.$$('.basic .word-exp');
|
||||
for (const el of trans) {
|
||||
const pos = await el.$('.pos');
|
||||
const tran = await el.$('.trans');
|
||||
data.trans.push({
|
||||
pos: pos ? (await pos.textContent())?.trim() : '',
|
||||
cn: tran ? (await tran.textContent())?.trim() : '',
|
||||
});
|
||||
}
|
||||
|
||||
// sentences
|
||||
const sentList = await page.$$('.blng_sents_part .trans-container ul li .col2');
|
||||
for (const el of sentList) {
|
||||
const en = await el.$('.sen-eng');
|
||||
const ch = await el.$('.sen-ch');
|
||||
data.sentences.push({
|
||||
v: en ? (await en.textContent())?.trim() : '',
|
||||
tran: ch ? (await ch.textContent())?.trim() : '',
|
||||
});
|
||||
}
|
||||
|
||||
// phrases
|
||||
const phrs = await page.$$('.phrs ul li .phrs-content');
|
||||
for (const el of phrs) {
|
||||
const point = await el.$('.point');
|
||||
const tran = await el.$('.phr_trans');
|
||||
data.phrases.push({
|
||||
v: point ? (await point.textContent())?.trim() : '',
|
||||
tran: tran ? (await tran.textContent())?.trim() : '',
|
||||
});
|
||||
}
|
||||
|
||||
// 同义词(optional)
|
||||
try {
|
||||
await page.getByText('同近义词', { timeout: 2000 }).click();
|
||||
await page.waitForSelector('.syno', { timeout: 3000 });
|
||||
const synos = await page.$$('.syno-item');
|
||||
for (const el of synos) {
|
||||
const pos = await el.$('.index');
|
||||
const tran = await el.$('.synptran');
|
||||
const wordEl = await el.$('.clickable');
|
||||
let str = wordEl ? (await wordEl.textContent())?.trim() : '';
|
||||
data.synos.push({
|
||||
pos: pos ? (await pos.textContent())?.trim() : '',
|
||||
tran: tran ? (await tran.textContent())?.trim() : '',
|
||||
ws: str.split('/').map(s => s.trim()).filter(Boolean),
|
||||
});
|
||||
}
|
||||
} catch {}
|
||||
|
||||
return data;
|
||||
} catch (err) {
|
||||
if (retry < 2) {
|
||||
console.log(`🔁 ${word} 抓取失败,重试中...`);
|
||||
await sleep(1000);
|
||||
return crawlWord(word, page, retry + 1);
|
||||
} else {
|
||||
console.log(`❌ ${word} 抓取失败,跳过。`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const files = fs.readdirSync(SOURCE_DIR).filter(f => f.endsWith('.json'));
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await browser.newPage();
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = path.join(SOURCE_DIR, file);
|
||||
const raw = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
||||
const wordList = raw.map(obj => obj.word).filter(Boolean);
|
||||
|
||||
const resultForThisFile = [];
|
||||
|
||||
console.log(`📂 处理文件:${file},共 ${wordList.length} 个单词`);
|
||||
|
||||
for (const word of wordList) {
|
||||
if (crawlCount >= MAX_COUNT) {
|
||||
console.log(`🚫 达到调试上限 ${MAX_COUNT},终止爬取`);
|
||||
await browser.close();
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await crawlWord(word, page);
|
||||
if (result) {
|
||||
crawlCount++;
|
||||
appendToAll(result);
|
||||
resultForThisFile.push(result);
|
||||
}
|
||||
|
||||
await sleep(500);
|
||||
}
|
||||
|
||||
const outputName = path.basename(file, '.json') + '_v2.json';
|
||||
const outputPath = path.join(RESULT_DIR, outputName);
|
||||
fs.writeFileSync(outputPath, JSON.stringify(resultForThisFile, null, 2), 'utf-8');
|
||||
|
||||
console.log(`✅ 已保存:${outputName}`);
|
||||
}
|
||||
|
||||
await browser.close();
|
||||
console.log('\n🎉 所有任务完成!');
|
||||
})();
|
||||
@@ -10,6 +10,7 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"jquery": "^3.7.1",
|
||||
"jsdom": "^23.0.1"
|
||||
"jsdom": "^23.0.1",
|
||||
"playwright": "^1.54.1"
|
||||
}
|
||||
}
|
||||
|
||||
678
js_node/pnpm-lock.yaml
generated
678
js_node/pnpm-lock.yaml
generated
@@ -1,292 +1,304 @@
|
||||
lockfileVersion: '6.0'
|
||||
lockfileVersion: '9.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
dependencies:
|
||||
jquery:
|
||||
specifier: ^3.7.1
|
||||
version: 3.7.1
|
||||
jsdom:
|
||||
specifier: ^23.0.1
|
||||
version: 23.0.1
|
||||
importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
jquery:
|
||||
specifier: ^3.7.1
|
||||
version: 3.7.1
|
||||
jsdom:
|
||||
specifier: ^23.0.1
|
||||
version: 23.2.0
|
||||
playwright:
|
||||
specifier: ^1.54.1
|
||||
version: 1.54.1
|
||||
|
||||
packages:
|
||||
|
||||
/agent-base@7.1.0:
|
||||
resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==}
|
||||
'@asamuzakjp/css-color@3.2.0':
|
||||
resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==}
|
||||
|
||||
'@asamuzakjp/dom-selector@2.0.2':
|
||||
resolution: {integrity: sha512-x1KXOatwofR6ZAYzXRBL5wrdV0vwNxlTCK9NCuLqAzQYARqGcvFwiJA6A1ERuh+dgeA4Dxm3JBYictIes+SqUQ==}
|
||||
|
||||
'@csstools/color-helpers@5.0.2':
|
||||
resolution: {integrity: sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@csstools/css-calc@2.1.4':
|
||||
resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@csstools/css-parser-algorithms': ^3.0.5
|
||||
'@csstools/css-tokenizer': ^3.0.4
|
||||
|
||||
'@csstools/css-color-parser@3.0.10':
|
||||
resolution: {integrity: sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@csstools/css-parser-algorithms': ^3.0.5
|
||||
'@csstools/css-tokenizer': ^3.0.4
|
||||
|
||||
'@csstools/css-parser-algorithms@3.0.5':
|
||||
resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@csstools/css-tokenizer': ^3.0.4
|
||||
|
||||
'@csstools/css-tokenizer@3.0.4':
|
||||
resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
agent-base@7.1.4:
|
||||
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
|
||||
engines: {node: '>= 14'}
|
||||
dependencies:
|
||||
debug: 4.3.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/asynckit@0.4.0:
|
||||
asynckit@0.4.0:
|
||||
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
||||
dev: false
|
||||
|
||||
/combined-stream@1.0.8:
|
||||
bidi-js@1.0.3:
|
||||
resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
|
||||
|
||||
call-bind-apply-helpers@1.0.2:
|
||||
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
combined-stream@1.0.8:
|
||||
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
dependencies:
|
||||
delayed-stream: 1.0.0
|
||||
dev: false
|
||||
|
||||
/cssstyle@3.0.0:
|
||||
resolution: {integrity: sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==}
|
||||
engines: {node: '>=14'}
|
||||
dependencies:
|
||||
rrweb-cssom: 0.6.0
|
||||
dev: false
|
||||
css-tree@2.3.1:
|
||||
resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==}
|
||||
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
|
||||
|
||||
/data-urls@5.0.0:
|
||||
cssstyle@4.6.0:
|
||||
resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
data-urls@5.0.0:
|
||||
resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
|
||||
engines: {node: '>=18'}
|
||||
dependencies:
|
||||
whatwg-mimetype: 4.0.0
|
||||
whatwg-url: 14.0.0
|
||||
dev: false
|
||||
|
||||
/debug@4.3.4:
|
||||
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
||||
debug@4.4.1:
|
||||
resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
|
||||
engines: {node: '>=6.0'}
|
||||
peerDependencies:
|
||||
supports-color: '*'
|
||||
peerDependenciesMeta:
|
||||
supports-color:
|
||||
optional: true
|
||||
dependencies:
|
||||
ms: 2.1.2
|
||||
dev: false
|
||||
|
||||
/decimal.js@10.4.3:
|
||||
resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==}
|
||||
dev: false
|
||||
decimal.js@10.6.0:
|
||||
resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
|
||||
|
||||
/delayed-stream@1.0.0:
|
||||
delayed-stream@1.0.0:
|
||||
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
dev: false
|
||||
|
||||
/entities@4.5.0:
|
||||
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
||||
dunder-proto@1.0.1:
|
||||
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
entities@6.0.1:
|
||||
resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
|
||||
engines: {node: '>=0.12'}
|
||||
dev: false
|
||||
|
||||
/form-data@4.0.0:
|
||||
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
|
||||
es-define-property@1.0.1:
|
||||
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
es-errors@1.3.0:
|
||||
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
es-object-atoms@1.1.1:
|
||||
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
es-set-tostringtag@2.1.0:
|
||||
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
form-data@4.0.3:
|
||||
resolution: {integrity: sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==}
|
||||
engines: {node: '>= 6'}
|
||||
dependencies:
|
||||
asynckit: 0.4.0
|
||||
combined-stream: 1.0.8
|
||||
mime-types: 2.1.35
|
||||
dev: false
|
||||
|
||||
/html-encoding-sniffer@4.0.0:
|
||||
fsevents@2.3.2:
|
||||
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
os: [darwin]
|
||||
|
||||
function-bind@1.1.2:
|
||||
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
||||
|
||||
get-intrinsic@1.3.0:
|
||||
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
get-proto@1.0.1:
|
||||
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
gopd@1.2.0:
|
||||
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
has-symbols@1.1.0:
|
||||
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
has-tostringtag@1.0.2:
|
||||
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
hasown@2.0.2:
|
||||
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
html-encoding-sniffer@4.0.0:
|
||||
resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
|
||||
engines: {node: '>=18'}
|
||||
dependencies:
|
||||
whatwg-encoding: 3.1.1
|
||||
dev: false
|
||||
|
||||
/http-proxy-agent@7.0.0:
|
||||
resolution: {integrity: sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==}
|
||||
http-proxy-agent@7.0.2:
|
||||
resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
|
||||
engines: {node: '>= 14'}
|
||||
dependencies:
|
||||
agent-base: 7.1.0
|
||||
debug: 4.3.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/https-proxy-agent@7.0.2:
|
||||
resolution: {integrity: sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==}
|
||||
https-proxy-agent@7.0.6:
|
||||
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
|
||||
engines: {node: '>= 14'}
|
||||
dependencies:
|
||||
agent-base: 7.1.0
|
||||
debug: 4.3.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/iconv-lite@0.6.3:
|
||||
iconv-lite@0.6.3:
|
||||
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dependencies:
|
||||
safer-buffer: 2.1.2
|
||||
dev: false
|
||||
|
||||
/is-potential-custom-element-name@1.0.1:
|
||||
is-potential-custom-element-name@1.0.1:
|
||||
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
|
||||
dev: false
|
||||
|
||||
/jquery@3.7.1:
|
||||
jquery@3.7.1:
|
||||
resolution: {integrity: sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==}
|
||||
dev: false
|
||||
|
||||
/jsdom@23.0.1:
|
||||
resolution: {integrity: sha512-2i27vgvlUsGEBO9+/kJQRbtqtm+191b5zAZrU/UezVmnC2dlDAFLgDYJvAEi94T4kjsRKkezEtLQTgsNEsW2lQ==}
|
||||
jsdom@23.2.0:
|
||||
resolution: {integrity: sha512-L88oL7D/8ufIES+Zjz7v0aes+oBMh2Xnh3ygWvL0OaICOomKEPKuPnIfBJekiXr+BHbbMjrWn/xqrDQuxFTeyA==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
canvas: ^2.11.2
|
||||
peerDependenciesMeta:
|
||||
canvas:
|
||||
optional: true
|
||||
dependencies:
|
||||
cssstyle: 3.0.0
|
||||
data-urls: 5.0.0
|
||||
decimal.js: 10.4.3
|
||||
form-data: 4.0.0
|
||||
html-encoding-sniffer: 4.0.0
|
||||
http-proxy-agent: 7.0.0
|
||||
https-proxy-agent: 7.0.2
|
||||
is-potential-custom-element-name: 1.0.1
|
||||
nwsapi: 2.2.7
|
||||
parse5: 7.1.2
|
||||
rrweb-cssom: 0.6.0
|
||||
saxes: 6.0.0
|
||||
symbol-tree: 3.2.4
|
||||
tough-cookie: 4.1.3
|
||||
w3c-xmlserializer: 5.0.0
|
||||
webidl-conversions: 7.0.0
|
||||
whatwg-encoding: 3.1.1
|
||||
whatwg-mimetype: 4.0.0
|
||||
whatwg-url: 14.0.0
|
||||
ws: 8.15.0
|
||||
xml-name-validator: 5.0.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
dev: false
|
||||
|
||||
/mime-db@1.52.0:
|
||||
lru-cache@10.4.3:
|
||||
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
|
||||
|
||||
math-intrinsics@1.1.0:
|
||||
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
mdn-data@2.0.30:
|
||||
resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
|
||||
|
||||
mime-db@1.52.0:
|
||||
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
dev: false
|
||||
|
||||
/mime-types@2.1.35:
|
||||
mime-types@2.1.35:
|
||||
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
dependencies:
|
||||
mime-db: 1.52.0
|
||||
dev: false
|
||||
|
||||
/ms@2.1.2:
|
||||
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
||||
dev: false
|
||||
ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
/nwsapi@2.2.7:
|
||||
resolution: {integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==}
|
||||
dev: false
|
||||
parse5@7.3.0:
|
||||
resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==}
|
||||
|
||||
/parse5@7.1.2:
|
||||
resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==}
|
||||
dependencies:
|
||||
entities: 4.5.0
|
||||
dev: false
|
||||
playwright-core@1.54.1:
|
||||
resolution: {integrity: sha512-Nbjs2zjj0htNhzgiy5wu+3w09YetDx5pkrpI/kZotDlDUaYk0HVA5xrBVPdow4SAUIlhgKcJeJg4GRKW6xHusA==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
/psl@1.9.0:
|
||||
resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}
|
||||
dev: false
|
||||
playwright@1.54.1:
|
||||
resolution: {integrity: sha512-peWpSwIBmSLi6aW2auvrUtf2DqY16YYcCMO8rTVx486jKmDTJg7UAhyrraP98GB8BoPURZP8+nxO7TSd4cPr5g==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
/punycode@2.3.1:
|
||||
psl@1.15.0:
|
||||
resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==}
|
||||
|
||||
punycode@2.3.1:
|
||||
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
||||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
/querystringify@2.2.0:
|
||||
querystringify@2.2.0:
|
||||
resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
|
||||
dev: false
|
||||
|
||||
/requires-port@1.0.0:
|
||||
require-from-string@2.0.2:
|
||||
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
requires-port@1.0.0:
|
||||
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
|
||||
dev: false
|
||||
|
||||
/rrweb-cssom@0.6.0:
|
||||
rrweb-cssom@0.6.0:
|
||||
resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==}
|
||||
dev: false
|
||||
|
||||
/safer-buffer@2.1.2:
|
||||
rrweb-cssom@0.8.0:
|
||||
resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==}
|
||||
|
||||
safer-buffer@2.1.2:
|
||||
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
|
||||
dev: false
|
||||
|
||||
/saxes@6.0.0:
|
||||
saxes@6.0.0:
|
||||
resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
|
||||
engines: {node: '>=v12.22.7'}
|
||||
dependencies:
|
||||
xmlchars: 2.2.0
|
||||
dev: false
|
||||
|
||||
/symbol-tree@3.2.4:
|
||||
source-map-js@1.2.1:
|
||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
symbol-tree@3.2.4:
|
||||
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
|
||||
dev: false
|
||||
|
||||
/tough-cookie@4.1.3:
|
||||
resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==}
|
||||
tough-cookie@4.1.4:
|
||||
resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==}
|
||||
engines: {node: '>=6'}
|
||||
dependencies:
|
||||
psl: 1.9.0
|
||||
punycode: 2.3.1
|
||||
universalify: 0.2.0
|
||||
url-parse: 1.5.10
|
||||
dev: false
|
||||
|
||||
/tr46@5.0.0:
|
||||
resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==}
|
||||
tr46@5.1.1:
|
||||
resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==}
|
||||
engines: {node: '>=18'}
|
||||
dependencies:
|
||||
punycode: 2.3.1
|
||||
dev: false
|
||||
|
||||
/universalify@0.2.0:
|
||||
universalify@0.2.0:
|
||||
resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
|
||||
engines: {node: '>= 4.0.0'}
|
||||
dev: false
|
||||
|
||||
/url-parse@1.5.10:
|
||||
url-parse@1.5.10:
|
||||
resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
|
||||
dependencies:
|
||||
querystringify: 2.2.0
|
||||
requires-port: 1.0.0
|
||||
dev: false
|
||||
|
||||
/w3c-xmlserializer@5.0.0:
|
||||
w3c-xmlserializer@5.0.0:
|
||||
resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
|
||||
engines: {node: '>=18'}
|
||||
dependencies:
|
||||
xml-name-validator: 5.0.0
|
||||
dev: false
|
||||
|
||||
/webidl-conversions@7.0.0:
|
||||
webidl-conversions@7.0.0:
|
||||
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/whatwg-encoding@3.1.1:
|
||||
whatwg-encoding@3.1.1:
|
||||
resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
|
||||
engines: {node: '>=18'}
|
||||
dependencies:
|
||||
iconv-lite: 0.6.3
|
||||
dev: false
|
||||
|
||||
/whatwg-mimetype@4.0.0:
|
||||
whatwg-mimetype@4.0.0:
|
||||
resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
|
||||
engines: {node: '>=18'}
|
||||
dev: false
|
||||
|
||||
/whatwg-url@14.0.0:
|
||||
resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==}
|
||||
whatwg-url@14.2.0:
|
||||
resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==}
|
||||
engines: {node: '>=18'}
|
||||
dependencies:
|
||||
tr46: 5.0.0
|
||||
webidl-conversions: 7.0.0
|
||||
dev: false
|
||||
|
||||
/ws@8.15.0:
|
||||
resolution: {integrity: sha512-H/Z3H55mrcrgjFwI+5jKavgXvwQLtfPCUEp6pi35VhoB0pfcHnSoyuTzkBEZpzq49g1193CUEwIvmsjcotenYw==}
|
||||
ws@8.18.3:
|
||||
resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
@@ -296,13 +308,299 @@ packages:
|
||||
optional: true
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
dev: false
|
||||
|
||||
/xml-name-validator@5.0.0:
|
||||
xml-name-validator@5.0.0:
|
||||
resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
|
||||
engines: {node: '>=18'}
|
||||
dev: false
|
||||
|
||||
/xmlchars@2.2.0:
|
||||
xmlchars@2.2.0:
|
||||
resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
|
||||
dev: false
|
||||
|
||||
snapshots:
|
||||
|
||||
'@asamuzakjp/css-color@3.2.0':
|
||||
dependencies:
|
||||
'@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
|
||||
'@csstools/css-color-parser': 3.0.10(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
|
||||
'@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
|
||||
'@csstools/css-tokenizer': 3.0.4
|
||||
lru-cache: 10.4.3
|
||||
|
||||
'@asamuzakjp/dom-selector@2.0.2':
|
||||
dependencies:
|
||||
bidi-js: 1.0.3
|
||||
css-tree: 2.3.1
|
||||
is-potential-custom-element-name: 1.0.1
|
||||
|
||||
'@csstools/color-helpers@5.0.2': {}
|
||||
|
||||
'@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)':
|
||||
dependencies:
|
||||
'@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
|
||||
'@csstools/css-tokenizer': 3.0.4
|
||||
|
||||
'@csstools/css-color-parser@3.0.10(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)':
|
||||
dependencies:
|
||||
'@csstools/color-helpers': 5.0.2
|
||||
'@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
|
||||
'@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
|
||||
'@csstools/css-tokenizer': 3.0.4
|
||||
|
||||
'@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)':
|
||||
dependencies:
|
||||
'@csstools/css-tokenizer': 3.0.4
|
||||
|
||||
'@csstools/css-tokenizer@3.0.4': {}
|
||||
|
||||
agent-base@7.1.4: {}
|
||||
|
||||
asynckit@0.4.0: {}
|
||||
|
||||
bidi-js@1.0.3:
|
||||
dependencies:
|
||||
require-from-string: 2.0.2
|
||||
|
||||
call-bind-apply-helpers@1.0.2:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
function-bind: 1.1.2
|
||||
|
||||
combined-stream@1.0.8:
|
||||
dependencies:
|
||||
delayed-stream: 1.0.0
|
||||
|
||||
css-tree@2.3.1:
|
||||
dependencies:
|
||||
mdn-data: 2.0.30
|
||||
source-map-js: 1.2.1
|
||||
|
||||
cssstyle@4.6.0:
|
||||
dependencies:
|
||||
'@asamuzakjp/css-color': 3.2.0
|
||||
rrweb-cssom: 0.8.0
|
||||
|
||||
data-urls@5.0.0:
|
||||
dependencies:
|
||||
whatwg-mimetype: 4.0.0
|
||||
whatwg-url: 14.2.0
|
||||
|
||||
debug@4.4.1:
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
|
||||
decimal.js@10.6.0: {}
|
||||
|
||||
delayed-stream@1.0.0: {}
|
||||
|
||||
dunder-proto@1.0.1:
|
||||
dependencies:
|
||||
call-bind-apply-helpers: 1.0.2
|
||||
es-errors: 1.3.0
|
||||
gopd: 1.2.0
|
||||
|
||||
entities@6.0.1: {}
|
||||
|
||||
es-define-property@1.0.1: {}
|
||||
|
||||
es-errors@1.3.0: {}
|
||||
|
||||
es-object-atoms@1.1.1:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
|
||||
es-set-tostringtag@2.1.0:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
get-intrinsic: 1.3.0
|
||||
has-tostringtag: 1.0.2
|
||||
hasown: 2.0.2
|
||||
|
||||
form-data@4.0.3:
|
||||
dependencies:
|
||||
asynckit: 0.4.0
|
||||
combined-stream: 1.0.8
|
||||
es-set-tostringtag: 2.1.0
|
||||
hasown: 2.0.2
|
||||
mime-types: 2.1.35
|
||||
|
||||
fsevents@2.3.2:
|
||||
optional: true
|
||||
|
||||
function-bind@1.1.2: {}
|
||||
|
||||
get-intrinsic@1.3.0:
|
||||
dependencies:
|
||||
call-bind-apply-helpers: 1.0.2
|
||||
es-define-property: 1.0.1
|
||||
es-errors: 1.3.0
|
||||
es-object-atoms: 1.1.1
|
||||
function-bind: 1.1.2
|
||||
get-proto: 1.0.1
|
||||
gopd: 1.2.0
|
||||
has-symbols: 1.1.0
|
||||
hasown: 2.0.2
|
||||
math-intrinsics: 1.1.0
|
||||
|
||||
get-proto@1.0.1:
|
||||
dependencies:
|
||||
dunder-proto: 1.0.1
|
||||
es-object-atoms: 1.1.1
|
||||
|
||||
gopd@1.2.0: {}
|
||||
|
||||
has-symbols@1.1.0: {}
|
||||
|
||||
has-tostringtag@1.0.2:
|
||||
dependencies:
|
||||
has-symbols: 1.1.0
|
||||
|
||||
hasown@2.0.2:
|
||||
dependencies:
|
||||
function-bind: 1.1.2
|
||||
|
||||
html-encoding-sniffer@4.0.0:
|
||||
dependencies:
|
||||
whatwg-encoding: 3.1.1
|
||||
|
||||
http-proxy-agent@7.0.2:
|
||||
dependencies:
|
||||
agent-base: 7.1.4
|
||||
debug: 4.4.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
https-proxy-agent@7.0.6:
|
||||
dependencies:
|
||||
agent-base: 7.1.4
|
||||
debug: 4.4.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
iconv-lite@0.6.3:
|
||||
dependencies:
|
||||
safer-buffer: 2.1.2
|
||||
|
||||
is-potential-custom-element-name@1.0.1: {}
|
||||
|
||||
jquery@3.7.1: {}
|
||||
|
||||
jsdom@23.2.0:
|
||||
dependencies:
|
||||
'@asamuzakjp/dom-selector': 2.0.2
|
||||
cssstyle: 4.6.0
|
||||
data-urls: 5.0.0
|
||||
decimal.js: 10.6.0
|
||||
form-data: 4.0.3
|
||||
html-encoding-sniffer: 4.0.0
|
||||
http-proxy-agent: 7.0.2
|
||||
https-proxy-agent: 7.0.6
|
||||
is-potential-custom-element-name: 1.0.1
|
||||
parse5: 7.3.0
|
||||
rrweb-cssom: 0.6.0
|
||||
saxes: 6.0.0
|
||||
symbol-tree: 3.2.4
|
||||
tough-cookie: 4.1.4
|
||||
w3c-xmlserializer: 5.0.0
|
||||
webidl-conversions: 7.0.0
|
||||
whatwg-encoding: 3.1.1
|
||||
whatwg-mimetype: 4.0.0
|
||||
whatwg-url: 14.2.0
|
||||
ws: 8.18.3
|
||||
xml-name-validator: 5.0.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
|
||||
lru-cache@10.4.3: {}
|
||||
|
||||
math-intrinsics@1.1.0: {}
|
||||
|
||||
mdn-data@2.0.30: {}
|
||||
|
||||
mime-db@1.52.0: {}
|
||||
|
||||
mime-types@2.1.35:
|
||||
dependencies:
|
||||
mime-db: 1.52.0
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
||||
parse5@7.3.0:
|
||||
dependencies:
|
||||
entities: 6.0.1
|
||||
|
||||
playwright-core@1.54.1: {}
|
||||
|
||||
playwright@1.54.1:
|
||||
dependencies:
|
||||
playwright-core: 1.54.1
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.2
|
||||
|
||||
psl@1.15.0:
|
||||
dependencies:
|
||||
punycode: 2.3.1
|
||||
|
||||
punycode@2.3.1: {}
|
||||
|
||||
querystringify@2.2.0: {}
|
||||
|
||||
require-from-string@2.0.2: {}
|
||||
|
||||
requires-port@1.0.0: {}
|
||||
|
||||
rrweb-cssom@0.6.0: {}
|
||||
|
||||
rrweb-cssom@0.8.0: {}
|
||||
|
||||
safer-buffer@2.1.2: {}
|
||||
|
||||
saxes@6.0.0:
|
||||
dependencies:
|
||||
xmlchars: 2.2.0
|
||||
|
||||
source-map-js@1.2.1: {}
|
||||
|
||||
symbol-tree@3.2.4: {}
|
||||
|
||||
tough-cookie@4.1.4:
|
||||
dependencies:
|
||||
psl: 1.15.0
|
||||
punycode: 2.3.1
|
||||
universalify: 0.2.0
|
||||
url-parse: 1.5.10
|
||||
|
||||
tr46@5.1.1:
|
||||
dependencies:
|
||||
punycode: 2.3.1
|
||||
|
||||
universalify@0.2.0: {}
|
||||
|
||||
url-parse@1.5.10:
|
||||
dependencies:
|
||||
querystringify: 2.2.0
|
||||
requires-port: 1.0.0
|
||||
|
||||
w3c-xmlserializer@5.0.0:
|
||||
dependencies:
|
||||
xml-name-validator: 5.0.0
|
||||
|
||||
webidl-conversions@7.0.0: {}
|
||||
|
||||
whatwg-encoding@3.1.1:
|
||||
dependencies:
|
||||
iconv-lite: 0.6.3
|
||||
|
||||
whatwg-mimetype@4.0.0: {}
|
||||
|
||||
whatwg-url@14.2.0:
|
||||
dependencies:
|
||||
tr46: 5.1.1
|
||||
webidl-conversions: 7.0.0
|
||||
|
||||
ws@8.18.3: {}
|
||||
|
||||
xml-name-validator@5.0.0: {}
|
||||
|
||||
xmlchars@2.2.0: {}
|
||||
|
||||
107482
js_node/result/2024HongBao_T2_word.json
Normal file
107482
js_node/result/2024HongBao_T2_word.json
Normal file
File diff suppressed because it is too large
Load Diff
136
js_node/results.json
Normal file
136
js_node/results.json
Normal file
@@ -0,0 +1,136 @@
|
||||
[
|
||||
{
|
||||
"word": "expect",
|
||||
"synos": [
|
||||
{
|
||||
"pos": "vt.",
|
||||
"tran": "期望;指望;认为;预料",
|
||||
"ws": [
|
||||
"promise oneself",
|
||||
"guess",
|
||||
"find",
|
||||
"feel",
|
||||
"make"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pos": "vi.",
|
||||
"tran": "期待;预期",
|
||||
"ws": [
|
||||
"look foward to",
|
||||
"to look forward to"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"word": "run",
|
||||
"synos": [
|
||||
{
|
||||
"pos": "vi.",
|
||||
"tran": "经营;奔跑;运转",
|
||||
"ws": [
|
||||
"go",
|
||||
"fare"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pos": "vt.",
|
||||
"tran": "管理,经营;运行;参赛",
|
||||
"ws": [
|
||||
"conduct",
|
||||
"direct",
|
||||
"control",
|
||||
"supervise",
|
||||
"operate"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pos": "n.",
|
||||
"tran": "奔跑;赛跑;趋向;奔跑的路程",
|
||||
"ws": [
|
||||
"footrace",
|
||||
"tendency to sth"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"word": "happy",
|
||||
"synos": [
|
||||
{
|
||||
"pos": "adj.",
|
||||
"tran": "幸福的;高兴的;巧妙的",
|
||||
"ws": [
|
||||
"pleased",
|
||||
"glad",
|
||||
"blessed",
|
||||
"smart"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"word": "blue",
|
||||
"synos": [
|
||||
{
|
||||
"pos": "adj.",
|
||||
"tran": "[光]蓝色的;忧郁的,沮丧的;下流的",
|
||||
"ws": [
|
||||
"dark",
|
||||
"disappointed",
|
||||
"dirty",
|
||||
"depressed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pos": "n.",
|
||||
"tran": "[光]蓝色",
|
||||
"ws": [
|
||||
"azur",
|
||||
"blau"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"word": "think",
|
||||
"synos": [
|
||||
{
|
||||
"pos": "vt.",
|
||||
"tran": "想;认为;想起;想像;打算",
|
||||
"ws": [
|
||||
"like",
|
||||
"imagine",
|
||||
"expect",
|
||||
"count",
|
||||
"guess"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pos": "vi.",
|
||||
"tran": "想;认为",
|
||||
"ws": [
|
||||
"consider",
|
||||
"ween"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pos": "n.",
|
||||
"tran": "想;想法",
|
||||
"ws": [
|
||||
"idea",
|
||||
"idee"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pos": "adj.",
|
||||
"tran": "思想的",
|
||||
"ws": [
|
||||
"ideological",
|
||||
"ideaistic"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
10762
js_node/source/2024HongBao_T2.json
Normal file
10762
js_node/source/2024HongBao_T2.json
Normal file
File diff suppressed because it is too large
Load Diff
54
js_node/test.cjs
Normal file
54
js_node/test.cjs
Normal file
@@ -0,0 +1,54 @@
|
||||
let path = require("path");
|
||||
let fs = require("fs");
|
||||
const axios = require('axios')
|
||||
|
||||
let str = fs.readFileSync('./save/allNew.min.json', "utf8");
|
||||
let failStr = fs.readFileSync('./fail.txt', "utf8");
|
||||
let source = "./source/";
|
||||
let result = "./result/";
|
||||
|
||||
let allNew = JSON.parse(str)
|
||||
let map = new Map()
|
||||
allNew.forEach(item => {
|
||||
if (item && item.word) {
|
||||
map.set(item.word, item);
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
let failList = JSON.parse(failStr)
|
||||
let list = JSON.parse(str)
|
||||
|
||||
//判断是不是目录
|
||||
const dirs = fs.readdirSync(source)
|
||||
dirs.forEach(dictName => {
|
||||
formatDict(source, dictName)
|
||||
})
|
||||
|
||||
async function sleep(val) {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(resolve, val)
|
||||
})
|
||||
}
|
||||
|
||||
function formatDict(path, name) {
|
||||
try {
|
||||
let newObj = []
|
||||
let str = fs.readFileSync(path + name, "utf8");
|
||||
let list = JSON.parse(str)
|
||||
list.map(v => {
|
||||
if (!v) return
|
||||
let r = map.get(v.word);
|
||||
if (r) {
|
||||
newObj.push(r)
|
||||
}
|
||||
})
|
||||
|
||||
// fs.writeFileSync(result + name.replace('.json', '_word.json'), JSON.stringify(newObj));
|
||||
fs.writeFileSync(result + name.replace('.json', '_word.json'), JSON.stringify(newObj,null, 2));
|
||||
console.log(name, newObj.length)
|
||||
} catch (e) {
|
||||
console.log('err', name, e)
|
||||
}
|
||||
}
|
||||
|
||||
5
js_node/words.txt
Normal file
5
js_node/words.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
expect
|
||||
run
|
||||
happy
|
||||
blue
|
||||
think
|
||||
@@ -3,7 +3,7 @@ const {JSDOM} = require("jsdom");
|
||||
const $ = require("jquery");
|
||||
|
||||
const axios = require('axios')
|
||||
axios('https://dict.youdao.com/result?word=private&lang=en').then(r => {
|
||||
axios('https://dict.youdao.com/result?word=tow&lang=en').then(r => {
|
||||
// console.log('r', r.data)
|
||||
const {window} = new JSDOM(r.data);
|
||||
const $ = require("jquery")(window);
|
||||
@@ -41,7 +41,7 @@ axios('https://dict.youdao.com/result?word=private&lang=en').then(r => {
|
||||
$('.blng_sents_part .trans-container ul li .col2').each(function () {
|
||||
let item = {}
|
||||
item.v = $($(this).children()[0]).find('.sen-eng').text()
|
||||
item.trans = $($(this).children()[1]).find('.sen-ch').text()
|
||||
item.tran = $($(this).children()[1]).find('.sen-ch').text()
|
||||
data.sentences.push(item)
|
||||
})
|
||||
|
||||
@@ -49,28 +49,7 @@ axios('https://dict.youdao.com/result?word=private&lang=en').then(r => {
|
||||
$('.phrs ul li .phrs-content').each(function () {
|
||||
let item = {}
|
||||
item.v = $(this).find('.point').text()
|
||||
item.trans = $(this).find('.phr_trans').text()
|
||||
item.tran = $(this).find('.phr_trans').text()
|
||||
data.phrases.push(item)
|
||||
})
|
||||
|
||||
|
||||
let tabs = $('#catalogue_usage .dict-tabs .tab-item')
|
||||
if (tabs.length && tabs.length > 1) {
|
||||
$(tabs[1]).trigger('click')
|
||||
$(tabs[1]).click()
|
||||
|
||||
setTimeout(() => {
|
||||
//解析同近义词
|
||||
$('.syno ul li .syno-content').each(function () {
|
||||
let item = {}
|
||||
item.v = $(this).find('.synptran').text()
|
||||
item.trans = $(this).find('.clickable').text()
|
||||
data.synos.push(item)
|
||||
})
|
||||
// console.log('data', data)
|
||||
|
||||
console.log($('.syno'))
|
||||
}, 500)
|
||||
}
|
||||
// console.log('data', data)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -286,7 +286,8 @@ const chinaExam = [{
|
||||
translateLanguage: 'common',
|
||||
language: 'en',
|
||||
type: DictType.word,
|
||||
},]
|
||||
},
|
||||
]
|
||||
|
||||
// 青少儿英语
|
||||
const childrenEnglish = [{
|
||||
@@ -784,7 +785,8 @@ const childrenEnglish = [{
|
||||
translateLanguage: 'common',
|
||||
language: 'en',
|
||||
type: DictType.word,
|
||||
},]
|
||||
},
|
||||
]
|
||||
|
||||
const json = [...chinaExam, ...internationalExam, ...childrenEnglish,]
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
{
|
||||
"title": "Breakfast or lunch?",
|
||||
"titleTranslate": "早餐还是午餐?",
|
||||
"text": "It was Sunday. \nI never get up early on Sundays. \nI sometimes stay in bed until lunchtime. \nLast Sunday I got up very late. \nI looked out of the window. \nIt was dark outside. \n'What a day!' I thought. 'It's raining again.'\n Just then, the telephone rang. \nIt was my aunt Lucy. \n'I've just arrived by train,' she said. 'I'm coming to see you.' \n\n'But I'm still having breakfast,' I said. \n\n'What are you doing?' she asked. \n\n'I'm having breakfast,' I repeated. \n\n'Dear me,' she said. 'Do you always get up so late? It's one o'clock!'",
|
||||
"textTranslate": "那是个星期天,\n而在星期天我是从来不早起的,\n有时我要一直躺到吃午饭的时候。\n上个星期天,我起得很晚。\n我望望窗外,\n外面一片昏暗。\n“鬼天气!”我想,“又下雨了。”\n正在这时,电话铃响了。\n是我姑母露西打来的。\n“我刚下火车,”她说,“我这就来看你。”\n\n“但我还在吃早饭,”我说。\n\n“你在干什么?”她问道。\n\n“我正在吃早饭,”我又说了一遍。\n\n“天啊,”她说,“你总是起得这么晚吗?现在已经1点钟了!”",
|
||||
"text": "It was Sunday. \nI never get up early on Sundays. \nI sometimes stay in bed until lunchtime. \nLast Sunday I got up very late. \nI looked out of the window. \nIt was dark outside. \n'What a day!' I thought. 'It's raining again.' \nJust then, the telephone rang. \nIt was my aunt Lucy. \n'I've just arrived by train,' she said. 'I'm coming to see you.' \n\n'But I'm still having breakfast,' I said. \n\n'What are you doing?' she asked. \n\n'I'm having breakfast,' I repeated. \n\n'Dear me,' she said. 'Do you always get up so late? It's one o'clock!'",
|
||||
"textTranslate": "那是个星期天, \n而在星期天我是从来不早起的, \n有时我要一直躺到吃午饭的时候。 \n上个星期天,我起得很晚。 \n我望望窗外, \n外面一片昏暗。 \n“鬼天气!”我想,“又下雨了。” \n正在这时,电话铃响了。 \n是我姑母露西打来的。 \n“我刚下火车,”她说,“我这就来看你。” \n\n“但我还在吃早饭,”我说。 \n\n“你在干什么?”她问道。 \n\n“我正在吃早饭,”我又说了一遍。 \n\n“天啊,”她说,“你总是起得这么晚吗?现在已经1点钟了!”",
|
||||
"newWords": [],
|
||||
"audioSrc": "/public/sound/article/nce2-1/02-Breakfast or Lunch.mp3",
|
||||
"lrcPosition": [[15.9,17.48],[17.75,21.51],[21.54,26.13],[26.58,30.47],[30.86,33.34],[33.34,35.68],[35.68,39.41],[39.41,45.64],[45.64,48.45],[48.45,53.01],[53.01,55.3],[55.3,60.11],[60.11,63.68],[63.4,67.15],[67.3,70.19],[69.98,75.54]],
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
--toolbar-width: 45rem;
|
||||
--toolbar-height: 3.2rem;
|
||||
--panel-width: 24rem;
|
||||
--space: 1.2rem;
|
||||
--space: 1rem;
|
||||
--radius: .5rem;
|
||||
--shadow: rgba(0, 0, 0, 0.08) 0px 4px 12px;
|
||||
--panel-margin-left: calc(50% - var(--practice-wrapper-translateX) / 2 + var(--toolbar-width) / 2 + 2rem);
|
||||
|
||||
@@ -23,6 +23,7 @@ defineEmits<{
|
||||
.empty {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 18rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@@ -39,4 +40,4 @@ defineEmits<{
|
||||
width: 9rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -34,16 +34,8 @@ export const EnKeyboardMap: KeyboardMap = {
|
||||
//生成文章段落数据
|
||||
export function genArticleSectionData(article: Article): number {
|
||||
let text = article.text.trim()
|
||||
if (!text) {
|
||||
// text = "Last week I went to the theatre. I had a very good seat. The play was very interesting. I did not enjoy it. A young man and a young woman were sitting behind me. They were talking loudly. I got very angry. I could not hear the actors. I turned round. I looked at the man and the woman angrily. They did not pay any attention. In the end, I could not bear it. I turned round again. 'I can't hear a word!' I said angrily.\n\n 'It's none of your business,' the young man said rudely. 'This is a private conversation!'"
|
||||
// text = `While it is yet to be seen what direction the second Trump administration will take globally in its China policy, VOA traveled to the main island of Mahe in Seychelles to look at how China and the U.S. have impacted the country, and how each is fairing in that competition for influence there.`
|
||||
// text = "It was Sunday. I never get up early on Sundays. I sometimes stay in bed until lunchtime. Last Sunday I got up very late. I looked out of the window. It was dark outside. 'What a day!' I thought. 'It's raining again.' Just then, the telephone rang. It was my aunt Lucy. 'I've just arrived by train,' she said. 'I'm coming to see you.'\n\n 'But I'm still having breakfast,' I said.\n\n 'What are you doing?' she asked.\n\n 'I'm having breakfast,' I repeated.\n\n 'Dear me,' she said. 'Do you always get up so late? It's one o'clock!'"
|
||||
article.sections = []
|
||||
ElMessage.error('请填写原文!')
|
||||
return
|
||||
}
|
||||
|
||||
console.log('genArticleSectionData',text)
|
||||
// console.log('genArticleSectionData',text)
|
||||
|
||||
let keyboardMap = EnKeyboardMap
|
||||
let sections: Sentence[][] = []
|
||||
@@ -237,7 +229,7 @@ export function genArticleSectionData(article: Article): number {
|
||||
article.sections = sections
|
||||
|
||||
let failCount = 0
|
||||
let translateList = article.textTranslate.split('\n\n')
|
||||
let translateList = article.textTranslate?.split('\n\n') || []
|
||||
for (let i = 0; i < article.sections.length; i++) {
|
||||
let v = article.sections[i]
|
||||
let sList = []
|
||||
@@ -270,7 +262,7 @@ export function genArticleSectionData(article: Article): number {
|
||||
article.textTranslate = translate
|
||||
|
||||
let count = 0
|
||||
if (article.lrcPosition.length) {
|
||||
if (article?.lrcPosition?.length) {
|
||||
article.sections.map((v, i) => {
|
||||
v.map((w, j) => {
|
||||
w.audioPosition = article.lrcPosition[count]
|
||||
|
||||
@@ -130,7 +130,7 @@ export function getCurrentStudyWord() {
|
||||
//取上一次学习的单词用于复习
|
||||
let list = getList(s, e)
|
||||
list.map(item => {
|
||||
if (!store.master.words.map(v => v.word.toLowerCase()).includes(item.word.toLowerCase())) {
|
||||
if (!store.known.words.map(v => v.word.toLowerCase()).includes(item.word.toLowerCase())) {
|
||||
data.review.push(item)
|
||||
}
|
||||
})
|
||||
@@ -148,7 +148,7 @@ export function getCurrentStudyWord() {
|
||||
if (d.length >= Math.floor(dict.perDayStudyNumber / 4)) break
|
||||
}
|
||||
let item = list[i]
|
||||
if (!store.master.words.map(v => v.word.toLowerCase()).includes(item.word.toLowerCase())) {
|
||||
if (!store.known.words.map(v => v.word.toLowerCase()).includes(item.word.toLowerCase())) {
|
||||
d.push(item)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,10 @@ async function getBookDetail(val: DictResource) {
|
||||
}
|
||||
|
||||
async function getBookDetail2(val: Dict) {
|
||||
if (!val.name) {
|
||||
showSearchDialog = true
|
||||
return
|
||||
}
|
||||
runtimeStore.editDict = cloneDeep(val)
|
||||
nav('book-detail')
|
||||
}
|
||||
@@ -51,6 +55,14 @@ function addBook() {
|
||||
runtimeStore.editDict = getDefaultDict()
|
||||
nav('book-detail', {isAdd: true})
|
||||
}
|
||||
|
||||
function startStudy() {
|
||||
if (!base.currentBook.name) {
|
||||
showSearchDialog = true
|
||||
return
|
||||
}
|
||||
router.push('/learn-article')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -58,16 +70,21 @@ function addBook() {
|
||||
<div class="card ">
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="bg-slate-200 p-3 gap-4 rounded-md cursor-pointer flex items-center">
|
||||
<span class="text-lg font-bold">{{ base.currentArticleDict.name }}</span>
|
||||
<BaseIcon @click="showSearchDialog = true" icon="gg:arrows-exchange"/>
|
||||
<span class="text-lg font-bold"
|
||||
@click="getBookDetail2(base.currentBook)">{{
|
||||
base.currentBook.name ?? '请选择书籍开始学习'
|
||||
}}</span>
|
||||
<BaseIcon @click="showSearchDialog = true"
|
||||
:icon="base.currentBook.name?'gg:arrows-exchange':'fluent:add-20-filled'"/>
|
||||
</div>
|
||||
<div class="rounded-xl bg-slate-800 flex items-center py-3 px-5 text-white cursor-pointer"
|
||||
@click="router.push('/learn-article')">
|
||||
:class="base.currentBook.name??'opacity-70 cursor-not-allowed'"
|
||||
@click="startStudy">
|
||||
开始学习
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-5 text-sm">已学习5555个单词的1%</div>
|
||||
<el-progress class="mt-1" :percentage="80" :show-text="false"></el-progress>
|
||||
<div class="mt-5 text-sm">已学习{{ base.currentBook.lastLearnIndex }}篇文章</div>
|
||||
<el-progress class="mt-1" :percentage="base.currentBookProgress" :show-text="false"></el-progress>
|
||||
</div>
|
||||
|
||||
<div class="card flex flex-col">
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import {onMounted, onUnmounted} from "vue";
|
||||
import {Article, DefaultArticle} from "@/types.ts";
|
||||
import {Article, getDefaultArticle} from "@/types.ts";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
|
||||
import List from "@/pages/pc/components/list/List.vue";
|
||||
import Dialog from "@/pages/pc/components/dialog/Dialog.vue";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import {useDisableEventListener, useWindowClick} from "@/hooks/event.ts";
|
||||
import {MessageBox} from "@/utils/MessageBox.tsx";
|
||||
@@ -24,7 +23,7 @@ const emit = defineEmits<{
|
||||
const base = useBaseStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
|
||||
let article = $ref<Article>(cloneDeep(DefaultArticle))
|
||||
let article = $ref<Article>(getDefaultArticle())
|
||||
let show = $ref(false)
|
||||
let editArticleRef: any = $ref()
|
||||
let listEl: any = $ref()
|
||||
@@ -98,7 +97,7 @@ function checkDataChange() {
|
||||
async function add() {
|
||||
let r = await checkDataChange()
|
||||
if (r) {
|
||||
article = cloneDeep(DefaultArticle)
|
||||
article = getDefaultArticle()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +152,7 @@ useWindowClick(() => showExport = false)
|
||||
ref="listEl"
|
||||
v-model:list="runtimeStore.editDict.articles"
|
||||
:select-item="article"
|
||||
@del-select-item="article = cloneDeep(DefaultArticle)"
|
||||
@del-select-item="article = getDefaultArticle()"
|
||||
@select-item="selectArticle"
|
||||
>
|
||||
<template v-slot="{item,index}">
|
||||
|
||||
@@ -5,8 +5,7 @@ import BackIcon from "@/components/BackIcon.vue";
|
||||
import Empty from "@/components/Empty.vue";
|
||||
import ArticleList from "@/pages/pc/components/list/ArticleList.vue";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import {Article, DefaultArticle} from "@/types.ts";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import {Article, getDefaultArticle} from "@/types.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import {useRoute, useRouter} from "vue-router";
|
||||
@@ -21,7 +20,7 @@ const route = useRoute()
|
||||
let isEdit = $ref(false)
|
||||
let isAdd = $ref(false)
|
||||
|
||||
let article: Article = $ref(cloneDeep(DefaultArticle))
|
||||
let article: Article = $ref(getDefaultArticle())
|
||||
let chapterIndex = $ref(-1)
|
||||
|
||||
function handleCheckedChange(val) {
|
||||
@@ -89,7 +88,7 @@ function formClose() {
|
||||
</ArticleList>
|
||||
<Empty v-else/>
|
||||
</div>
|
||||
<div class="right flex-[3] shrink-0 pl-4 overflow-auto">
|
||||
<div class="right flex-[4] shrink-0 pl-4 overflow-auto">
|
||||
<div v-if="chapterIndex>-1">
|
||||
<div class="en-article-family title text-xl">
|
||||
<div class="text-center text-2xl">{{ article.title }}</div>
|
||||
|
||||
@@ -9,7 +9,7 @@ import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {MessageBox} from "@/utils/MessageBox.tsx";
|
||||
import PracticeArticle from "@/pages/pc/practice/practice-article/index.vue";
|
||||
import PracticeArticle from "@/pages/pc/article/practice-article/index.vue";
|
||||
import {ShortcutKey} from "@/types.ts";
|
||||
import DictModal from "@/pages/pc/components/dialog/DictDiglog.vue";
|
||||
import {useStartKeyboardEventListener} from "@/hooks/event.ts";
|
||||
@@ -142,4 +142,4 @@ useStartKeyboardEventListener()
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -30,7 +30,7 @@ interface IProps {
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
article: () => cloneDeep(DefaultArticle),
|
||||
article: () => getDefaultArticle(),
|
||||
type: 'single'
|
||||
})
|
||||
|
||||
@@ -48,7 +48,7 @@ const TranslateEngineOptions = [
|
||||
{value: 'youdao', label: '有道'},
|
||||
]
|
||||
|
||||
let editArticle = $ref<Article>(cloneDeep(DefaultArticle))
|
||||
let editArticle = $ref<Article>(getDefaultArticle())
|
||||
|
||||
watch(() => props.article, val => {
|
||||
editArticle = cloneDeep(val)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {Article, DefaultArticle, Sentence, TranslateEngine} from "@/types.ts";
|
||||
import {Article, getDefaultArticle, Sentence, TranslateEngine} from "@/types.ts";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import EditAbleText from "@/pages/pc/components/EditAbleText.vue";
|
||||
import {Icon} from "@iconify/vue";
|
||||
@@ -21,7 +21,7 @@ interface IProps {
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
article: () => cloneDeep(DefaultArticle),
|
||||
article: () => getDefaultArticle(),
|
||||
type: 'single'
|
||||
})
|
||||
|
||||
@@ -39,13 +39,13 @@ const TranslateEngineOptions = [
|
||||
{value: 'youdao', label: '有道'},
|
||||
]
|
||||
|
||||
let editArticle = $ref<Article>(cloneDeep(DefaultArticle))
|
||||
let editArticle = $ref<Article>(getDefaultArticle())
|
||||
|
||||
watch(() => props.article, val => {
|
||||
editArticle = cloneDeep(val)
|
||||
progress = 0
|
||||
failCount = 0
|
||||
apply()
|
||||
apply(false)
|
||||
}, {immediate: true})
|
||||
|
||||
watch(() => editArticle.text, (s) => {
|
||||
@@ -54,7 +54,16 @@ watch(() => editArticle.text, (s) => {
|
||||
}
|
||||
})
|
||||
|
||||
function apply() {
|
||||
function apply(isHandle: boolean = true) {
|
||||
let text = editArticle.text.trim()
|
||||
if (!text && isHandle) {
|
||||
// text = "Last week I went to the theatre. I had a very good seat. The play was very interesting. I did not enjoy it. A young man and a young woman were sitting behind me. They were talking loudly. I got very angry. I could not hear the actors. I turned round. I looked at the man and the woman angrily. They did not pay any attention. In the end, I could not bear it. I turned round again. 'I can't hear a word!' I said angrily.\n\n 'It's none of your business,' the young man said rudely. 'This is a private conversation!'"
|
||||
// text = `While it is yet to be seen what direction the second Trump administration will take globally in its China policy, VOA traveled to the main island of Mahe in Seychelles to look at how China and the U.S. have impacted the country, and how each is fairing in that competition for influence there.`
|
||||
// text = "It was Sunday. I never get up early on Sundays. I sometimes stay in bed until lunchtime. Last Sunday I got up very late. I looked out of the window. It was dark outside. 'What a day!' I thought. 'It's raining again.' Just then, the telephone rang. It was my aunt Lucy. 'I've just arrived by train,' she said. 'I'm coming to see you.'\n\n 'But I'm still having breakfast,' I said.\n\n 'What are you doing?' she asked.\n\n 'I'm having breakfast,' I repeated.\n\n 'Dear me,' she said. 'Do you always get up so late? It's one o'clock!'"
|
||||
editArticle.sections = []
|
||||
ElMessage.error('请填写原文!')
|
||||
return
|
||||
}
|
||||
failCount = genArticleSectionData(editArticle)
|
||||
}
|
||||
|
||||
@@ -365,13 +374,13 @@ function setStartTime(val: Sentence, i: number, j: number) {
|
||||
</template>
|
||||
</el-popover>
|
||||
<el-button type="primary" @click="splitTranslateText">分句</el-button>
|
||||
<el-button type="primary" @click="apply">应用</el-button>
|
||||
<el-button type="primary" @click="apply(true)">应用</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row flex flex-col gap-2">
|
||||
<div class="title">结果</div>
|
||||
<div class="center">正文、译文与结果均可编辑,修改一处,另外两处会自动同步变动</div>
|
||||
<div class="center">正文、译文与结果均可编辑,编辑后点击应用按钮会自动同步</div>
|
||||
<div class="flex gap-2">
|
||||
<BaseButton>添加音频</BaseButton>
|
||||
<el-upload
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import {onMounted, onUnmounted} from "vue";
|
||||
import {Article, DefaultArticle} from "@/types.ts";
|
||||
import {Article, getDefaultArticle} from "@/types.ts";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
@@ -23,7 +23,7 @@ const emit = defineEmits<{
|
||||
const base = useBaseStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
|
||||
let article = $ref<Article>(cloneDeep(DefaultArticle))
|
||||
let article = $ref<Article>(getDefaultArticle())
|
||||
let show = $ref(false)
|
||||
let editArticleRef: any = $ref()
|
||||
let listEl: any = $ref()
|
||||
@@ -97,7 +97,7 @@ function checkDataChange() {
|
||||
async function add() {
|
||||
let r = await checkDataChange()
|
||||
if (r) {
|
||||
article = cloneDeep(DefaultArticle)
|
||||
article = getDefaultArticle()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ useWindowClick(() => showExport = false)
|
||||
ref="listEl"
|
||||
v-model:list="runtimeStore.editDict.articles"
|
||||
:select-item="article"
|
||||
@del-select-item="article = cloneDeep(DefaultArticle)"
|
||||
@del-select-item="article = getDefaultArticle()"
|
||||
@select-item="selectArticle"
|
||||
>
|
||||
<template v-slot="{item,index}">
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {Article, DefaultArticle} from "@/types.ts";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import {Article, getDefaultArticle} from "@/types.ts";
|
||||
import Dialog from "@/pages/pc/components/dialog/Dialog.vue";
|
||||
import {useDisableEventListener} from "@/hooks/event.ts";
|
||||
import EditArticle2 from "@/pages/pc/article/components/EditArticle2.vue";
|
||||
@@ -12,7 +11,7 @@ interface IProps {
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
article: () => cloneDeep(DefaultArticle),
|
||||
article: () => getDefaultArticle(),
|
||||
modelValue: false
|
||||
})
|
||||
const emit = defineEmits<{
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import {computed, nextTick, onMounted, onUnmounted, watch} from "vue"
|
||||
import {Article, ArticleWord, DefaultArticle, Sentence, Word} from "@/types.ts";
|
||||
import {computed, onMounted, onUnmounted, watch} from "vue"
|
||||
import {Article, ArticleWord, getDefaultArticle, Sentence, Word} from "@/types.ts";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import {usePracticeStore} from "@/stores/practice.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {usePlayBeep, usePlayCorrect, usePlayKeyboardAudio, usePlayWordAudio} from "@/hooks/sound.ts";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import jq from 'jquery'
|
||||
import {_nextTick} from "@/utils";
|
||||
@@ -25,7 +24,7 @@ interface IProps {
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
article: () => cloneDeep(DefaultArticle),
|
||||
article: () => getDefaultArticle(),
|
||||
sectionIndex: 0,
|
||||
sentenceIndex: 0,
|
||||
wordIndex: 0,
|
||||
@@ -455,7 +454,7 @@ let showQuestions = $ref(false)
|
||||
|
||||
<div class="options flex justify-center" v-if="isEnd">
|
||||
<BaseButton
|
||||
v-if="store.currentArticleDict.lastLearnIndex < store.currentArticleDict.articles.length - 1"
|
||||
v-if="store.currentBook.lastLearnIndex < store.currentBook.articles.length - 1"
|
||||
@click="emitter.emit(EventKey.next)">下一章
|
||||
</BaseButton>
|
||||
</div>
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import TypingArticle from "./TypingArticle.vue";
|
||||
import {Article, ArticleItem, ArticleWord, DefaultArticle, DisplayStatistics, ShortcutKey, Word} from "@/types.ts";
|
||||
import {Article, ArticleItem, ArticleWord, DisplayStatistics, getDefaultArticle, ShortcutKey, Word} from "@/types.ts";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import TypingWord from "@/pages/pc/components/TypingWord.vue";
|
||||
import Panel from "../../components/Panel.vue";
|
||||
@@ -33,7 +33,7 @@ let wordData = $ref({
|
||||
})
|
||||
let articleData = $ref({
|
||||
articles: [],
|
||||
article: cloneDeep(DefaultArticle),
|
||||
article: getDefaultArticle(),
|
||||
sectionIndex: 0,
|
||||
sentenceIndex: 0,
|
||||
wordIndex: 0,
|
||||
@@ -41,22 +41,22 @@ let articleData = $ref({
|
||||
})
|
||||
let showEditArticle = $ref(false)
|
||||
let typingArticleRef = $ref<any>()
|
||||
let editArticle = $ref<Article>(cloneDeep(DefaultArticle))
|
||||
let editArticle = $ref<Article>(getDefaultArticle())
|
||||
let articleIsActive = $computed(() => tabIndex === 0)
|
||||
|
||||
function next() {
|
||||
if (!articleIsActive) return
|
||||
if (store.currentArticleDict.lastLearnIndex >= articleData.articles.length - 1) {
|
||||
store.currentArticleDict.lastLearnIndex = 0
|
||||
} else store.currentArticleDict.lastLearnIndex++
|
||||
if (store.currentBook.lastLearnIndex >= articleData.articles.length - 1) {
|
||||
store.currentBook.lastLearnIndex = 0
|
||||
} else store.currentBook.lastLearnIndex++
|
||||
|
||||
emitter.emit(EventKey.resetWord)
|
||||
getCurrentPractice()
|
||||
}
|
||||
|
||||
function init() {
|
||||
if (!store.currentArticleDict.articles.length) return
|
||||
articleData.articles = cloneDeep(store.currentArticleDict.articles)
|
||||
if (!store.currentBook.articles.length) return
|
||||
articleData.articles = cloneDeep(store.currentBook.articles)
|
||||
getCurrentPractice()
|
||||
console.log('inin', articleData.article)
|
||||
|
||||
@@ -64,7 +64,7 @@ function init() {
|
||||
|
||||
function setArticle(val: Article) {
|
||||
let tempVal = cloneDeep(val)
|
||||
articleData.articles[store.currentArticleDict.lastLearnIndex] = tempVal
|
||||
articleData.articles[store.currentBook.lastLearnIndex] = tempVal
|
||||
articleData.article = tempVal
|
||||
statisticsStore.inputWordNumber = 0
|
||||
statisticsStore.wrong = 0
|
||||
@@ -82,13 +82,13 @@ function setArticle(val: Article) {
|
||||
}
|
||||
|
||||
function getCurrentPractice() {
|
||||
// console.log('store.currentArticleDict',store.currentArticleDict)
|
||||
// console.log('store.currentBook',store.currentBook)
|
||||
// return
|
||||
tabIndex = 0
|
||||
articleData.article = cloneDeep(DefaultArticle)
|
||||
articleData.article = getDefaultArticle()
|
||||
|
||||
let currentArticle = articleData.articles[store.currentArticleDict.lastLearnIndex]
|
||||
let tempArticle = {...DefaultArticle, ...currentArticle}
|
||||
let currentArticle = articleData.articles[store.currentBook.lastLearnIndex]
|
||||
let tempArticle = getDefaultArticle(currentArticle)
|
||||
// console.log('article', tempArticle)
|
||||
if (tempArticle.sections.length) {
|
||||
setArticle(tempArticle)
|
||||
@@ -102,9 +102,9 @@ function saveArticle(val: Article) {
|
||||
console.log('saveArticle', val, JSON.stringify(val.lrcPosition))
|
||||
console.log('saveArticle', val.textTranslate)
|
||||
showEditArticle = false
|
||||
let rIndex = store.currentArticleDict.articles.findIndex(v => v.id === val.id)
|
||||
let rIndex = store.currentBook.articles.findIndex(v => v.id === val.id)
|
||||
if (rIndex > -1) {
|
||||
store.currentArticleDict.articles[rIndex] = cloneDeep(val)
|
||||
store.currentBook.articles[rIndex] = cloneDeep(val)
|
||||
}
|
||||
setArticle(val)
|
||||
}
|
||||
@@ -163,7 +163,7 @@ function nextWord(word: ArticleWord) {
|
||||
function handleChangeChapterIndex(val: ArticleItem) {
|
||||
let rIndex = articleData.articles.findIndex(v => v.id === val.item.id)
|
||||
if (rIndex > -1) {
|
||||
store.currentArticleDict.lastLearnIndex = rIndex
|
||||
store.currentBook.lastLearnIndex = rIndex
|
||||
getCurrentPractice()
|
||||
}
|
||||
}
|
||||
@@ -315,11 +315,11 @@ const {playSentenceAudio} = usePlaySentenceAudio()
|
||||
@click="emitter.emit(EventKey.openDictModal,'list')"
|
||||
icon="carbon:change-catalog"/>
|
||||
<div class="title">
|
||||
{{ store.currentArticleDict.name }}
|
||||
{{ store.currentBook.name }}
|
||||
</div>
|
||||
<Tooltip
|
||||
:title="`下一章(${settingStore.shortcutKeyMap[ShortcutKey.NextChapter]})`"
|
||||
v-if="store.currentArticleDict.lastLearnIndex < articleData.articles.length - 1">
|
||||
v-if="store.currentBook.lastLearnIndex < articleData.articles.length - 1">
|
||||
<IconWrapper>
|
||||
<Icon @click="emitter.emit(EventKey.next)" icon="octicon:arrow-right-24"/>
|
||||
</IconWrapper>
|
||||
@@ -434,9 +434,6 @@ const {playSentenceAudio} = usePlaySentenceAudio()
|
||||
icon="tabler:edit"
|
||||
@click="emitter.emit(ShortcutKey.EditArticle)"
|
||||
/>
|
||||
|
||||
|
||||
|
||||
<BaseIcon
|
||||
@click="settingStore.showPanel = !settingStore.showPanel"
|
||||
:title="`面板(${settingStore.shortcutKeyMap[ShortcutKey.TogglePanel]})`"
|
||||
@@ -457,7 +454,6 @@ const {playSentenceAudio} = usePlaySentenceAudio()
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
|
||||
.practice-wrapper {
|
||||
font-size: 0.9rem;
|
||||
width: 100%;
|
||||
@@ -1,222 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import { onMounted, onUnmounted } from "vue"
|
||||
import { usePracticeStore } from "@/stores/practice.ts";
|
||||
import { useSettingStore } from "@/stores/setting.ts";
|
||||
import { Article, ArticleWord, ShortcutKey, Word } from "@/types.ts";
|
||||
import { Icon } from "@iconify/vue";
|
||||
import VolumeSetting from "@/pages/pc/components/toolbar/VolumeSetting.vue";
|
||||
import TranslateSetting from "@/pages/pc/components/toolbar/TranslateSetting.vue";
|
||||
import Tooltip from "@/pages/pc/components/Tooltip.vue";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import IconWrapper from "@/pages/pc/components/IconWrapper.vue";
|
||||
import { useArticleOptions } from "@/hooks/dict.ts";
|
||||
|
||||
const statisticsStore = usePracticeStore()
|
||||
const settingStore = useSettingStore()
|
||||
|
||||
const {
|
||||
isArticleCollect,
|
||||
toggleArticleCollect
|
||||
} = useArticleOptions()
|
||||
|
||||
const emit = defineEmits<{
|
||||
ignore: [],
|
||||
wrong: [val: Word],
|
||||
nextWord: [val: ArticleWord],
|
||||
over: [],
|
||||
edit: [val: Article]
|
||||
}>()
|
||||
|
||||
function format(val: number, suffix: string = '', check: number = -1) {
|
||||
return val === check ? '-' : (val + suffix)
|
||||
}
|
||||
|
||||
const progress = $computed(() => {
|
||||
if (!statisticsStore.total) return 0
|
||||
if (statisticsStore.index > statisticsStore.total) return 100
|
||||
return ((statisticsStore.index / statisticsStore.total) * 100)
|
||||
})
|
||||
|
||||
let speedMinute = $ref(0)
|
||||
let timer = $ref(0)
|
||||
onMounted(() => {
|
||||
timer = setInterval(() => {
|
||||
speedMinute = Math.floor((Date.now() - statisticsStore.startDate) / 1000 / 60)
|
||||
}, 1000)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
timer && clearInterval(timer)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="footer " :class="!settingStore.showToolbar && 'hide'">
|
||||
<div class="bottom ">
|
||||
<div class="flex gap-2">
|
||||
<el-progress
|
||||
class="flex-1"
|
||||
:percentage="progress"
|
||||
:stroke-width="8"
|
||||
:show-text="false" />
|
||||
<el-progress
|
||||
class="flex-1"
|
||||
:percentage="progress"
|
||||
:stroke-width="8"
|
||||
:show-text="false" />
|
||||
</div>
|
||||
<div class="stat">
|
||||
<div class="row">
|
||||
<div class="num">{{ speedMinute }}分钟</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">时间</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ statisticsStore.total }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">单词总数</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ format(statisticsStore.inputWordNumber, '', 0) }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">输入数</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ format(statisticsStore.wrong, '', 0) }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">错误数</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ format(statisticsStore.correctRate, '%') }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">正确率</div>
|
||||
</div>
|
||||
<div class="center flex-col">
|
||||
<div class="text-xl">A private conversation!</div>
|
||||
<div class="options-wrapper">
|
||||
<div class="flex gap-1">
|
||||
<Tooltip
|
||||
:title="`开关默写模式(${settingStore.shortcutKeyMap[ShortcutKey.ToggleDictation]})`"
|
||||
>
|
||||
<IconWrapper>
|
||||
<Icon icon="majesticons:eye-off-line"
|
||||
v-if="settingStore.dictation"
|
||||
@click="settingStore.dictation = false" />
|
||||
<Icon icon="mdi:eye-outline"
|
||||
v-else
|
||||
@click="settingStore.dictation = true" />
|
||||
</IconWrapper>
|
||||
</Tooltip>
|
||||
|
||||
<TranslateSetting />
|
||||
|
||||
<VolumeSetting />
|
||||
|
||||
<BaseIcon
|
||||
:title="`编辑(${settingStore.shortcutKeyMap[ShortcutKey.EditArticle]})`"
|
||||
icon="tabler:edit"
|
||||
@click="emit('edit',)"
|
||||
/>
|
||||
|
||||
<BaseIcon
|
||||
v-if="!isArticleCollect()"
|
||||
class="collect"
|
||||
@click="toggleArticleCollect()"
|
||||
:title="`收藏(${settingStore.shortcutKeyMap[ShortcutKey.ToggleCollect]})`"
|
||||
icon="ph:star" />
|
||||
<BaseIcon
|
||||
v-else
|
||||
class="fill"
|
||||
@click="toggleArticleCollect()"
|
||||
:title="`取消收藏(${settingStore.shortcutKeyMap[ShortcutKey.ToggleCollect]})`"
|
||||
icon="ph:star-fill" />
|
||||
<BaseIcon
|
||||
:title="`下一句(${settingStore.shortcutKeyMap[ShortcutKey.Next]})`"
|
||||
icon="icon-park-outline:go-ahead"
|
||||
@click="emit('over')" />
|
||||
|
||||
<BaseIcon
|
||||
@click="settingStore.showPanel = !settingStore.showPanel"
|
||||
:title="`面板(${settingStore.shortcutKeyMap[ShortcutKey.TogglePanel]})`"
|
||||
icon="tdesign:menu-unfold" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<el-progress :percentage="progress"
|
||||
:stroke-width="8"
|
||||
:show-text="false" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
.footer {
|
||||
width: var(--article-width);
|
||||
margin-bottom: .8rem;
|
||||
transition: all var(--anim-time);
|
||||
position: relative;
|
||||
margin-top: 1rem;
|
||||
|
||||
&.hide {
|
||||
margin-bottom: -6rem;
|
||||
margin-top: 3rem;
|
||||
|
||||
.progress {
|
||||
bottom: calc(100% + 1.8rem);
|
||||
}
|
||||
}
|
||||
|
||||
.bottom {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
border-radius: .6rem;
|
||||
background: var(--color-second-bg);
|
||||
padding: .2rem var(--space) .4rem var(--space);
|
||||
z-index: 2;
|
||||
border: 1px solid var(--color-item-border);
|
||||
box-shadow: var(--shadow);
|
||||
|
||||
.stat {
|
||||
margin-top: .5rem;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: .3rem;
|
||||
width: 5rem;
|
||||
color: gray;
|
||||
|
||||
.line {
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
background: var(--color-sub-gray);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.progress {
|
||||
width: 100%;
|
||||
transition: all .3s;
|
||||
padding: 0 .6rem;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
:deep(.el-progress-bar__inner) {
|
||||
background: var(--color-scrollbar);
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -1,38 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import "vue-activity-calendar/style.css";
|
||||
import {useRouter} from "vue-router";
|
||||
import BasePage from "@/pages/pc/components/BasePage.vue";
|
||||
|
||||
const base = useBaseStore()
|
||||
const router = useRouter()
|
||||
|
||||
function clickEvent(e) {
|
||||
console.log('e', e)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BasePage>
|
||||
<article class="">
|
||||
<div class="text-align-center text-xl font-bold">One good turn deserves another</div>
|
||||
<div>
|
||||
I was having dinner at a restaurant when Tony Steele came in. Tony worked in a lawyer's office years ago, but he is now working at a bank. He gets a good salary, but he always borrows money from his friends and never pays it back. Tony saw me and came and sat at the same table. He has never borrowed money from me. While he was eating, I asked him to lend me twenty pounds. To my surprise, he gave me the money immediately. I have never borrowed any money from you, Tony said, so now you can pay for my dinner!
|
||||
</div>
|
||||
</article>
|
||||
</BasePage>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.card {
|
||||
@apply rounded-xl bg-white p-4 mt-5;
|
||||
}
|
||||
|
||||
.center {
|
||||
@apply flex justify-center items-center;
|
||||
}
|
||||
|
||||
.title {
|
||||
@apply text-lg font-medium;
|
||||
}
|
||||
</style>
|
||||
@@ -1,164 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import Toolbar from "@/pages/pc/components/toolbar/index.vue"
|
||||
import { onMounted, onUnmounted, watch } from "vue";
|
||||
import { usePracticeStore } from "@/stores/practice.ts";
|
||||
import Footer from "@/pages/pc/word/Footer.vue";
|
||||
import { useBaseStore } from "@/stores/base.ts";
|
||||
|
||||
import Statistics from "@/pages/pc/word/Statistics.vue";
|
||||
import { emitter, EventKey } from "@/utils/eventBus.ts";
|
||||
import { useSettingStore } from "@/stores/setting.ts";
|
||||
import { useRuntimeStore } from "@/stores/runtime.ts";
|
||||
import { MessageBox } from "@/utils/MessageBox.tsx";
|
||||
import PracticeArticle from "@/pages/pc/practice/practice-article/index.vue";
|
||||
import { ShortcutKey } from "@/types.ts";
|
||||
import DictModal from "@/pages/pc/components/dialog/DictDiglog.vue";
|
||||
import { useStartKeyboardEventListener } from "@/hooks/event.ts";
|
||||
import useTheme from "@/hooks/theme.ts";
|
||||
|
||||
const statisticsStore = usePracticeStore()
|
||||
const store = useBaseStore()
|
||||
const settingStore = useSettingStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const { toggleTheme } = useTheme()
|
||||
const practiceRef: any = $ref()
|
||||
|
||||
watch(statisticsStore, () => {
|
||||
if (statisticsStore.inputWordNumber < 1) {
|
||||
return statisticsStore.correctRate = -1
|
||||
}
|
||||
if (statisticsStore.wrong > statisticsStore.inputWordNumber) {
|
||||
return statisticsStore.correctRate = 0
|
||||
}
|
||||
statisticsStore.correctRate = 100 - Math.trunc(((statisticsStore.wrong) / (statisticsStore.inputWordNumber)) * 100)
|
||||
})
|
||||
|
||||
|
||||
function test() {
|
||||
MessageBox.confirm(
|
||||
'2您选择了“本地翻译”,但译文内容却为空白,是否修改为“不需要翻译”并保存?',
|
||||
'1提示',
|
||||
() => {
|
||||
console.log('ok')
|
||||
},
|
||||
() => {
|
||||
console.log('cencal')
|
||||
})
|
||||
}
|
||||
|
||||
function write() {
|
||||
// console.log('write')
|
||||
settingStore.dictation = true
|
||||
repeat()
|
||||
}
|
||||
|
||||
//TODO 需要判断是否已忽略
|
||||
function repeat() {
|
||||
// console.log('repeat')
|
||||
emitter.emit(EventKey.resetWord)
|
||||
practiceRef.getCurrentPractice()
|
||||
}
|
||||
|
||||
function prev() {
|
||||
// console.log('next')
|
||||
if (store.currentDict.chapterIndex === 0) {
|
||||
ElMessage.warning('已经在第一章了~')
|
||||
} else {
|
||||
store.currentDict.chapterIndex--
|
||||
repeat()
|
||||
}
|
||||
}
|
||||
|
||||
function toggleShowTranslate() {
|
||||
settingStore.translate = !settingStore.translate
|
||||
}
|
||||
|
||||
function toggleDictation() {
|
||||
settingStore.dictation = !settingStore.dictation
|
||||
}
|
||||
|
||||
function openSetting() {
|
||||
runtimeStore.showSettingModal = true
|
||||
}
|
||||
|
||||
function openDictDetail() {
|
||||
emitter.emit(EventKey.openDictModal, 'detail')
|
||||
}
|
||||
|
||||
function toggleConciseMode() {
|
||||
settingStore.showToolbar = !settingStore.showToolbar
|
||||
settingStore.showPanel = settingStore.showToolbar
|
||||
}
|
||||
|
||||
function togglePanel() {
|
||||
settingStore.showPanel = !settingStore.showPanel
|
||||
}
|
||||
|
||||
function jumpSpecifiedChapter(val: number) {
|
||||
store.currentDict.chapterIndex = val
|
||||
repeat()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
emitter.on(EventKey.write, write)
|
||||
emitter.on(EventKey.repeat, repeat)
|
||||
emitter.on(EventKey.jumpSpecifiedChapter, jumpSpecifiedChapter)
|
||||
|
||||
emitter.on(ShortcutKey.PreviousChapter, prev)
|
||||
emitter.on(ShortcutKey.RepeatChapter, repeat)
|
||||
emitter.on(ShortcutKey.DictationChapter, write)
|
||||
emitter.on(ShortcutKey.ToggleShowTranslate, toggleShowTranslate)
|
||||
emitter.on(ShortcutKey.ToggleDictation, toggleDictation)
|
||||
emitter.on(ShortcutKey.OpenSetting, openSetting)
|
||||
emitter.on(ShortcutKey.OpenDictDetail, openDictDetail)
|
||||
emitter.on(ShortcutKey.ToggleTheme, toggleTheme)
|
||||
emitter.on(ShortcutKey.ToggleConciseMode, toggleConciseMode)
|
||||
emitter.on(ShortcutKey.TogglePanel, togglePanel)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
emitter.off(EventKey.write, write)
|
||||
emitter.off(EventKey.repeat, repeat)
|
||||
emitter.off(EventKey.jumpSpecifiedChapter, jumpSpecifiedChapter)
|
||||
|
||||
emitter.off(ShortcutKey.PreviousChapter, prev)
|
||||
emitter.off(ShortcutKey.RepeatChapter, repeat)
|
||||
emitter.off(ShortcutKey.DictationChapter, write)
|
||||
emitter.off(ShortcutKey.ToggleShowTranslate, toggleShowTranslate)
|
||||
emitter.off(ShortcutKey.ToggleDictation, toggleDictation)
|
||||
emitter.off(ShortcutKey.OpenSetting, openSetting)
|
||||
emitter.off(ShortcutKey.OpenDictDetail, openDictDetail)
|
||||
emitter.off(ShortcutKey.ToggleTheme, toggleTheme)
|
||||
emitter.off(ShortcutKey.ToggleConciseMode, toggleConciseMode)
|
||||
emitter.off(ShortcutKey.TogglePanel, togglePanel)
|
||||
})
|
||||
|
||||
useStartKeyboardEventListener()
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class="practice-wrapper">
|
||||
<Toolbar />
|
||||
<PracticeArticle ref="practiceRef" />
|
||||
<Footer />
|
||||
</div>
|
||||
<DictModal />
|
||||
<Statistics />
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.practice-wrapper {
|
||||
font-size: 0.9rem;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
//padding-right: var(--practice-wrapper-padding-right);
|
||||
transform: translateX(var(--practice-wrapper-translateX));
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
@@ -1,163 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import { onMounted, onUnmounted, watch } from "vue";
|
||||
import { usePracticeStore } from "@/stores/practice.ts";
|
||||
import { useBaseStore } from "@/stores/base.ts";
|
||||
|
||||
import Statistics from "@/pages/pc/word/Statistics.vue";
|
||||
import { emitter, EventKey } from "@/utils/eventBus.ts";
|
||||
import { useSettingStore } from "@/stores/setting.ts";
|
||||
import { useRuntimeStore } from "@/stores/runtime.ts";
|
||||
import { MessageBox } from "@/utils/MessageBox.tsx";
|
||||
import PracticeArticle from "@/pages/pc/practice/practice-article/index.vue";
|
||||
import { ShortcutKey } from "@/types.ts";
|
||||
import DictModal from "@/pages/pc/components/dialog/DictDiglog.vue";
|
||||
import { useStartKeyboardEventListener } from "@/hooks/event.ts";
|
||||
import useTheme from "@/hooks/theme.ts";
|
||||
import ArticleFooter from "@/pages/pc/article/ArticleFooter.vue";
|
||||
|
||||
const statisticsStore = usePracticeStore()
|
||||
const store = useBaseStore()
|
||||
const settingStore = useSettingStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const { toggleTheme } = useTheme()
|
||||
const practiceRef: any = $ref()
|
||||
|
||||
watch(statisticsStore, () => {
|
||||
if (statisticsStore.inputWordNumber < 1) {
|
||||
return statisticsStore.correctRate = -1
|
||||
}
|
||||
if (statisticsStore.wrong > statisticsStore.inputWordNumber) {
|
||||
return statisticsStore.correctRate = 0
|
||||
}
|
||||
statisticsStore.correctRate = 100 - Math.trunc(((statisticsStore.wrong) / (statisticsStore.inputWordNumber)) * 100)
|
||||
})
|
||||
|
||||
|
||||
function test() {
|
||||
MessageBox.confirm(
|
||||
'2您选择了“本地翻译”,但译文内容却为空白,是否修改为“不需要翻译”并保存?',
|
||||
'1提示',
|
||||
() => {
|
||||
console.log('ok')
|
||||
},
|
||||
() => {
|
||||
console.log('cencal')
|
||||
})
|
||||
}
|
||||
|
||||
function write() {
|
||||
// console.log('write')
|
||||
settingStore.dictation = true
|
||||
repeat()
|
||||
}
|
||||
|
||||
//TODO 需要判断是否已忽略
|
||||
function repeat() {
|
||||
// console.log('repeat')
|
||||
emitter.emit(EventKey.resetWord)
|
||||
practiceRef.getCurrentPractice()
|
||||
}
|
||||
|
||||
function prev() {
|
||||
// console.log('next')
|
||||
if (store.currentDict.chapterIndex === 0) {
|
||||
ElMessage.warning('已经在第一章了~')
|
||||
} else {
|
||||
store.currentDict.chapterIndex--
|
||||
repeat()
|
||||
}
|
||||
}
|
||||
|
||||
function toggleShowTranslate() {
|
||||
settingStore.translate = !settingStore.translate
|
||||
}
|
||||
|
||||
function toggleDictation() {
|
||||
settingStore.dictation = !settingStore.dictation
|
||||
}
|
||||
|
||||
function openSetting() {
|
||||
runtimeStore.showSettingModal = true
|
||||
}
|
||||
|
||||
function openDictDetail() {
|
||||
emitter.emit(EventKey.openDictModal, 'detail')
|
||||
}
|
||||
|
||||
function toggleConciseMode() {
|
||||
settingStore.showToolbar = !settingStore.showToolbar
|
||||
settingStore.showPanel = settingStore.showToolbar
|
||||
}
|
||||
|
||||
function togglePanel() {
|
||||
settingStore.showPanel = !settingStore.showPanel
|
||||
}
|
||||
|
||||
function jumpSpecifiedChapter(val: number) {
|
||||
store.currentDict.chapterIndex = val
|
||||
repeat()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
emitter.on(EventKey.write, write)
|
||||
emitter.on(EventKey.repeat, repeat)
|
||||
emitter.on(EventKey.jumpSpecifiedChapter, jumpSpecifiedChapter)
|
||||
|
||||
emitter.on(ShortcutKey.PreviousChapter, prev)
|
||||
emitter.on(ShortcutKey.RepeatChapter, repeat)
|
||||
emitter.on(ShortcutKey.DictationChapter, write)
|
||||
emitter.on(ShortcutKey.ToggleShowTranslate, toggleShowTranslate)
|
||||
emitter.on(ShortcutKey.ToggleDictation, toggleDictation)
|
||||
emitter.on(ShortcutKey.OpenSetting, openSetting)
|
||||
emitter.on(ShortcutKey.OpenDictDetail, openDictDetail)
|
||||
emitter.on(ShortcutKey.ToggleTheme, toggleTheme)
|
||||
emitter.on(ShortcutKey.ToggleConciseMode, toggleConciseMode)
|
||||
emitter.on(ShortcutKey.TogglePanel, togglePanel)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
emitter.off(EventKey.write, write)
|
||||
emitter.off(EventKey.repeat, repeat)
|
||||
emitter.off(EventKey.jumpSpecifiedChapter, jumpSpecifiedChapter)
|
||||
|
||||
emitter.off(ShortcutKey.PreviousChapter, prev)
|
||||
emitter.off(ShortcutKey.RepeatChapter, repeat)
|
||||
emitter.off(ShortcutKey.DictationChapter, write)
|
||||
emitter.off(ShortcutKey.ToggleShowTranslate, toggleShowTranslate)
|
||||
emitter.off(ShortcutKey.ToggleDictation, toggleDictation)
|
||||
emitter.off(ShortcutKey.OpenSetting, openSetting)
|
||||
emitter.off(ShortcutKey.OpenDictDetail, openDictDetail)
|
||||
emitter.off(ShortcutKey.ToggleTheme, toggleTheme)
|
||||
emitter.off(ShortcutKey.ToggleConciseMode, toggleConciseMode)
|
||||
emitter.off(ShortcutKey.TogglePanel, togglePanel)
|
||||
})
|
||||
|
||||
useStartKeyboardEventListener()
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class="practice-wrapper">
|
||||
<PracticeArticle ref="practiceRef" />
|
||||
<ArticleFooter />
|
||||
</div>
|
||||
<DictModal />
|
||||
<Statistics />
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.practice-wrapper {
|
||||
font-size: 0.9rem;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
//padding-right: var(--practice-wrapper-padding-right);
|
||||
transform: translateX(var(--practice-wrapper-translateX));
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
@@ -46,7 +46,7 @@ watch(() => settingStore.load, (n) => {
|
||||
v-if="show">
|
||||
<div class="notice">
|
||||
坚持练习,提高外语能力。将
|
||||
<span class="active">「Typing Word」</span>
|
||||
<span class="active">「Type Words」</span>
|
||||
保存为书签,永不迷失!
|
||||
</div>
|
||||
<div class="wrapper">
|
||||
@@ -190,4 +190,4 @@ watch(() => settingStore.load, (n) => {
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -47,7 +47,7 @@ function toggle() {
|
||||
/>
|
||||
<div class="options">
|
||||
<BaseButton @click="toggle">取消</BaseButton>
|
||||
<BaseButton @click="save">保存</BaseButton>
|
||||
<BaseButton @click="save">应用</BaseButton>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -76,4 +76,4 @@ function toggle() {
|
||||
font-size: 1.2rem;
|
||||
min-height: 1.1rem;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -85,7 +85,7 @@ function changeCollect() {
|
||||
<div class="tab" :class="tabIndex === 1 && 'active'" @click="tabIndex = 1">收藏</div>
|
||||
<div class="tab" :class="tabIndex === 2 && 'active'" @click="tabIndex = 2">{{ store.simple.name }}</div>
|
||||
<div class="tab" :class="tabIndex === 3 && 'active'" @click="tabIndex = 3">{{ store.wrong.name }}</div>
|
||||
<div class="tab" :class="tabIndex === 4 && 'active'" @click="tabIndex = 4">{{ store.master.name }}</div>
|
||||
<div class="tab" :class="tabIndex === 4 && 'active'" @click="tabIndex = 4">{{ store.known.name }}</div>
|
||||
</div>
|
||||
<Tooltip
|
||||
:title="`关闭(${settingStore.shortcutKeyMap[ShortcutKey.TogglePanel]})`"
|
||||
|
||||
@@ -447,7 +447,7 @@ function importData(e) {
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="tabIndex === 5" class="about">
|
||||
<h1>Typing Word</h1>
|
||||
<h1>Type Words</h1>
|
||||
<p>
|
||||
本项目完全开源!好用请大家多多点Star!
|
||||
</p>
|
||||
|
||||
@@ -84,11 +84,11 @@ defineExpose({scrollToBottom, scrollToItem})
|
||||
.search {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 0 var(--space);
|
||||
padding-right: var(--space);
|
||||
}
|
||||
|
||||
.translate {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -5,7 +5,7 @@ import BaseButton from "@/components/BaseButton.vue";
|
||||
import Empty from "@/components/Empty.vue";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import {Article, DefaultArticle, Dict, DictResource, DictType, getDefaultDict, Sort, TranslateType} from "@/types.ts";
|
||||
import {Article, Dict, DictResource, DictType, getDefaultArticle, getDefaultDict, Sort} from "@/types.ts";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import EditBatchArticleModal from "@/pages/pc/article/components/EditBatchArticleModal.vue";
|
||||
import {Icon} from "@iconify/vue";
|
||||
@@ -19,7 +19,6 @@ import {MessageBox} from "@/utils/MessageBox.tsx";
|
||||
import {syncMyDictList} from "@/hooks/dict.ts";
|
||||
import {useWindowClick} from "@/hooks/event.ts";
|
||||
import ArticleList from "@/pages/pc/components/list/ArticleList.vue";
|
||||
import * as copy from "copy-to-clipboard";
|
||||
import {getTranslateText} from "@/hooks/article.ts";
|
||||
import {_copy} from "@/utils";
|
||||
|
||||
@@ -31,7 +30,7 @@ const store = useBaseStore()
|
||||
const settingStore = useSettingStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
let chapterIndex = $ref(-1)
|
||||
let article: Article = $ref(cloneDeep(DefaultArticle))
|
||||
let article: Article = $ref(getDefaultArticle())
|
||||
let isEditDict = $ref(false)
|
||||
let showExport = $ref(false)
|
||||
let loading = $ref(false)
|
||||
@@ -104,7 +103,7 @@ function delArticle(index: number) {
|
||||
article = runtimeStore.editDict.articles[chapterIndex]
|
||||
}
|
||||
} else {
|
||||
article = cloneDeep(DefaultArticle)
|
||||
article = getDefaultArticle()
|
||||
chapterIndex = -1
|
||||
}
|
||||
syncMyDictList(runtimeStore.editDict)
|
||||
@@ -117,7 +116,7 @@ async function resetDict() {
|
||||
'提示',
|
||||
async () => {
|
||||
chapterIndex = -1
|
||||
article = cloneDeep(DefaultArticle)
|
||||
article = getDefaultArticle()
|
||||
if (runtimeStore.editDict.url) {
|
||||
runtimeStore.editDict.sort = Sort.normal
|
||||
runtimeStore.editDict.chapterWordNumber = settingStore.chapterWordNumber
|
||||
@@ -150,15 +149,14 @@ function importData(e: any) {
|
||||
if (res.length) {
|
||||
let articles = res.map(v => {
|
||||
if (v['原文标题'] && v['原文正文']) {
|
||||
let article: Article = {
|
||||
...DefaultArticle,
|
||||
let article: Article = getDefaultArticle({
|
||||
id: nanoid(6),
|
||||
checked: false,
|
||||
title: String(v['原文标题']),
|
||||
text: String(v['原文正文']),
|
||||
titleTranslate: String(v['译文标题']),
|
||||
textTranslate: String(v['译文正文']),
|
||||
}
|
||||
})
|
||||
return article
|
||||
}
|
||||
}).filter(v => v)
|
||||
@@ -276,7 +274,7 @@ defineExpose({getDictDetail, add, editDict})
|
||||
</div>
|
||||
<template v-if="!isPinDict">
|
||||
<BaseIcon icon="tabler:edit" @click='editDict'/>
|
||||
<!-- <BaseIcon icon="ph:star" @click='no'/>-->
|
||||
<!-- <BaseIcon icon="ph:star" @click='no'/>-->
|
||||
<BaseButton size="small" v-if="runtimeStore.editDict.isCustom" @click="resetDict">恢复默认</BaseButton>
|
||||
</template>
|
||||
<div class="import hvr-grow">
|
||||
|
||||
@@ -40,14 +40,14 @@ const {toggleTheme} = useTheme()
|
||||
<Icon icon="ph:article-ny-times"/>
|
||||
<span v-if="settingStore.sideExpand">文章</span>
|
||||
</div>
|
||||
<div class="row" @click="router.push('/article2')">
|
||||
<Icon icon="healthicons:i-exam-multiple-choice-outline"/>
|
||||
<span v-if="settingStore.sideExpand">试卷</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<Icon icon="mdi-light:forum"/>
|
||||
<span v-if="settingStore.sideExpand">社区</span>
|
||||
</div>
|
||||
<!-- <div class="row" @click="router.push('/article2')">-->
|
||||
<!-- <Icon icon="healthicons:i-exam-multiple-choice-outline"/>-->
|
||||
<!-- <span v-if="settingStore.sideExpand">试卷</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="row">-->
|
||||
<!-- <Icon icon="mdi-light:forum"/>-->
|
||||
<!-- <span v-if="settingStore.sideExpand">社区</span>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
<div class="bottom flex justify-evenly ">
|
||||
<BaseIcon
|
||||
|
||||
@@ -162,21 +162,9 @@ function changePerDayStudyNumber() {
|
||||
我的词典
|
||||
</div>
|
||||
<div class="grid grid-cols-6 gap-4 mt-4">
|
||||
<div class="book" @click="nav('edit-word-dict',{type:0})">
|
||||
<span>收藏</span>
|
||||
<div class="absolute bottom-4 right-4">{{ store.collectWord.words.length }}个词</div>
|
||||
</div>
|
||||
<div class="book" @click="nav('edit-word-dict',{type:1})">
|
||||
<span>错词本</span>
|
||||
<div class="absolute bottom-4 right-4">{{ store.wrong.words.length }}个词</div>
|
||||
</div>
|
||||
<div class="book" @click="nav('edit-word-dict',{type:2})">
|
||||
<span>简单词</span>
|
||||
<div class="absolute bottom-4 right-4">{{ store.simple.words.length }}个词</div>
|
||||
</div>
|
||||
<div class="book" @click="nav('edit-word-dict',{type:3})">
|
||||
<span>已掌握</span>
|
||||
<div class="absolute bottom-4 right-4">{{ store.master.words.length }}个词</div>
|
||||
<div class="book" v-for="item in store.word.bookList" @click="nav('edit-word-dict',{type:item.type})">
|
||||
<span>{{ item.name }}</span>
|
||||
<div class="absolute bottom-4 right-4">{{ item.words.length }}个词</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -238,7 +226,7 @@ function changePerDayStudyNumber() {
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.target-modal {
|
||||
.target-modal {
|
||||
width: 30rem;
|
||||
padding: var(--space);
|
||||
padding-top: 0;
|
||||
|
||||
@@ -1,25 +1,11 @@
|
||||
import * as VueRouter from 'vue-router'
|
||||
import {RouteRecordRaw} from 'vue-router'
|
||||
import Mobile from '@/pages/mobile/index.vue'
|
||||
import MobilePractice from '@/pages/mobile/practice/index.vue'
|
||||
import Test from "@/pages/test/test.vue";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import DictDetail from "@/pages/mobile/DictDetail.vue";
|
||||
import SetDictPlan from "@/pages/mobile/SetDictPlan.vue";
|
||||
import Setting from "@/pages/mobile/my/setting/Setting.vue";
|
||||
import DataManage from "@/pages/mobile/my/DataManage.vue";
|
||||
import CollectPage from "@/pages/mobile/my/CollectPage.vue";
|
||||
import WrongPage from "@/pages/mobile/my/WrongPage.vue";
|
||||
import SimplePage from "@/pages/mobile/my/SimplePage.vue";
|
||||
import About from "@/pages/mobile/my/About.vue";
|
||||
import Feedback from "@/pages/mobile/my/Feedback.vue";
|
||||
import MusicSetting from "@/pages/mobile/my/setting/MusicSetting.vue";
|
||||
import OtherSetting from "@/pages/mobile/my/setting/OtherSetting.vue";
|
||||
import WordHome from "@/pages/pc/word/WordHome.vue";
|
||||
import PC from "@/pages/pc/index.vue";
|
||||
import Dict2 from '@/pages/pc/dict2/index.vue'
|
||||
import ArticleIndex from "@/pages/pc/article/ArticleIndex.vue";
|
||||
import Article2Index from "@/pages/pc/article2/ArticleIndex.vue";
|
||||
import HomeIndex from "@/pages/pc/home/HomeIndex.vue";
|
||||
import LearnArticle from "@/pages/pc/article/LearnArticle.vue";
|
||||
import EditWordDict from "@/pages/pc/word/EditWordDict.vue";
|
||||
@@ -39,7 +25,6 @@ export const routes: RouteRecordRaw[] = [
|
||||
{path: 'edit-word-dict', component: EditWordDict},
|
||||
{path: 'dict', component: Dict2},
|
||||
{path: 'article', component: ArticleIndex},
|
||||
{path: 'article2', component: Article2Index},
|
||||
{path: 'edit-article', component: EditArticlePage},
|
||||
{path: 'batch-edit-article', component: BatchEditArticlePage},
|
||||
{path: 'learn-article', component: LearnArticle},
|
||||
|
||||
@@ -16,22 +16,17 @@ export interface BaseState {
|
||||
simpleWords: string[],
|
||||
load: boolean
|
||||
|
||||
articleDictList?: Dict[]
|
||||
|
||||
commonDictList: any[],
|
||||
wordDictList?: Dict[],
|
||||
currentStudy?: {
|
||||
word: {
|
||||
dictIndex: number,
|
||||
},
|
||||
article: {
|
||||
dictIndex: number,
|
||||
}
|
||||
},
|
||||
// word: {
|
||||
// studyIndex: number,
|
||||
// dictList: [],
|
||||
// },
|
||||
word: {
|
||||
studyIndex: number,
|
||||
bookList: Dict[],
|
||||
},
|
||||
article: {
|
||||
bookList: Dict[],
|
||||
studyIndex: number,
|
||||
@@ -39,194 +34,14 @@ export interface BaseState {
|
||||
}
|
||||
|
||||
export const DefaultBaseState = (): BaseState => ({
|
||||
commonDictList: [
|
||||
getDefaultDict(),
|
||||
{
|
||||
...getDefaultDict(),
|
||||
index: 1,
|
||||
name: '收藏', type: DictType.collectWord, words: [
|
||||
{
|
||||
"id": "pharmacy",
|
||||
"word": "pharmacy",
|
||||
"trans": [
|
||||
{
|
||||
"cn": "n.药房,配药学,药学,制药业,一批备用药品"
|
||||
}
|
||||
],
|
||||
"phonetic0": "ˈfɑ:məsi",
|
||||
"phonetic1": "ˈfɑ:rməsi"
|
||||
},
|
||||
{
|
||||
"id": "foregone",
|
||||
"word": "foregone",
|
||||
"trans": [
|
||||
{
|
||||
"cn": "过去的;先前的;预知的;预先决定的"
|
||||
},
|
||||
{
|
||||
"cn": "发生在…之前(forego的过去分词)"
|
||||
}
|
||||
],
|
||||
"phonetic0": "fɔː'gɒn",
|
||||
"phonetic1": "'fɔrɡɔn"
|
||||
},
|
||||
|
||||
{
|
||||
"id": "calculate",
|
||||
"word": "calculate",
|
||||
"trans": [
|
||||
{
|
||||
"cn": "vt.& vi.计算,估计,打算,计划,旨在"
|
||||
},
|
||||
{
|
||||
"cn": "vt.预测,推测"
|
||||
}
|
||||
],
|
||||
"phonetic0": "ˈkælkjuleɪt",
|
||||
"phonetic1": "ˈkælkjəˌlet"
|
||||
},
|
||||
{
|
||||
"id": "compete",
|
||||
"word": "compete",
|
||||
"trans": [
|
||||
{
|
||||
"cn": "vi.竞赛,竞争,比得上,参加比赛(或竞赛)"
|
||||
}
|
||||
],
|
||||
"phonetic0": "kəmˈpi:t",
|
||||
"phonetic1": "kəmˈpit"
|
||||
},
|
||||
{
|
||||
"id": "furnish",
|
||||
"word": "furnish",
|
||||
"trans": [
|
||||
{
|
||||
"cn": "vt.陈设,布置,提供,供应,装修(房屋)"
|
||||
}
|
||||
],
|
||||
"phonetic0": "ˈfɜ:nɪʃ",
|
||||
"phonetic1": "ˈfɜ:rnɪʃ"
|
||||
},
|
||||
], statistics: []
|
||||
},
|
||||
{
|
||||
...getDefaultDict(),
|
||||
index: 2, name: '收藏', type: DictType.collectArticle, articles: [], statistics: []
|
||||
},
|
||||
{
|
||||
...getDefaultDict(),
|
||||
index: 3, name: '简单词', type: DictType.simple, words: [], statistics: []
|
||||
},
|
||||
{
|
||||
...getDefaultDict(),
|
||||
index: 4, name: '错词', type: DictType.wrong, words: [], statistics: []
|
||||
},
|
||||
{
|
||||
...getDefaultDict(),
|
||||
index: 5, name: '已掌握', type: DictType.master, words: [], statistics: []
|
||||
},
|
||||
],
|
||||
articleDictList: [
|
||||
{
|
||||
...getDefaultDict(),
|
||||
id: 'article_nce2',
|
||||
name: "新概念英语2-课文",
|
||||
description: '新概念英语2-课文',
|
||||
category: '英语学习',
|
||||
tags: ['新概念英语'],
|
||||
url: 'NCE_2.json',
|
||||
translateLanguage: 'common',
|
||||
language: 'en',
|
||||
type: DictType.article,
|
||||
resourceId: 'article_nce2',
|
||||
length: 96,
|
||||
lastLearnIndex: 1
|
||||
},
|
||||
],
|
||||
wordDictList: [
|
||||
{
|
||||
...getDefaultDict(),
|
||||
"id": 137,
|
||||
"name": "新概念英语(新版)-2",
|
||||
"description": "新概念英语新版第二册",
|
||||
"length": 862,
|
||||
"version": 1,
|
||||
"fileName": "nce-new-2",
|
||||
"category": "青少年英语",
|
||||
"langType": 0,
|
||||
"tranType": 1,
|
||||
"userId": null,
|
||||
"tags": [
|
||||
"新概念英语"
|
||||
],
|
||||
"langTypeStr": "en",
|
||||
"tranTypeStr": "zh",
|
||||
"dictType": DictType.word,
|
||||
statistics: []
|
||||
},
|
||||
],
|
||||
commonDictList: [],
|
||||
wordDictList: [],
|
||||
currentStudy: {
|
||||
word: {
|
||||
dictIndex: 0,
|
||||
},
|
||||
article: {
|
||||
dictIndex: 0,
|
||||
},
|
||||
},
|
||||
myDictList: [
|
||||
{
|
||||
...getDefaultDict(),
|
||||
id: 'collect',
|
||||
name: '收藏',
|
||||
type: DictType.collect,
|
||||
category: '自带字典',
|
||||
tags: ['自带'],
|
||||
isCustom: true,
|
||||
},
|
||||
{
|
||||
...getDefaultDict(),
|
||||
id: 'skip',
|
||||
name: '简单词',
|
||||
type: DictType.simple,
|
||||
category: '自带字典',
|
||||
isCustom: true,
|
||||
},
|
||||
{
|
||||
...getDefaultDict(),
|
||||
id: 'wrong',
|
||||
name: '错词本',
|
||||
type: DictType.wrong,
|
||||
category: '自带字典',
|
||||
isCustom: true,
|
||||
},
|
||||
{
|
||||
...getDefaultDict(),
|
||||
id: 'cet4',
|
||||
name: 'CET-4',
|
||||
description: '大学英语四级词库',
|
||||
category: '中国考试',
|
||||
tags: ['大学英语'],
|
||||
url: 'CET4_T.json',
|
||||
length: 2607,
|
||||
translateLanguage: 'common',
|
||||
language: 'en',
|
||||
type: DictType.word
|
||||
},
|
||||
{
|
||||
...getDefaultDict(),
|
||||
id: 'article_nce2',
|
||||
name: "新概念英语2-课文",
|
||||
description: '新概念英语2-课文',
|
||||
category: '英语学习',
|
||||
tags: ['新概念英语'],
|
||||
url: 'NCE_2.json',
|
||||
translateLanguage: 'common',
|
||||
language: 'en',
|
||||
type: DictType.article,
|
||||
resourceId: 'article_nce2',
|
||||
length: 96
|
||||
},
|
||||
],
|
||||
myDictList: [],
|
||||
current: {
|
||||
index: 4,
|
||||
practiceType: DictType.article,
|
||||
@@ -239,6 +54,96 @@ export const DefaultBaseState = (): BaseState => ({
|
||||
'the', 'that', 'this', 'to', 'of', 'for', 'and', 'at', 'not', 'no', 'yes',
|
||||
],
|
||||
load: false,
|
||||
word: {
|
||||
bookList: [
|
||||
getDefaultDict({
|
||||
index: 1,
|
||||
name: '收藏', type: DictType.collectWord, words: [
|
||||
{
|
||||
"id": "pharmacy",
|
||||
"word": "pharmacy",
|
||||
"trans": [
|
||||
{
|
||||
"cn": "n.药房,配药学,药学,制药业,一批备用药品"
|
||||
}
|
||||
],
|
||||
"phonetic0": "ˈfɑ:məsi",
|
||||
"phonetic1": "ˈfɑ:rməsi"
|
||||
},
|
||||
{
|
||||
"id": "foregone",
|
||||
"word": "foregone",
|
||||
"trans": [
|
||||
{
|
||||
"cn": "过去的;先前的;预知的;预先决定的"
|
||||
},
|
||||
{
|
||||
"cn": "发生在…之前(forego的过去分词)"
|
||||
}
|
||||
],
|
||||
"phonetic0": "fɔː'gɒn",
|
||||
"phonetic1": "'fɔrɡɔn"
|
||||
},
|
||||
|
||||
{
|
||||
"id": "calculate",
|
||||
"word": "calculate",
|
||||
"trans": [
|
||||
{
|
||||
"cn": "vt.& vi.计算,估计,打算,计划,旨在"
|
||||
},
|
||||
{
|
||||
"cn": "vt.预测,推测"
|
||||
}
|
||||
],
|
||||
"phonetic0": "ˈkælkjuleɪt",
|
||||
"phonetic1": "ˈkælkjəˌlet"
|
||||
},
|
||||
{
|
||||
"id": "compete",
|
||||
"word": "compete",
|
||||
"trans": [
|
||||
{
|
||||
"cn": "vi.竞赛,竞争,比得上,参加比赛(或竞赛)"
|
||||
}
|
||||
],
|
||||
"phonetic0": "kəmˈpi:t",
|
||||
"phonetic1": "kəmˈpit"
|
||||
},
|
||||
{
|
||||
"id": "furnish",
|
||||
"word": "furnish",
|
||||
"trans": [
|
||||
{
|
||||
"cn": "vt.陈设,布置,提供,供应,装修(房屋)"
|
||||
}
|
||||
],
|
||||
"phonetic0": "ˈfɜ:nɪʃ",
|
||||
"phonetic1": "ˈfɜ:rnɪʃ"
|
||||
},
|
||||
], statistics: []
|
||||
}),
|
||||
getDefaultDict({
|
||||
index: 2, name: '错词', type: DictType.wrong, words: [], statistics: []
|
||||
}),
|
||||
getDefaultDict({
|
||||
index: 3, name: '已掌握', type: DictType.known, words: [], statistics: []
|
||||
}),
|
||||
getDefaultDict({
|
||||
id: 'nce-new-2',
|
||||
name: '新概念英语(新版)-2',
|
||||
description: '新概念英语新版第二册',
|
||||
category: '青少年英语',
|
||||
tags: ['新概念英语'],
|
||||
url: 'nce-new-2.json',
|
||||
length: 862,
|
||||
translateLanguage: 'common',
|
||||
language: 'en',
|
||||
type: DictType.word
|
||||
}),
|
||||
],
|
||||
studyIndex: 3,
|
||||
},
|
||||
article: {
|
||||
bookList: [
|
||||
getDefaultDict({name: '收藏'})
|
||||
@@ -253,22 +158,22 @@ export const useBaseStore = defineStore('base', {
|
||||
},
|
||||
getters: {
|
||||
collect(): Dict {
|
||||
return this.myDictList[0]
|
||||
return this.word.bookList[0]
|
||||
},
|
||||
collectWord(): Dict {
|
||||
return this.commonDictList[1]
|
||||
return this.word.bookList[1]
|
||||
},
|
||||
collectArticle(): Dict {
|
||||
return this.commonDictList[2]
|
||||
return this.word.bookList[2]
|
||||
},
|
||||
simple(): Dict {
|
||||
return this.commonDictList[3]
|
||||
return this.word.bookList[2]
|
||||
},
|
||||
wrong(): Dict {
|
||||
return this.commonDictList[4]
|
||||
return this.word.bookList[1]
|
||||
},
|
||||
master(): Dict {
|
||||
return this.commonDictList[5]
|
||||
known(): Dict {
|
||||
return this.word.bookList[2]
|
||||
},
|
||||
skipWordNames() {
|
||||
return this.simple.words.map(v => v.word.toLowerCase())
|
||||
@@ -289,10 +194,10 @@ export const useBaseStore = defineStore('base', {
|
||||
return this.myDictList[this.current.index] ?? {}
|
||||
},
|
||||
currentStudyWordDict(): Dict {
|
||||
if (this.sword.dictIndex >= 0) {
|
||||
return this.wordDictList[this.currentStudy.word.dictIndex] ?? getDefaultDict()
|
||||
if (this.word.bookList.studyIndex >= 0) {
|
||||
return this.word.bookList[this.word.bookList.studyIndex] ?? getDefaultDict()
|
||||
}
|
||||
return this.commonDictList[Math.abs(this.currentStudy.word.dictIndex) - 1] ?? getDefaultDict()
|
||||
return getDefaultDict()
|
||||
},
|
||||
sdict(): Dict {
|
||||
return this.currentStudyWordDict
|
||||
@@ -310,16 +215,20 @@ export const useBaseStore = defineStore('base', {
|
||||
currentArticleCollectDict(): Dict {
|
||||
return this.article.bookList[0]
|
||||
},
|
||||
currentArticleDict(): Dict {
|
||||
return this.article.bookList[this.article.studyIndex] ?? {}
|
||||
},
|
||||
chapter(state: BaseState): Word[] {
|
||||
return this.currentDict.chapterWords[this.currentDict.chapterIndex] ?? []
|
||||
},
|
||||
|
||||
currentBook(): Dict {
|
||||
return this.article.bookList[this.article.studyIndex] ?? {}
|
||||
},
|
||||
currentBookProgress(): number {
|
||||
if (this.currentBook.name) return Number(Number(this.currentBook.lastLearnIndex / this.currentBook.length).toFixed(2))
|
||||
return 0
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setState(obj: any) {
|
||||
return
|
||||
//这样不会丢失watch的值的引用
|
||||
merge(this, obj)
|
||||
},
|
||||
@@ -338,12 +247,16 @@ export const useBaseStore = defineStore('base', {
|
||||
console.error('读取本地dict数据失败', e)
|
||||
}
|
||||
|
||||
if (this.currentStudy.word.dictIndex >= 0) {
|
||||
if (this.word.studyIndex >= 0) {
|
||||
// await _checkDictWords(this.currentStudyWordDict)
|
||||
// console.log('this.wordDictList', this.wordDictList[0].words[0])
|
||||
let current = this.word.bookList[this.word.studyIndex]
|
||||
let dictResourceUrl = `./dicts/${current.language}/${current.type}/${current.translateLanguage}/${current.url}`;
|
||||
current.words = await getDictFile(dictResourceUrl)
|
||||
|
||||
console.log('this.current', current)
|
||||
}
|
||||
if (this.currentStudy.article.dictIndex >= 0) {
|
||||
let current = this.articleDictList[this.currentStudy.article.dictIndex]
|
||||
if (this.article.studyIndex >= 0) {
|
||||
let current = this.article.bookList[this.article.studyIndex]
|
||||
let dictResourceUrl = `./dicts/${current.language}/${current.type}/${current.translateLanguage}/${current.url}`;
|
||||
if (!current.articles.length) {
|
||||
let s = await getDictFile(dictResourceUrl)
|
||||
@@ -352,7 +265,7 @@ export const useBaseStore = defineStore('base', {
|
||||
return v
|
||||
}))
|
||||
}
|
||||
console.log('this.currentArticleDict', this.currentArticleDict.articles[0])
|
||||
// console.log('this.currentBook', this.currentBook.articles[0])
|
||||
}
|
||||
emitter.emit(EventKey.changeDict)
|
||||
resolve(true)
|
||||
@@ -371,7 +284,7 @@ export const useBaseStore = defineStore('base', {
|
||||
this.wordDictList.push(getDefaultDict(dict))
|
||||
this.currentStudy.word.dictIndex = this.wordDictList.length - 1
|
||||
}
|
||||
await _checkDictWords(this.currentStudyWordDict)
|
||||
// await _checkDictWords(this.currentStudyWordDict)
|
||||
|
||||
console.log(' store.currentStudyWordDict', this.currentStudyWordDict)
|
||||
emitter.emit(EventKey.changeDict)
|
||||
@@ -444,6 +357,5 @@ export const useBaseStore = defineStore('base', {
|
||||
this.currentStudy.word.dictIndex = rIndex
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
})
|
||||
|
||||
38
src/types.ts
38
src/types.ts
@@ -23,7 +23,6 @@ export type Word = {
|
||||
memory?: string,
|
||||
}
|
||||
|
||||
|
||||
export function getDefaultWord(val?: any): Word {
|
||||
return {
|
||||
id: '',
|
||||
@@ -68,9 +67,8 @@ export enum DictType {
|
||||
collect = 'collect',
|
||||
simple = 'simple',
|
||||
wrong = 'wrong',
|
||||
master = 'master',
|
||||
known = 'known',
|
||||
collectWord = 'collect-word',
|
||||
collectArticle = 'collect-article',
|
||||
word = 'word',
|
||||
article = 'article',
|
||||
}
|
||||
@@ -98,12 +96,6 @@ export interface Sentence {
|
||||
audioPosition: number[]
|
||||
}
|
||||
|
||||
export enum TranslateType {
|
||||
custom = 'custom',
|
||||
network = 'network',
|
||||
none = 'none'
|
||||
}
|
||||
|
||||
export interface Article {
|
||||
id: string,
|
||||
title: string,
|
||||
@@ -123,19 +115,21 @@ export interface Article {
|
||||
}[]
|
||||
}
|
||||
|
||||
export const DefaultArticle: Article = {
|
||||
// id: nanoid(6),
|
||||
id: '',
|
||||
title: '',
|
||||
titleTranslate: '',
|
||||
text: '',
|
||||
textTranslate: '',
|
||||
newWords: [],
|
||||
textAllWords: [],
|
||||
sections: [],
|
||||
audioSrc: '',
|
||||
lrcPosition: [],
|
||||
questions: [],
|
||||
export function getDefaultArticle(val: Partial<Article> = {}): Article {
|
||||
return {
|
||||
id: '',
|
||||
title: '',
|
||||
titleTranslate: '',
|
||||
text: '',
|
||||
textTranslate: '',
|
||||
newWords: [],
|
||||
textAllWords: [],
|
||||
sections: [],
|
||||
audioSrc: '',
|
||||
lrcPosition: [],
|
||||
questions: [],
|
||||
...val
|
||||
}
|
||||
}
|
||||
|
||||
export interface Statistics {
|
||||
|
||||
@@ -6,7 +6,7 @@ export const SoundFileOptions = [
|
||||
{value: '笔记本键盘', label: '笔记本键盘'},
|
||||
]
|
||||
|
||||
export const APP_NAME = 'Typing Word'
|
||||
export const APP_NAME = 'Type Words'
|
||||
|
||||
export const SAVE_DICT_KEY = {
|
||||
key: 'typing-word-dict',
|
||||
@@ -20,4 +20,4 @@ export const SAVE_SETTING_KEY = {
|
||||
export const EXPORT_DATA_KEY = {
|
||||
key: 'typing-word-export',
|
||||
version: 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,9 +120,6 @@ export function shakeCommonDict(n: BaseState): BaseState {
|
||||
data.wordDictList.map((v: Dict) => {
|
||||
if (!v.isCustom) v.words = []
|
||||
})
|
||||
data.articleDictList.map((v: Dict) => {
|
||||
if (!v.isCustom) v.articles = []
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
@@ -133,6 +130,7 @@ export function isMobile(): boolean {
|
||||
export function getDictFile(url: string) {
|
||||
return new Promise<any[]>(async resolve => {
|
||||
let r = await fetch(url).catch(r => {
|
||||
console.log('getDictFile_error',r)
|
||||
})
|
||||
let v = await r.json()
|
||||
resolve(v)
|
||||
@@ -177,10 +175,9 @@ export function _fetch(url: string) {
|
||||
export async function _checkDictWords(dict: Dict) {
|
||||
console.log('_checkDictWords', dict)
|
||||
if ([DictType.collect,
|
||||
DictType.simple,
|
||||
DictType.known,
|
||||
DictType.wrong].includes(dict.dictType)) {
|
||||
} else {
|
||||
|
||||
//TODO 需要和其他需要下载的地方统一
|
||||
//如果不是自定义词典,并且有url地址才去下载
|
||||
if (!dict.isCustom && dict.fileName) {
|
||||
@@ -201,7 +198,7 @@ export async function _checkDictWords(dict: Dict) {
|
||||
if (res && res.request.responseURL !== url) {
|
||||
r = res.data
|
||||
} else {
|
||||
let dictLocalUrl = `./dicts/${dict.langTypeStr}/${dict.dictType}/${dict.tranTypeStr}/${dict.fileName}-v${dict.version}.json`;
|
||||
let dictLocalUrl = `./dicts/${dict.language}/${dict.type}/${dict.translateLanguage}/${dict.url}`;
|
||||
let r3 = await fetch(dictLocalUrl)
|
||||
try {
|
||||
r = await r3.json()
|
||||
@@ -286,4 +283,4 @@ export function _parseLRC(lrc: string): { start: number, end: number, text: stri
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user