feat:improve the import and export functions
This commit is contained in:
3
components.d.ts
vendored
3
components.d.ts
vendored
@@ -23,7 +23,6 @@ declare module 'vue' {
|
||||
IconFluentAdd20Filled: typeof import('~icons/fluent/add20-filled')['default']
|
||||
IconFluentAdd20Regular: typeof import('~icons/fluent/add20-regular')['default']
|
||||
IconFluentAddSquare20Regular: typeof import('~icons/fluent/add-square20-regular')['default']
|
||||
IconFluentAppsList24Regular: typeof import('~icons/fluent/apps-list24-regular')['default']
|
||||
IconFluentArrowBounce20Regular: typeof import('~icons/fluent/arrow-bounce20-regular')['default']
|
||||
IconFluentArrowCircleRight16Regular: typeof import('~icons/fluent/arrow-circle-right16-regular')['default']
|
||||
IconFluentArrowLeft16Regular: typeof import('~icons/fluent/arrow-left16-regular')['default']
|
||||
@@ -45,7 +44,6 @@ declare module 'vue' {
|
||||
IconFluentErrorCircle20Filled: typeof import('~icons/fluent/error-circle20-filled')['default']
|
||||
IconFluentEye16Regular: typeof import('~icons/fluent/eye16-regular')['default']
|
||||
IconFluentEyeOff16Regular: typeof import('~icons/fluent/eye-off16-regular')['default']
|
||||
IconFluentHeadphones20Regular: typeof import('~icons/fluent/headphones20-regular')['default']
|
||||
IconFluentHome20Regular: typeof import('~icons/fluent/home20-regular')['default']
|
||||
IconFluentKeyboardLayoutFloat20Regular: typeof import('~icons/fluent/keyboard-layout-float20-regular')['default']
|
||||
IconFluentMailEdit20Regular: typeof import('~icons/fluent/mail-edit20-regular')['default']
|
||||
@@ -73,7 +71,6 @@ declare module 'vue' {
|
||||
IconFluentWeatherMoon16Regular: typeof import('~icons/fluent/weather-moon16-regular')['default']
|
||||
IconFluentWeatherSunny16Regular: typeof import('~icons/fluent/weather-sunny16-regular')['default']
|
||||
IconIconParkOutlineAddMusic: typeof import('~icons/icon-park-outline/add-music')['default']
|
||||
IconIconParkSolidAddMusic: typeof import('~icons/icon-park-solid/add-music')['default']
|
||||
IconMaterialSymbolsMail: typeof import('~icons/material-symbols/mail')['default']
|
||||
IconRiTwitterFill: typeof import('~icons/ri/twitter-fill')['default']
|
||||
IconSimpleIconsGithub: typeof import('~icons/simple-icons/github')['default']
|
||||
|
||||
12721
package-lock.json
generated
12721
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -24,7 +24,6 @@
|
||||
"dayjs": "^1.11.13",
|
||||
"file-saver": "^2.0.5",
|
||||
"idb-keyval": "^6.2.2",
|
||||
"libarchive-wasm": "^1.2.0",
|
||||
"md5": "^2.2.1",
|
||||
"mitt": "^3.0.1",
|
||||
"nanoid": "^5.1.5",
|
||||
|
||||
43
pnpm-lock.yaml
generated
43
pnpm-lock.yaml
generated
@@ -87,6 +87,9 @@ importers:
|
||||
'@types/file-saver':
|
||||
specifier: ^2.0.7
|
||||
version: 2.0.7
|
||||
'@types/jszip':
|
||||
specifier: ^3.4.1
|
||||
version: 3.4.1
|
||||
'@types/lodash-es':
|
||||
specifier: ^4.17.12
|
||||
version: 4.17.12
|
||||
@@ -827,6 +830,10 @@ packages:
|
||||
'@types/file-saver@2.0.7':
|
||||
resolution: {integrity: sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==}
|
||||
|
||||
'@types/jszip@3.4.1':
|
||||
resolution: {integrity: sha512-TezXjmf3lj+zQ651r6hPqvSScqBLvyPI9FxdXBqpEwBijNGQ2NXpaFW/7joGzveYkKQUil7iiDHLo6LV71Pc0A==}
|
||||
deprecated: This is a stub types definition. jszip provides its own type definitions, so you do not need this installed.
|
||||
|
||||
'@types/lodash-es@4.17.12':
|
||||
resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==}
|
||||
|
||||
@@ -2265,6 +2272,9 @@ packages:
|
||||
ieee754@1.2.1:
|
||||
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
||||
|
||||
immediate@3.0.6:
|
||||
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
|
||||
|
||||
immutable@5.1.3:
|
||||
resolution: {integrity: sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==}
|
||||
|
||||
@@ -2496,6 +2506,9 @@ packages:
|
||||
jstoxml@2.2.9:
|
||||
resolution: {integrity: sha512-OYWlK0j+roh+eyaMROlNbS5cd5R25Y+IUpdl7cNdB8HNrkgwQzIS7L9MegxOiWNBj9dQhA/yAxiMwCC5mwNoBw==}
|
||||
|
||||
jszip@3.10.1:
|
||||
resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==}
|
||||
|
||||
just-debounce@1.1.0:
|
||||
resolution: {integrity: sha512-qpcRocdkUmf+UTNBYx5w6dexX5J31AKK1OmPwH630a83DdVVUIngk55RSAiIGpQyoH0dlr872VHfPjnQnK1qDQ==}
|
||||
|
||||
@@ -2540,6 +2553,9 @@ packages:
|
||||
libarchive-wasm@1.2.0:
|
||||
resolution: {integrity: sha512-aunFn8oL9VwGRj+brRvdOv8BRUD4Ea1WxJW45IdiuXE2Vp/m/X+M1UxSU+yPzXfc1mPPC8AARaflg/CtF11u8g==}
|
||||
|
||||
lie@3.3.0:
|
||||
resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==}
|
||||
|
||||
liftoff@3.1.0:
|
||||
resolution: {integrity: sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==}
|
||||
engines: {node: '>= 0.8'}
|
||||
@@ -2841,6 +2857,9 @@ packages:
|
||||
package-manager-detector@1.3.0:
|
||||
resolution: {integrity: sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==}
|
||||
|
||||
pako@1.0.11:
|
||||
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
|
||||
|
||||
parent-module@1.0.1:
|
||||
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -3162,6 +3181,9 @@ packages:
|
||||
resolution: {integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
setimmediate@1.0.5:
|
||||
resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
|
||||
|
||||
side-channel-list@1.0.0:
|
||||
resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -4404,6 +4426,10 @@ snapshots:
|
||||
|
||||
'@types/file-saver@2.0.7': {}
|
||||
|
||||
'@types/jszip@3.4.1':
|
||||
dependencies:
|
||||
jszip: 3.10.1
|
||||
|
||||
'@types/lodash-es@4.17.12':
|
||||
dependencies:
|
||||
'@types/lodash': 4.17.20
|
||||
@@ -6200,6 +6226,8 @@ snapshots:
|
||||
|
||||
ieee754@1.2.1: {}
|
||||
|
||||
immediate@3.0.6: {}
|
||||
|
||||
immutable@5.1.3: {}
|
||||
|
||||
import-fresh@3.3.1:
|
||||
@@ -6403,6 +6431,13 @@ snapshots:
|
||||
|
||||
jstoxml@2.2.9: {}
|
||||
|
||||
jszip@3.10.1:
|
||||
dependencies:
|
||||
lie: 3.3.0
|
||||
pako: 1.0.11
|
||||
readable-stream: 2.3.8
|
||||
setimmediate: 1.0.5
|
||||
|
||||
just-debounce@1.1.0: {}
|
||||
|
||||
kind-of@3.2.2:
|
||||
@@ -6440,6 +6475,10 @@ snapshots:
|
||||
|
||||
libarchive-wasm@1.2.0: {}
|
||||
|
||||
lie@3.3.0:
|
||||
dependencies:
|
||||
immediate: 3.0.6
|
||||
|
||||
liftoff@3.1.0:
|
||||
dependencies:
|
||||
extend: 3.0.2
|
||||
@@ -6791,6 +6830,8 @@ snapshots:
|
||||
|
||||
package-manager-detector@1.3.0: {}
|
||||
|
||||
pako@1.0.11: {}
|
||||
|
||||
parent-module@1.0.1:
|
||||
dependencies:
|
||||
callsites: 3.1.0
|
||||
@@ -7128,6 +7169,8 @@ snapshots:
|
||||
is-plain-object: 2.0.4
|
||||
split-string: 3.1.0
|
||||
|
||||
setimmediate@1.0.5: {}
|
||||
|
||||
side-channel-list@1.0.0:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
|
||||
@@ -135,7 +135,7 @@ const {
|
||||
<div class="right flex-[4] shrink-0 pl-4 overflow-auto">
|
||||
<div v-if="selectArticle.id">
|
||||
<div class="en-article-family title text-xl">
|
||||
<div class="text-center text-2xl my-2" v-if="selectArticle.audioSrc">
|
||||
<div class="text-center text-2xl my-2">
|
||||
<ArticleAudio :article="selectArticle"></ArticleAudio>
|
||||
</div>
|
||||
<div class="text-center text-2xl">{{ selectArticle.title }}</div>
|
||||
|
||||
@@ -171,7 +171,7 @@ async function handleAudioChange(e: any) {
|
||||
let uploadFile = e.target?.files?.[0]
|
||||
if (!uploadFile) return
|
||||
let data = {
|
||||
id: nanoid(6),
|
||||
id: nanoid(),
|
||||
file: uploadFile,
|
||||
}
|
||||
//把文件存到indexDB
|
||||
|
||||
@@ -6,7 +6,14 @@ import {getShortcutKey, useEventListener} from "@/hooks/event.ts";
|
||||
import {checkAndUpgradeSaveDict, checkAndUpgradeSaveSetting, cloneDeep, shakeCommonDict} from "@/utils";
|
||||
import {DefaultShortcutKeyMap, ShortcutKey} from "@/types/types.ts";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import {APP_NAME, EXPORT_DATA_KEY, SAVE_DICT_KEY, SAVE_SETTING_KEY, SoundFileOptions} from "@/utils/const.ts";
|
||||
import {
|
||||
APP_NAME,
|
||||
EXPORT_DATA_KEY,
|
||||
LOCAL_FILE_KEY,
|
||||
SAVE_DICT_KEY,
|
||||
SAVE_SETTING_KEY,
|
||||
SoundFileOptions
|
||||
} from "@/utils/const.ts";
|
||||
import VolumeIcon from "@/components/icon/VolumeIcon.vue";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import {saveAs} from "file-saver";
|
||||
@@ -23,6 +30,8 @@ import InputNumber from "@/pages/pc/components/base/InputNumber.vue";
|
||||
import PopConfirm from "@/pages/pc/components/PopConfirm.vue";
|
||||
import Textarea from "@/pages/pc/components/base/Textarea.vue";
|
||||
import SettingItem from "@/pages/pc/setting/SettingItem.vue";
|
||||
// import {ArchiveReader, libarchiveWasm} from "libarchive-wasm";
|
||||
import {get, set} from "idb-keyval";
|
||||
|
||||
const emit = defineEmits<{
|
||||
toggleDisabledDialogEscKey: [val: boolean]
|
||||
@@ -66,7 +75,7 @@ watch(() => editShortcutKey, (newVal) => {
|
||||
|
||||
useEventListener('keydown', (e: KeyboardEvent) => {
|
||||
if (!disabledDefaultKeyboardEvent) return
|
||||
|
||||
|
||||
// 确保阻止浏览器默认行为
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
@@ -85,11 +94,11 @@ useEventListener('keydown', (e: KeyboardEvent) => {
|
||||
settingStore.shortcutKeyMap[editShortcutKey] = ''
|
||||
} else {
|
||||
// 忽略单独的修饰键
|
||||
if (shortcutKey === 'Ctrl+' || shortcutKey === 'Alt+' || shortcutKey === 'Shift+' ||
|
||||
if (shortcutKey === 'Ctrl+' || shortcutKey === 'Alt+' || shortcutKey === 'Shift+' ||
|
||||
e.key === 'Control' || e.key === 'Alt' || e.key === 'Shift') {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (const [k, v] of Object.entries(settingStore.shortcutKeyMap)) {
|
||||
if (v === shortcutKey && k !== editShortcutKey) {
|
||||
settingStore.shortcutKeyMap[editShortcutKey] = DefaultShortcutKeyMap[editShortcutKey]
|
||||
@@ -101,7 +110,6 @@ useEventListener('keydown', (e: KeyboardEvent) => {
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
function handleInputBlur() {
|
||||
// 输入框失焦时结束编辑状态
|
||||
editShortcutKey = ''
|
||||
@@ -144,7 +152,7 @@ function getShortcutKeyName(key: string): string {
|
||||
'ToggleConciseMode': '切换简洁模式',
|
||||
'TogglePanel': '切换面板'
|
||||
}
|
||||
|
||||
|
||||
return shortcutKeyNameMap[key] || key
|
||||
}
|
||||
|
||||
@@ -154,7 +162,24 @@ function resetShortcutKeyMap() {
|
||||
Toast.success('恢复成功')
|
||||
}
|
||||
|
||||
function exportData(notice = '导出成功!') {
|
||||
async function loadJSZip() {
|
||||
if (window.JSZip) return window.JSZip;
|
||||
return new Promise((resolve, reject) => {
|
||||
const script = document.createElement("script");
|
||||
// script.src = "https://cdn.jsdelivr.net/npm/jszip@3.10.1/dist/jszip.min.js";
|
||||
script.src = "https://2study.top/libs/jszip.min.js";
|
||||
script.onload = () => resolve(window.JSZip);
|
||||
script.onerror = reject;
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
||||
let exportLoading = $ref(false)
|
||||
let importLoading = $ref(false)
|
||||
|
||||
async function exportData(notice = '导出成功!') {
|
||||
exportLoading = true
|
||||
const JSZip = await loadJSZip();
|
||||
let data = {
|
||||
version: EXPORT_DATA_KEY.version,
|
||||
val: {
|
||||
@@ -168,42 +193,94 @@ function exportData(notice = '导出成功!') {
|
||||
}
|
||||
}
|
||||
}
|
||||
let blob = new Blob([JSON.stringify(data)], {type: "text/plain;charset=utf-8"});
|
||||
saveAs(blob, `${APP_NAME}-User-Data-${dayjs().format('YYYY-MM-DD HH-mm-ss')}.json`);
|
||||
const zip = new JSZip();
|
||||
zip.file("data.json", JSON.stringify(data));
|
||||
|
||||
const mp3 = zip.folder("mp3");
|
||||
const allRecords = await get(LOCAL_FILE_KEY);
|
||||
for (const rec of allRecords) {
|
||||
mp3.file(rec.id + ".mp3", rec.file);
|
||||
}
|
||||
exportLoading = false
|
||||
zip.generateAsync({type: "blob"}).then(function (content) {
|
||||
saveAs(content, `${APP_NAME}-User-Data-${dayjs().format('YYYY-MM-DD HH-mm-ss')}.zip`);
|
||||
});
|
||||
Toast.success(notice)
|
||||
}
|
||||
|
||||
function importData(e) {
|
||||
let file = e.target.files[0]
|
||||
if (!file) return
|
||||
// no()
|
||||
let reader = new FileReader();
|
||||
reader.onload = function (v) {
|
||||
let str: any = v.target.result;
|
||||
if (str) {
|
||||
let obj = {
|
||||
version: -1,
|
||||
val: {
|
||||
setting: {},
|
||||
dict: {},
|
||||
}
|
||||
}
|
||||
try {
|
||||
obj = JSON.parse(str)
|
||||
let data = obj.val
|
||||
let settingState = checkAndUpgradeSaveSetting(data.setting)
|
||||
settingState.load = true
|
||||
settingStore.setState(settingState)
|
||||
let baseState = checkAndUpgradeSaveDict(data.dict)
|
||||
baseState.load = true
|
||||
store.setState(baseState)
|
||||
Toast.success('导入成功!')
|
||||
} catch (err) {
|
||||
return Toast.error('导入失败!')
|
||||
}
|
||||
function importJson(str: string, notice: boolean = true) {
|
||||
let obj = {
|
||||
version: -1,
|
||||
val: {
|
||||
setting: {},
|
||||
dict: {},
|
||||
}
|
||||
}
|
||||
reader.readAsText(file);
|
||||
try {
|
||||
obj = JSON.parse(str)
|
||||
let data = obj.val
|
||||
let settingState = checkAndUpgradeSaveSetting(data.setting)
|
||||
settingState.load = true
|
||||
settingStore.setState(settingState)
|
||||
let baseState = checkAndUpgradeSaveDict(data.dict)
|
||||
baseState.load = true
|
||||
store.setState(baseState)
|
||||
notice && Toast.success('导入成功!')
|
||||
} catch (err) {
|
||||
return Toast.error('导入失败!')
|
||||
}
|
||||
}
|
||||
|
||||
async function importData(e) {
|
||||
let file = e.target.files[0]
|
||||
if (!file) return
|
||||
if (file.name.endsWith(".json")) {
|
||||
let reader = new FileReader();
|
||||
reader.onload = function (v) {
|
||||
let str: any = v.target.result;
|
||||
if (str) {
|
||||
importJson(str)
|
||||
}
|
||||
}
|
||||
reader.readAsText(file);
|
||||
} else if (file.name.endsWith(".zip")) {
|
||||
try {
|
||||
importLoading = true
|
||||
const JSZip = await loadJSZip();
|
||||
const zip = await JSZip.loadAsync(file);
|
||||
|
||||
const dataFile = zip.file("data.json");
|
||||
if (!dataFile) {
|
||||
return Toast.error("缺少 data.json,导入失败");
|
||||
}
|
||||
|
||||
const mp3Folder = zip.folder("mp3");
|
||||
if (mp3Folder) {
|
||||
const records: { id: string; file: Blob }[] = [];
|
||||
for (const filename in zip.files) {
|
||||
if (filename.startsWith("mp3/") && filename.endsWith(".mp3")) {
|
||||
const entry = zip.file(filename);
|
||||
if (!entry) continue;
|
||||
const blob = await entry.async("blob");
|
||||
const id = filename.replace(/^mp3\//, "").replace(/\.mp3$/, "");
|
||||
records.push({ id, file: blob });
|
||||
}
|
||||
}
|
||||
await set(LOCAL_FILE_KEY, records);
|
||||
}
|
||||
|
||||
const str = await dataFile.async("string");
|
||||
importJson(str, false)
|
||||
|
||||
Toast.success("导入成功!");
|
||||
} catch (e) {
|
||||
Toast.error("导入失败!");
|
||||
} finally {
|
||||
importLoading = false
|
||||
}
|
||||
} else {
|
||||
Toast.error("不支持的文件类型");
|
||||
}
|
||||
}
|
||||
|
||||
function importOldData() {
|
||||
@@ -299,9 +376,9 @@ function importOldData() {
|
||||
v-model="simpleWords" :autosize="{minRows: 6, maxRows: 10}"/>
|
||||
</SettingItem>
|
||||
|
||||
<!-- 音效-->
|
||||
<!-- 音效-->
|
||||
<!-- 音效-->
|
||||
<!-- 音效-->
|
||||
<!-- 音效-->
|
||||
<!-- 音效-->
|
||||
<div class="line"></div>
|
||||
<SettingItem main-title="音效"/>
|
||||
<SettingItem title="单词/句子发音口音">
|
||||
@@ -354,9 +431,9 @@ function importOldData() {
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 单词练习设置-->
|
||||
<!-- 单词练习设置-->
|
||||
<!-- 单词练习设置-->
|
||||
<!-- 单词练习设置-->
|
||||
<!-- 单词练习设置-->
|
||||
<!-- 单词练习设置-->
|
||||
<div v-if="tabIndex === 1">
|
||||
<SettingItem title="练习模式">
|
||||
<RadioGroup v-model="settingStore.wordPracticeMode" class="flex-col gap-0!">
|
||||
@@ -401,9 +478,9 @@ function importOldData() {
|
||||
</SettingItem>
|
||||
|
||||
|
||||
<!-- 发音-->
|
||||
<!-- 发音-->
|
||||
<!-- 发音-->
|
||||
<!-- 发音-->
|
||||
<!-- 发音-->
|
||||
<!-- 发音-->
|
||||
<div class="line"></div>
|
||||
<SettingItem mainTitle="音效"/>
|
||||
<SettingItem title="自动发音">
|
||||
@@ -419,9 +496,9 @@ function importOldData() {
|
||||
</SettingItem>
|
||||
|
||||
|
||||
<!-- 自动切换-->
|
||||
<!-- 自动切换-->
|
||||
<!-- 自动切换-->
|
||||
<!-- 自动切换-->
|
||||
<!-- 自动切换-->
|
||||
<!-- 自动切换-->
|
||||
<div class="line"></div>
|
||||
<SettingItem mainTitle="自动切换"/>
|
||||
<SettingItem title="自动切换下一个单词"
|
||||
@@ -444,9 +521,9 @@ function importOldData() {
|
||||
</SettingItem>
|
||||
|
||||
|
||||
<!-- 字体设置-->
|
||||
<!-- 字体设置-->
|
||||
<!-- 字体设置-->
|
||||
<!-- 字体设置-->
|
||||
<!-- 字体设置-->
|
||||
<!-- 字体设置-->
|
||||
<div class="line"></div>
|
||||
<SettingItem mainTitle="字体设置"/>
|
||||
<SettingItem title="外语字体">
|
||||
@@ -466,10 +543,9 @@ function importOldData() {
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- 文章练习设置-->
|
||||
<!-- 文章练习设置-->
|
||||
<!-- 文章练习设置-->
|
||||
<!-- 文章练习设置-->
|
||||
<!-- 文章练习设置-->
|
||||
<!-- 文章练习设置-->
|
||||
<div v-if="tabIndex === 2">
|
||||
<!-- 发音-->
|
||||
<!-- 发音-->
|
||||
@@ -492,7 +568,6 @@ function importOldData() {
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="body" v-if="tabIndex === 3">
|
||||
<div class="row">
|
||||
<label class="main-title">功能</label>
|
||||
@@ -503,7 +578,8 @@ function importOldData() {
|
||||
<label class="item-title">{{ getShortcutKeyName(item[0]) }}</label>
|
||||
<div class="wrapper" @click="editShortcutKey = item[0]">
|
||||
<div class="set-key" v-if="editShortcutKey === item[0]">
|
||||
<input ref="shortcutInput" :value="item[1]?item[1]:'未设置快捷键'" readonly type="text" @blur="handleInputBlur">
|
||||
<input ref="shortcutInput" :value="item[1]?item[1]:'未设置快捷键'" readonly type="text"
|
||||
@blur="handleInputBlur">
|
||||
<span @click.stop="editShortcutKey = ''">按键盘进行设置,<span
|
||||
class="text-red!">设置完成点击这里</span></span>
|
||||
</div>
|
||||
@@ -527,7 +603,7 @@ function importOldData() {
|
||||
<b class="text-red">仅保存在本地</b>。如果您需要在不同的设备、浏览器或者其他非官方部署上使用 {{ APP_NAME }},
|
||||
您需要手动进行数据同步和保存。
|
||||
</div>
|
||||
<BaseButton class="mt-3" @click="exportData()">导出数据</BaseButton>
|
||||
<BaseButton :loading="exportLoading" class="mt-3" @click="exportData()">导出数据</BaseButton>
|
||||
|
||||
<div class="line my-3"></div>
|
||||
|
||||
@@ -535,9 +611,9 @@ function importOldData() {
|
||||
</div>
|
||||
<div class="flex gap-space mt-3">
|
||||
<div class="import hvr-grow">
|
||||
<BaseButton>导入数据</BaseButton>
|
||||
<BaseButton :loading="importLoading">导入数据</BaseButton>
|
||||
<input type="file"
|
||||
accept="application/json"
|
||||
accept="application/json,.zip,application/zip"
|
||||
@change="importData">
|
||||
</div>
|
||||
<PopConfirm
|
||||
@@ -656,7 +732,7 @@ function importOldData() {
|
||||
background: var(--color-second);
|
||||
color: var(--color-font-1);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
2
src/types/global.d.ts
vendored
2
src/types/global.d.ts
vendored
@@ -9,6 +9,7 @@ declare global {
|
||||
umami: {
|
||||
track(name: string, data?: any): void
|
||||
},
|
||||
JSZip: any,
|
||||
__CURRENT_WORD_INFO__?: {
|
||||
word: string,
|
||||
input: string,
|
||||
@@ -18,6 +19,7 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
console.json = function (v: any, space = 0) {
|
||||
const json = JSON.stringify(
|
||||
v,
|
||||
|
||||
@@ -18,7 +18,7 @@ export const SAVE_SETTING_KEY = {
|
||||
}
|
||||
export const EXPORT_DATA_KEY = {
|
||||
key: 'typing-word-export',
|
||||
version: 1
|
||||
version: 2
|
||||
}
|
||||
|
||||
export const LOCAL_FILE_KEY = 'typing-word-files'
|
||||
|
||||
Reference in New Issue
Block a user