add: history,wordclass & bug fixes
parent
913b4a9438
commit
357be521de
File diff suppressed because it is too large
Load Diff
|
|
@ -8,8 +8,14 @@
|
||||||
"lint": "vue-cli-service lint"
|
"lint": "vue-cli-service lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@alicloud/alimt20181012": "^1.2.0",
|
||||||
|
"axios": "^1.5.1",
|
||||||
|
"boxicons": "^2.1.4",
|
||||||
"core-js": "^3.8.3",
|
"core-js": "^3.8.3",
|
||||||
"vue": "^3.2.13"
|
"crypto-js": "^4.1.1",
|
||||||
|
"node-uuid": "^1.4.8",
|
||||||
|
"vue": "^3.2.13",
|
||||||
|
"vue-router": "^4.2.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.12.16",
|
"@babel/core": "^7.12.16",
|
||||||
|
|
@ -17,6 +23,7 @@
|
||||||
"@vue/cli-plugin-babel": "~5.0.0",
|
"@vue/cli-plugin-babel": "~5.0.0",
|
||||||
"@vue/cli-plugin-eslint": "~5.0.0",
|
"@vue/cli-plugin-eslint": "~5.0.0",
|
||||||
"@vue/cli-service": "~5.0.0",
|
"@vue/cli-service": "~5.0.0",
|
||||||
|
"element-plus": "^2.3.14",
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^7.32.0",
|
||||||
"eslint-plugin-vue": "^8.0.3"
|
"eslint-plugin-vue": "^8.0.3"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,39 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="">
|
<html lang="">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
<title>
|
||||||
|
<%= htmlWebpackPlugin.options.title %>
|
||||||
|
</title>
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<noscript>
|
<noscript>
|
||||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
|
||||||
|
Please enable it to continue.</strong>
|
||||||
</noscript>
|
</noscript>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<!-- built files will be auto injected -->
|
<!-- built files will be auto injected -->
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
{"requestId":"8f5a2a3c-2a9e-4929-906d-d1f5195fdef4","errorCode":"202","l":"en2zh-CHS"}
|
||||||
113
src/App.vue
113
src/App.vue
|
|
@ -1,26 +1,115 @@
|
||||||
<template>
|
<template>
|
||||||
<img alt="Vue logo" src="./assets/logo.png">
|
<router-view></router-view>
|
||||||
<HelloWorld msg="Welcome to Your Vue.js App"/>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import HelloWorld from './components/HelloWorld.vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'App',
|
||||||
components: {
|
data() {
|
||||||
HelloWorld
|
return {
|
||||||
|
index: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
change(index) {
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
let wordsets = localStorage.getItem("wordsets");
|
||||||
|
if (wordsets) {
|
||||||
|
window.wordsets = JSON.parse(wordsets);
|
||||||
|
} else window.wordsets = {};
|
||||||
|
let res = localStorage.getItem("bgimg");
|
||||||
|
document.body.style.backgroundImage = `url("${res}")`;
|
||||||
|
const isDarkTheme = window.matchMedia("(prefers-color-scheme: dark)");
|
||||||
|
if(isDarkTheme.matches) document.getElementsByTagName("html")[0].className= "dark";
|
||||||
|
isDarkTheme.addEventListener('change', (event) => {
|
||||||
|
if (event.matches) {
|
||||||
|
document.getElementsByTagName("html")[0].className= "dark";
|
||||||
|
} else {
|
||||||
|
document.getElementsByTagName("html")[0].className= "";
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
#app {
|
html {
|
||||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
--bg-color: #ffffffae;
|
||||||
-webkit-font-smoothing: antialiased;
|
--text-color: #464646;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
}
|
||||||
text-align: center;
|
|
||||||
color: #2c3e50;
|
html.dark {
|
||||||
margin-top: 60px;
|
--bg-color: #2a2a2aae;
|
||||||
|
--text-color: #c0c0c0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes enter {
|
||||||
|
0% {
|
||||||
|
translate: 100px 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
translate: 0 0;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
height: calc(100%-20px);
|
||||||
|
width: calc(100%-20px);
|
||||||
|
padding: 20px;
|
||||||
|
animation: enter ease-out .6s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colbox {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rowbox {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: var(--el-box-shadow);
|
||||||
|
padding: 20px;
|
||||||
|
margin: 10px;
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.wordset {
|
||||||
|
border-radius: 13px;
|
||||||
|
padding: 20px;
|
||||||
|
height: 220px;
|
||||||
|
width: 170px;
|
||||||
|
box-shadow: var(--el-box-shadow);
|
||||||
|
margin: 20px;
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mid-text {
|
||||||
|
width: 65px;
|
||||||
|
transform: translate(0, 6px);
|
||||||
|
color: var(--text-color);
|
||||||
|
margin-right: 5px;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.para {
|
||||||
|
margin: 15px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,322 @@
|
||||||
|
<template>
|
||||||
|
<el-page-header style="margin:20px" @back="back_home">
|
||||||
|
<template #content>
|
||||||
|
<span class="text-large font-600 mr-3"> 编辑单词本 </span>
|
||||||
|
</template>
|
||||||
|
</el-page-header>
|
||||||
|
<el-container v-if="mode < 2" class="container card">
|
||||||
|
<el-header id="header" class="colbox">
|
||||||
|
<div class="no">共有{{ Object.keys(this.wordsets).length }}个单词集合</div>
|
||||||
|
<div>
|
||||||
|
<el-button @click="export_set" type="success"><box-icon color="white" name='export'
|
||||||
|
size="15px"></box-icon>导出</el-button>
|
||||||
|
<el-button @click="import_set" type="warning"><box-icon color="white" name='import'
|
||||||
|
size="15px"></box-icon>导入</el-button>
|
||||||
|
<el-button @click="mode = 1" type="primary"><box-icon color="white" name='plus'></box-icon>新建单词本</el-button>
|
||||||
|
</div>
|
||||||
|
</el-header>
|
||||||
|
<el-main>
|
||||||
|
<div v-if="mode === 0" class="colbox" if="sets-container">
|
||||||
|
<el-collapse accordion v-model="active_set_class" style="width: 100%;">
|
||||||
|
<el-collapse-item v-for="(set_class, class_name) in wordsets" :title="class_name" :key="class_name"
|
||||||
|
:name="class_name">
|
||||||
|
<div class="colbox">
|
||||||
|
<div v-for="(wordset, index) in set_class" :key="index" class="wordset rowbox">
|
||||||
|
<div class="no">
|
||||||
|
{{ index + 1 }}
|
||||||
|
</div>
|
||||||
|
<div class="title">
|
||||||
|
{{ wordset.name }}
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 17px;font-weight: 200;">
|
||||||
|
创建日期:{{ (new Date(wordset.created)).toLocaleString() }}
|
||||||
|
</div>
|
||||||
|
<div class="option">
|
||||||
|
<box-icon class="btn" name='edit' color="var(--text-color)" @click="edit(wordset, class_name)"></box-icon>
|
||||||
|
<box-icon class="btn" name='trash' color="var(--text-color)"
|
||||||
|
@click="del(class_name, wordset.id, index)"></box-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-collapse-item>
|
||||||
|
</el-collapse>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div v-if="mode === 1" id="new" class="rowbox">
|
||||||
|
<el-page-header @back="mode = 0">
|
||||||
|
<template #content>
|
||||||
|
<span class="text-large font-600 mr-3"> 新建单词本 </span>
|
||||||
|
</template>
|
||||||
|
</el-page-header>
|
||||||
|
<div class="colbox">
|
||||||
|
<div class="mid-text">名称:</div>
|
||||||
|
<el-input v-model="set_name"></el-input>
|
||||||
|
<div class="mid-text">分组:</div>
|
||||||
|
<el-select allow-create filterable v-model="new_set_class" class="m-2" placeholder="Select">
|
||||||
|
<el-option v-for="item in Object.keys(wordsets)" :key="item" :label="item" :value="item" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<el-button style="width: 50%;" type="primary" @click="new_wordset()">创建</el-button>
|
||||||
|
</div>
|
||||||
|
</el-main>
|
||||||
|
</el-container>
|
||||||
|
<div v-if="mode === 2" class="container" style="overflow: auto;">
|
||||||
|
<el-page-header @back="mode = 0">
|
||||||
|
<template #content>
|
||||||
|
<span class="text-large font-600 mr-3"> {{ editing.name }} ({{ editing.id }}) </span>
|
||||||
|
</template>
|
||||||
|
</el-page-header>
|
||||||
|
<div class="colbox" style="margin-top: 20px;">
|
||||||
|
<div class="rowbox">
|
||||||
|
<div class="card" style="margin-bottom: 20px;">
|
||||||
|
<div class="title">添加单词</div>
|
||||||
|
<el-input v-model="new_word"></el-input>
|
||||||
|
<div class="colbox">
|
||||||
|
<el-text class="mx-1" style="width: 60px;">翻译:</el-text>
|
||||||
|
<el-input v-model="new_trans"></el-input>
|
||||||
|
</div>
|
||||||
|
<div class="colbox">
|
||||||
|
<el-text class="mx-1" style="width: 80px;">词性:</el-text>
|
||||||
|
<el-select v-model="word_type" class="m-2" placeholder="Select">
|
||||||
|
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<el-button @click="add_word()" type="primary">添加</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="title">更改名称</div>
|
||||||
|
<div class="colbox">
|
||||||
|
<el-text class="mx-1" style="width: 60px;">新名称:</el-text>
|
||||||
|
<el-input v-model="new_name"></el-input>
|
||||||
|
</div>
|
||||||
|
<el-button @click="change_name()" type="primary">更改名称</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="width: 100%;" class="card">
|
||||||
|
<el-table :data="table" style="max-height: 550px;overflow: auto;border-radius: 10px;">
|
||||||
|
<el-table-column type="index" />
|
||||||
|
<el-table-column prop="word" label="单词" />
|
||||||
|
<el-table-column prop="type" label="词性" />
|
||||||
|
<el-table-column prop="trans" label="翻译" />
|
||||||
|
<el-table-column fixed="right" label="操作">
|
||||||
|
<template #default="scope">
|
||||||
|
<div style="display: flex;flex-direction: row;">
|
||||||
|
<box-icon color="var(--text-color)" size="14px" class="btn" name='trash' @click="del_word(scope.$index)"></box-icon>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ElNotification, ElMessage, ElMessageBox } from 'element-plus';
|
||||||
|
import uuid from 'node-uuid';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "NoteEditor",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
mode: 0,
|
||||||
|
wordsets: window.wordsets,
|
||||||
|
set_name: "",
|
||||||
|
new_set_class: "",
|
||||||
|
editing: {},
|
||||||
|
editingClass: "",
|
||||||
|
new_word: "",
|
||||||
|
new_name: "",
|
||||||
|
word_type: "adj.",
|
||||||
|
options: [{ label: "verb(v.)", value: "v." }, { label: "adverb(adv.)", value: "adv." }, { label: "noun(n.)", value: "n." }, { label: "adjective(adj.)", value: "adj." }, { label: "prepositions(prep.)", value: "prep." }, { label: "phrase(phr.)", value: "phr." },],
|
||||||
|
table: [],
|
||||||
|
new_trans: "",
|
||||||
|
active_set_class: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
back_home() {
|
||||||
|
this.$router.push("/");
|
||||||
|
},
|
||||||
|
new_wordset() {
|
||||||
|
if (!this.new_set_class) {
|
||||||
|
ElMessage({
|
||||||
|
message: "集合不能为空",
|
||||||
|
type: "error"
|
||||||
|
})
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.wordsets[this.new_set_class]) {
|
||||||
|
this.wordsets[this.new_set_class] = [];
|
||||||
|
}
|
||||||
|
let id = uuid.v1();
|
||||||
|
this.wordsets[this.new_set_class].push({
|
||||||
|
name: this.set_name,
|
||||||
|
created: (new Date()).getTime(),
|
||||||
|
id: id
|
||||||
|
});
|
||||||
|
localStorage.setItem("wordsets", JSON.stringify(this.wordsets));
|
||||||
|
localStorage.setItem(id, JSON.stringify([]));
|
||||||
|
this.mode = 0;
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
del(set_class_name, id, index) {
|
||||||
|
ElMessageBox.confirm("确定要删除吗?")
|
||||||
|
.then(() => {
|
||||||
|
localStorage.removeItem(id);
|
||||||
|
this.wordsets[set_class_name].splice(index, 1);
|
||||||
|
if (!this.wordsets[set_class_name].length) {
|
||||||
|
delete this.wordsets[set_class_name];
|
||||||
|
}
|
||||||
|
localStorage.setItem("wordsets", JSON.stringify(this.wordsets));
|
||||||
|
ElMessage(`已经删除 ${set_class_name} `);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
edit(set, set_class_name) {
|
||||||
|
this.editing = set;
|
||||||
|
this.table = JSON.parse(localStorage.getItem(set.id));
|
||||||
|
this.mode = 2;
|
||||||
|
this.editingClass = set_class_name;
|
||||||
|
},
|
||||||
|
add_word() {
|
||||||
|
this.table.push({
|
||||||
|
word: this.new_word,
|
||||||
|
type: this.word_type,
|
||||||
|
trans: this.new_trans
|
||||||
|
})
|
||||||
|
localStorage.setItem(this.editing.id, JSON.stringify(this.table));
|
||||||
|
ElMessage({
|
||||||
|
message: `已添加 ${this.new_word} (${this.word_type})`,
|
||||||
|
type: 'success',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
change_name() {
|
||||||
|
let old_name = this.editing.name;
|
||||||
|
this.editing.name = this.new_name;
|
||||||
|
localStorage.setItem("wordsets", JSON.stringify(this.wordsets));
|
||||||
|
ElMessage({
|
||||||
|
message: `已更改 ${old_name} 为 ${this.new_name}`,
|
||||||
|
type: 'success',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
del_word(index) {
|
||||||
|
this.table.splice(index, 1);
|
||||||
|
localStorage.setItem(this.editing.id, JSON.stringify(this.table));
|
||||||
|
},
|
||||||
|
async export_set() {
|
||||||
|
let fileHandle = await window.showSaveFilePicker({
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
description: "JSON file",
|
||||||
|
accept: {
|
||||||
|
'text/json': ['.json'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
let writestream = await fileHandle.createWritable();
|
||||||
|
let data = {
|
||||||
|
wordsets: this.wordsets,
|
||||||
|
words: {}
|
||||||
|
};
|
||||||
|
for (let i of Object.values(this.wordsets)) {
|
||||||
|
for (let j of i) {
|
||||||
|
let temp = localStorage.getItem(j.id);
|
||||||
|
data.words[j.id] = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writestream.write(JSON.stringify(data));
|
||||||
|
writestream.close();
|
||||||
|
},
|
||||||
|
async import_set() {
|
||||||
|
let [fileHandle] = await window.showOpenFilePicker({
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
description: "JSON file",
|
||||||
|
accept: {
|
||||||
|
'text/json': ['.json'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
let file = await fileHandle.getFile();
|
||||||
|
let data = JSON.parse(await file.text());
|
||||||
|
let cnt = 0;
|
||||||
|
for (let i of Object.keys(data.wordsets)) {
|
||||||
|
for (let j of data.wordsets[i]) {
|
||||||
|
if (!localStorage.getItem(j.id)) {
|
||||||
|
if (!this.wordsets[i]) this.wordsets[i] = [];
|
||||||
|
this.wordsets[i].push(j);
|
||||||
|
localStorage.setItem(j.id, data.words[j.id]);
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
localStorage.setItem("wordsets", JSON.stringify(this.wordsets));
|
||||||
|
ElNotification({
|
||||||
|
type: "success",
|
||||||
|
title: "添加成功",
|
||||||
|
message: `已添加 ${cnt} 本单词本`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.active_set_class = Object.keys(this.wordsets)[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
#header {
|
||||||
|
border-bottom: solid 1px #bcbcbc;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark #header {
|
||||||
|
border-bottom: solid 1px #616161;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-weight: 800;
|
||||||
|
color: var(--text-color);
|
||||||
|
font-size: 35px;
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
opacity: 0;
|
||||||
|
transition: .5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wordset:hover .option {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card div {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#new div {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sets-container {
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="hello">
|
|
||||||
<h1>{{ msg }}</h1>
|
|
||||||
<p>
|
|
||||||
For a guide and recipes on how to configure / customize this project,<br>
|
|
||||||
check out the
|
|
||||||
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
|
|
||||||
</p>
|
|
||||||
<h3>Installed CLI Plugins</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
|
|
||||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
|
|
||||||
</ul>
|
|
||||||
<h3>Essential Links</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
|
|
||||||
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
|
|
||||||
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
|
|
||||||
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
|
|
||||||
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
|
|
||||||
</ul>
|
|
||||||
<h3>Ecosystem</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
|
|
||||||
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
|
|
||||||
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
|
|
||||||
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
|
|
||||||
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'HelloWorld',
|
|
||||||
props: {
|
|
||||||
msg: String
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
|
||||||
<style scoped>
|
|
||||||
h3 {
|
|
||||||
margin: 40px 0 0;
|
|
||||||
}
|
|
||||||
ul {
|
|
||||||
list-style-type: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
li {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0 10px;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: #42b983;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
<template>
|
||||||
|
<div id="ball"></div>
|
||||||
|
<div class="container">
|
||||||
|
<div id="main">
|
||||||
|
<div id="title">WordIn</div>
|
||||||
|
<div class="colbox">
|
||||||
|
<router-link to="/recite" class="button">
|
||||||
|
背诵
|
||||||
|
</router-link>
|
||||||
|
<router-link to="editor" class="button">
|
||||||
|
编辑单词本
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="setting">
|
||||||
|
<box-icon class="btn" color="var(--text-color)" name='cog' @click="open_setting_dialog"></box-icon>
|
||||||
|
</div>
|
||||||
|
<el-dialog v-model="settingVisible" title="设置">
|
||||||
|
<div class="title">更换自定义背景</div>
|
||||||
|
<div class="colbox" style="margin: 10px;">
|
||||||
|
<el-input v-model="img_url"></el-input>
|
||||||
|
<el-button type="primary" style="margin-left: 10px;" @click="set_bg">确定</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="title">黑暗模式</div>
|
||||||
|
<el-switch @change="toggleDark" v-model="isdark" size="large" active-text="Dark" inactive-text="Light" />
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "HomePage",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
img_url: "",
|
||||||
|
settingVisible: false,
|
||||||
|
isdark: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
set_bg() {
|
||||||
|
localStorage.setItem("bgimg", this.img_url);
|
||||||
|
window.location.reload();
|
||||||
|
},
|
||||||
|
open_setting_dialog() {
|
||||||
|
this.settingVisible = true;
|
||||||
|
},
|
||||||
|
toggleDark(){
|
||||||
|
if(this.isdark){
|
||||||
|
document.getElementsByTagName("html")[0].className= "dark";
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
document.getElementsByTagName("html")[0].className= "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created(){
|
||||||
|
if(document.getElementsByTagName("html")[0].className === "dark"){
|
||||||
|
this.isdark = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.title {
|
||||||
|
font-weight: 800;
|
||||||
|
color: var(--text-color);
|
||||||
|
font-size: 35px;
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#setting {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#title {
|
||||||
|
font-size: 180px;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
width: 200px;
|
||||||
|
height: 100px;
|
||||||
|
font-size: 25px;
|
||||||
|
font-weight: 800;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: solid 1px #FAFAFA;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: var(--el-box-shadow);
|
||||||
|
background-color: rgba(255, 255, 255, 0.237);
|
||||||
|
backdrop-filter: blur(20px);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: .5s;
|
||||||
|
margin: 30px;
|
||||||
|
color: var(--text-color);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark .button{
|
||||||
|
background-color: rgba(56, 56, 56, 0.301);
|
||||||
|
border: solid 1px #848484;
|
||||||
|
box-shadow: 0px 12px 32px 4px rgba(198, 198, 198, 0.078), 0px 8px 20px rgba(216, 216, 216, 0.171);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:hover {
|
||||||
|
box-shadow: var(--el-box-shadow) inset #00000017 0px 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ball {
|
||||||
|
background-image: linear-gradient(120deg, #e0c3fcca 0%, #8ec5fcc4 100%);
|
||||||
|
width: 1500px;
|
||||||
|
height: 1500px;
|
||||||
|
border-radius: 100%;
|
||||||
|
position: absolute;
|
||||||
|
top: -100%;
|
||||||
|
right: -20%;
|
||||||
|
animation: enter .8s ease-out;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main {
|
||||||
|
margin: 80px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,343 @@
|
||||||
|
<template>
|
||||||
|
<el-page-header style="margin:20px" @back="back_home">
|
||||||
|
<template #content>
|
||||||
|
<span class="text-large font-600 mr-3"> 背诵 </span>
|
||||||
|
</template>
|
||||||
|
</el-page-header>
|
||||||
|
<div v-if="mode === 0" class="container">
|
||||||
|
<div class="card item">
|
||||||
|
<div class="title">选择测验范围:</div>
|
||||||
|
<el-checkbox v-model="checkAll" :indeterminate="isIndeterminate" @change="handleCheckAllChange"
|
||||||
|
size="large">全选</el-checkbox>
|
||||||
|
<el-checkbox-group v-model="checkedSets" @change="handleChange">
|
||||||
|
<div class="set_radios" v-for="(set_class, set_class_name) in wordsets" :key="set_class_name">
|
||||||
|
<p>{{ set_class_name }}</p>
|
||||||
|
<el-checkbox class="checkbox" v-for="set in set_class" :key="set.id" :label="set.id" size="large"
|
||||||
|
border>
|
||||||
|
<div style="font-size: 18px;font-weight: 500;">
|
||||||
|
{{ set.name }}
|
||||||
|
</div>
|
||||||
|
</el-checkbox>
|
||||||
|
</div>
|
||||||
|
</el-checkbox-group>
|
||||||
|
<el-button style="margin: 20px;width: 50%;" type="primary" @click="init">开始背诵</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="card item">
|
||||||
|
<div class="title" style="font-size: 35px;">继续上一次的背诵</div>
|
||||||
|
<el-button style="margin: 20px;width: 50%;" type="primary" @click="last_recite">开始背诵</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="test" class="colbox" v-if="mode === 1">
|
||||||
|
<div style="flex-grow: 1;" class="card">
|
||||||
|
<div id="status">背诵进度: {{ current + 1 }}/{{ total }}</div>
|
||||||
|
<div>
|
||||||
|
<textarea autofocus id="input" v-if="current === answered" @input="change" class="answer"
|
||||||
|
spellcheck="false">
|
||||||
|
</textarea>
|
||||||
|
<div v-if="current < answered" class="answer">{{ word.word }}</div>
|
||||||
|
</div>
|
||||||
|
<p id="explain">
|
||||||
|
<el-button v-if="current > 0" @click="prev">上一个</el-button>
|
||||||
|
<el-button v-if="current < answered" @click="next">下一个</el-button>
|
||||||
|
<el-button v-if="current === answered" @click="showAnswer">显示答案</el-button>
|
||||||
|
<el-button @click="terminate" type="danger">停止背诵</el-button>
|
||||||
|
<box-icon class="btn" style="margin-left: 10px;translate: 0 4px;" @click="audio_play"
|
||||||
|
name='volume-full'></box-icon>
|
||||||
|
</p>
|
||||||
|
<div id="trans">{{ word.type }} {{ word.trans }}</div>
|
||||||
|
</div>
|
||||||
|
<div id="add-to-box" class="card">
|
||||||
|
<el-text class="mx-1" style="font-size: 35px;font-weight: 800;">加入至</el-text>
|
||||||
|
<div class="colbox para">
|
||||||
|
<div class="mid-text">分组:</div>
|
||||||
|
<el-select v-model="set_class" class="m-2" placeholder="Select">
|
||||||
|
<el-option v-for="item in Object.keys(wordsets)" :key="item" :label="item" :value="item" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="colbox para">
|
||||||
|
<div class="mid-text">单词本:</div>
|
||||||
|
<el-select v-model="set_id" class="m-2" placeholder="Select">
|
||||||
|
<el-option v-for="item in wordsets[set_class]" :key="item.id" :label="item.name" :value="item.id" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<el-button @click="add_to" type="primary">添加</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ElMessage, ElNotification, ElMessageBox } from 'element-plus';
|
||||||
|
import { nextTick } from 'vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "WordRecite",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
mode: 0,
|
||||||
|
allsets: [],
|
||||||
|
wordsets: window.wordsets,
|
||||||
|
checkedSets: [],
|
||||||
|
isIndeterminate: false,
|
||||||
|
checkAll: false,
|
||||||
|
testWords: [],
|
||||||
|
seq: [],
|
||||||
|
total: 0,
|
||||||
|
current: 0,
|
||||||
|
answered: 0,
|
||||||
|
word: {},
|
||||||
|
set_class: "",
|
||||||
|
set_id: "",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleCheckAllChange(val) {
|
||||||
|
this.checkedSets = val ? this.allsets : [];
|
||||||
|
this.isIndeterminate = false;
|
||||||
|
},
|
||||||
|
handleChange(value) {
|
||||||
|
console.log(this.checkedSets);
|
||||||
|
this.checkAll = value.length === this.allsets.length;
|
||||||
|
this.isIndeterminate = value.length > 0 && value.length < this.allsets.length;
|
||||||
|
},
|
||||||
|
init() {
|
||||||
|
if (this.checkedSets.length <= 0) {
|
||||||
|
ElMessage({
|
||||||
|
message: "请至少选择一个单词本",
|
||||||
|
type: "error"
|
||||||
|
})
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let seed = (new Date()).getTime();
|
||||||
|
this.testWords = [];
|
||||||
|
for (let i of this.checkedSets) {
|
||||||
|
let words = JSON.parse(localStorage.getItem(i));
|
||||||
|
this.testWords = this.testWords.concat(...words);
|
||||||
|
}
|
||||||
|
localStorage.setItem("lastrecite", JSON.stringify({
|
||||||
|
checkedSets: this.checkedSets,
|
||||||
|
seed: seed
|
||||||
|
}))
|
||||||
|
this.total = this.testWords.length;
|
||||||
|
if (this.total === 0) {
|
||||||
|
ElMessage({
|
||||||
|
message: "单词本为空",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let arr = Array.from(new Array(this.total).keys());
|
||||||
|
for (let i = this.total - 1; i > 0; i--) {
|
||||||
|
[arr[i], arr[seed % i]] = [arr[seed % i], arr[i]];
|
||||||
|
}
|
||||||
|
this.seq = arr;
|
||||||
|
this.current = 0;
|
||||||
|
this.answered = 0;
|
||||||
|
this.mode = 1;
|
||||||
|
nextTick(() => {
|
||||||
|
this.show();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
last_recite() {
|
||||||
|
let lr = JSON.parse(localStorage.getItem("lastrecite"));
|
||||||
|
if (!lr) {
|
||||||
|
ElMessage({
|
||||||
|
message: "没有记录",
|
||||||
|
type: "error"
|
||||||
|
})
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.testWords = [];
|
||||||
|
for (let i of lr.checkedSets) {
|
||||||
|
let content = localStorage.getItem(i);
|
||||||
|
if (content) this.testWords = this.testWords.concat(...JSON.parse(content));
|
||||||
|
else {
|
||||||
|
ElMessage({
|
||||||
|
message: "单词本不存在",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.total = this.testWords.length;
|
||||||
|
let arr = Array.from(new Array(this.total).keys());
|
||||||
|
for (let i = this.total - 1; i > 0; i--) {
|
||||||
|
[arr[i], arr[lr.seed % i]] = [arr[lr.seed % i], arr[i]];
|
||||||
|
}
|
||||||
|
this.seq = arr;
|
||||||
|
let cur = Number(localStorage.getItem("lastcurrent"));
|
||||||
|
this.current = cur;
|
||||||
|
this.answered = cur;
|
||||||
|
this.mode = 1;
|
||||||
|
nextTick(() => {
|
||||||
|
this.show();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
next() {
|
||||||
|
this.current++;
|
||||||
|
if (this.current === this.total) {
|
||||||
|
this.mode = 0;
|
||||||
|
ElNotification({
|
||||||
|
message: "您已完成所有单词的背诵",
|
||||||
|
title: "Congratulations!",
|
||||||
|
type: "success"
|
||||||
|
})
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nextTick(() => {
|
||||||
|
this.show();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
prev() {
|
||||||
|
this.current--;
|
||||||
|
this.show();
|
||||||
|
},
|
||||||
|
showAnswer() {
|
||||||
|
ElMessage(`答案:${this.word.word}`);
|
||||||
|
},
|
||||||
|
show() {
|
||||||
|
this.word = this.testWords[this.seq[this.current]];
|
||||||
|
console.log(this.word);
|
||||||
|
if (this.current === this.answered) {
|
||||||
|
let e = document.getElementById("input");
|
||||||
|
e.value = "_".repeat(this.word.word.length);
|
||||||
|
e.setSelectionRange(0, 0);
|
||||||
|
e.style.height = `${e.scrollHeight}px`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
change() {
|
||||||
|
let e = document.getElementById("input");
|
||||||
|
let t = e.value, n = t.indexOf("_"), o;
|
||||||
|
if (n == this.word.word.length) {
|
||||||
|
if (t.substring(0, t.length - 1).toLowerCase() == this.word.word.toLowerCase()) {
|
||||||
|
ElMessage({
|
||||||
|
title: "Success",
|
||||||
|
message: "正确",
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
this.answered++;
|
||||||
|
localStorage.setItem("lastcurrent",this.answered.toString());
|
||||||
|
this.next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ElMessage({
|
||||||
|
title: "Error",
|
||||||
|
message: "错误",
|
||||||
|
type: "error"
|
||||||
|
}),
|
||||||
|
o = "_".repeat(this.word.word.length);
|
||||||
|
n = 0;
|
||||||
|
} else
|
||||||
|
n == -1 ? o = "_".repeat(this.word.word.length) : (o = t.substring(0, n),
|
||||||
|
o += "_".repeat(this.word.word.length - n));
|
||||||
|
e.value = o;
|
||||||
|
e.setSelectionRange(n, n);
|
||||||
|
e.style.height = `${e.scrollHeight}px`;
|
||||||
|
e.focus();
|
||||||
|
},
|
||||||
|
audio_play() {
|
||||||
|
var t = new Audio("https://dict.youdao.com/dictvoice?audio=" + this.word.word);
|
||||||
|
t.play();
|
||||||
|
},
|
||||||
|
terminate() {
|
||||||
|
ElMessageBox.confirm("确定要终止吗?")
|
||||||
|
.then(() => {
|
||||||
|
this.mode = 0;
|
||||||
|
ElNotification({
|
||||||
|
message: "背诵已终止",
|
||||||
|
type: "info",
|
||||||
|
title: "Terminate"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
back_home() {
|
||||||
|
this.$router.push("/");
|
||||||
|
},
|
||||||
|
add_to() {
|
||||||
|
let data = JSON.parse(localStorage.getItem(this.set_id));
|
||||||
|
data.push(this.word);
|
||||||
|
localStorage.setItem(this.set_id, JSON.stringify(data));
|
||||||
|
return ElMessage({
|
||||||
|
message: `已添加 ${this.word.word} (${this.word.type})`,
|
||||||
|
type: 'success',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
for (let i of Object.values(window.wordsets)) {
|
||||||
|
for (let j of i) {
|
||||||
|
this.allsets.push(j.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(this.allsets);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.item {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 50px;
|
||||||
|
font-weight: 800;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox {
|
||||||
|
font-size: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#status {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.answer {
|
||||||
|
margin-top: 10px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||||
|
font-size: 70px;
|
||||||
|
letter-spacing: 30px;
|
||||||
|
border: none;
|
||||||
|
outline-style: none;
|
||||||
|
height: 100px;
|
||||||
|
padding: 0;
|
||||||
|
transition: .5s;
|
||||||
|
resize: vertical;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#test {
|
||||||
|
padding: 50px;
|
||||||
|
animation: enter .5s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#explain {
|
||||||
|
font-size: 25px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#trans {
|
||||||
|
font-size: 50px;
|
||||||
|
transition: .5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.set_radios {
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.set_radios p {
|
||||||
|
font-size: 25px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#add-to-box {
|
||||||
|
width: 30%;
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
10
src/main.js
10
src/main.js
|
|
@ -1,4 +1,12 @@
|
||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
import ElementPlus from 'element-plus'
|
||||||
|
import 'element-plus/dist/index.css'
|
||||||
|
import 'element-plus/theme-chalk/dark/css-vars.css'
|
||||||
|
import 'boxicons'
|
||||||
|
import router from './router.js'
|
||||||
|
|
||||||
createApp(App).mount('#app')
|
const app = createApp(App);
|
||||||
|
app.use(ElementPlus);
|
||||||
|
app.use(router);
|
||||||
|
app.mount('#app');
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
import {createRouter,createWebHashHistory} from 'vue-router'
|
||||||
|
import editor from './components/Editor.vue'
|
||||||
|
import recite from './components/Recite.vue'
|
||||||
|
import home from "./components/Home.vue"
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
{ path: '/', component: home },
|
||||||
|
{ path: '/recite', component: recite },
|
||||||
|
{ path: '/editor', component: editor },
|
||||||
|
]
|
||||||
|
|
||||||
|
export default createRouter({
|
||||||
|
history: createWebHashHistory(),
|
||||||
|
routes,
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
const axios = require("axios");
|
||||||
|
const CryptoJS = require("crypto-js");
|
||||||
|
const fs = require("fs");
|
||||||
|
|
||||||
|
function truncate(q){
|
||||||
|
var len = q.length;
|
||||||
|
if(len<=20) return q;
|
||||||
|
return q.substring(0, 10) + len + q.substring(len-10, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
var appKey = "73ff413042ffde53";
|
||||||
|
var key = "FAt2w0mFLoDDIz6ZoAY8WJFgZvLFtIYe";
|
||||||
|
var salt = (new Date).getTime();
|
||||||
|
var curtime = Math.round(new Date().getTime()/1000);
|
||||||
|
var query = 'hello world';
|
||||||
|
var str1 = appKey + truncate(query) + salt + curtime + key;
|
||||||
|
var sign = CryptoJS.SHA256(str1).toString(CryptoJS.enc.Hex);
|
||||||
|
|
||||||
|
console.log(sign);
|
||||||
|
|
||||||
|
axios.post('https://openapi.youdao.com/api',{
|
||||||
|
q: query.toString(),
|
||||||
|
appKey: appKey,
|
||||||
|
salt: (new Date).getTime(),
|
||||||
|
from: 'en',
|
||||||
|
to: 'zh-CHS',
|
||||||
|
sign: sign,
|
||||||
|
signType: "v3",
|
||||||
|
curtime:Math.round(new Date().getTime()/1000),
|
||||||
|
},{
|
||||||
|
headers:{
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
}).then((res)=>{
|
||||||
|
fs.writeFileSync("result.json",JSON.stringify(res.data));
|
||||||
|
})
|
||||||
Loading…
Reference in New Issue