向 doubao.com/chat/ 提问:
node.js + js-mdict 作为后端,vue 3 + vite 作为前端,编写在线查询英汉词典
后端部分(express + js-mdict
)
1. 项目结构
首先,创建一个项目目录,结构如下:cd \js
mkdir mydict-app
mydict-app/
├── public/
│ ├── index.html
│ ├── styles.css
│ └── script.js
├── server/
│ └── app.js
├── package.json
└── README.md
英汉词典文件(*.mdx , *.mdd) 已经放在 /js/testdict/
2. 安装依赖
在项目根目录下运行以下命令,初始化项目并安装所需的依赖:
cd mydict-app
npm init -y
npm install express cors
added 69 packages in 6s
npm install js-mdict
added 3 packages in 2s
3. 编写后端代码
创建 app.js
文件,代码如下:
const express = require('express');
const cors = require('cors');
const Mdict = require('js-mdict');
const path = require('path');
const app = express();
app.use(cors());
app.use(express.json());
// 加载MDict词典文件
//const mdict = new Mdict('path/to/your/dictionary.mdx');
const mdx = new Mdict.MDX('/js/testdict/oale8.mdx');
const mdd = new Mdict.MDD('/js/testdict/oale8.mdd');
const isWord = (txt) => {
// 只允许字母、/、空格、-
return /^[a-zA-Z \/\-]+$/.test(txt);
};
// 处理查询请求
app.get('/search', async (req, res) => {
const word = req.query.word;
if (!word) {
return res.status(400).json({ error: 'No word input'});
}
console.log("cha: "+ word);
try {
let data = await mdx.lookup(word);
let result;
if (data.definition){ result = data.definition;}
else { result ='';}
res.json({ result });
} catch (error) {
console.error('Error searching in MDict:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// 处理前缀查询请求
app.get('/prefix', (req, res) => {
let word = req.query.word;
// 检查word是否合法
if (word.length>50 || !isWord(word)) {
return res.status(400).json({ error: 'Invalid input.'});
}
if (word) {
let alist = mdx.prefix(word);
console.log("pre: "+ word);
if(alist.length >0){
let wordls = [];
alist.forEach(function(value){
wordls.push(value.keyText);
});
res.json({wordls});
} else {
res.status(500).json({ info: 'this word not found'});
}
} else {
res.status(500).json({ error: 'No word input'});
}
});
// 处理模糊查询请求
app.get('/fuzzy', (req, res) => {
let word = req.query.word;
// 检查word是否合法
if (word.length>50 || !isWord(word)) {
return res.status(400).json({ error: 'Invalid input.'});
}
if (word) {
let alist = mdx.fuzzy_search(word,3,1);
console.log("fuzzy: "+ word);
if(alist.length >0){
let wordls = [];
alist.forEach(function(value){
wordls.push(value.keyText);
});
res.json({wordls});
} else {
res.status(500).json({ info: 'this word not found'});
}
} else {
res.status(500).json({ error: 'No word input'});
}
});
// 指定目录
const dir1 = "/";
// 实现文件下载,*/是路径
app.get('/*/:fileName', (req, res, next) => {
let path1 = req.params[0]; // 捕获 * 匹配的部分
let fileName = req.params.fileName; // 捕获文件名
// 检查路径中是否包含非法字符(如 ..)
if (fileName.includes('..')) {
return res.status(400).json({ error: 'Invalid path: Path traversal is not allowed.'});
}
//console.log(fileName);
let extname = path.extname(fileName);
let ext = extname.substring(1).toLowerCase();
if (['bmp','gif','jpg','png'].includes(ext)){
let filePath = path.join(dir1,path1, fileName);
//console.log(filePath);
let data = mdd.locate(filePath);
if (data){
console.log('key: '+ data.keyText);
//console.log(Buffer.isBuffer(data.definition));
if (data.definition){
let binaryData = Buffer.from(data.definition, 'base64');
//res.setHeader('Content-Type', 'application/octet-stream');
res.set({
'Content-Type': 'image',
'Content-Disposition': 'attachment;',
'Content-Length': Buffer.byteLength(binaryData)
});
//console.log('bytes: '+ Buffer.byteLength(binaryData));
res.end(binaryData);
} else {
res.status(400).json({ error: 'data.definition is null'});
}
} else {
res.status(400).json({ error: 'data is null'});
}
} else {
res.status(400).json({ error: 'filename.ext is not image'});
}
});
const port = process.env.PORT || 8006;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
4. 运行后端服务: cd mydict-app
node server/app.js
前端部分(Vue 3 + Vite)
1. 创建前端项目 cd \js
cnpm create vite@latest mydict-web --template vue
选 Vue 3
选 javascript
项目结构:Vite 会自动创建一个基本的项目结构,包括 src
目录下的组件、路由和状态管理等文件。主要文件和目录如下:
App.vue
:根组件main.js
:应用程序入口router
:Vue Router配置store
:状态管理
在 public
中添加一些英汉字典的样式:oalecd8e.css , oalecd8e.js , uk_pron.png, us_pron.png,
copy jquery-3.2.1.min.js pulibc\jquery.js
2. 安装依赖
cd mydict-web
cnpm install axios
cnpm install vue-router -S
package.json 如下
{
"name": "mydict-web",
"private": true,
"version": "0.1.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"axios": "^1.7.9",
"vue": "^3.5.13",
"vue-router": "^4.5.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.1",
"vite": "^6.1.0"
}
}
3. 编写前端代码
修改 src/App.vue
文件:
<template>
<div id="app">
<input v-model="sWord" placeholder="请输入英文单词" @keyup.enter="search">
<button @click="search">查询</button>
<button @click="prefix">前缀查询</button>
<button @click="fuzzy">模糊查询</button>
<div v-if="result">
<h3>查询结果</h3>
<div v-html="result"></div>
</div>
<div v-if="error">{{ error }}</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
import axios from 'axios';
const sWord = ref('');
const result = ref('');
const error = ref('');
// 查询
const search = async () => {
try {
const response = await axios.get('http://localhost:8006/search', {
params: {
word: sWord.value
}
});
result.value = response.data.result;
error.value = '';
} catch (err) {
result.value = '';
error.value = err.response?.data?.error || '请求出错,请稍后重试';
}
};
// 前缀查询
const prefix = async () => {
try {
const response = await axios.get('http://localhost:8006/prefix', {
params: {
word: sWord.value
}
});
let items = [];
let wordls = response.data.wordls;
wordls.forEach((item, i) => {
if (i<=20){
items[i] = `<a href="http://localhost:8006/search?word=${item}" target="#">${item}</a>`;
}
});
if (items.length >0){ result.value = items.join(', ');}
else {result.value = '';}
error.value = '';
} catch (err) {
result.value = '';
error.value = err.response?.data?.error || '请求出错,请稍后重试';
}
};
// 模糊查询
const fuzzy = async () => {
try {
const response = await axios.get('http://localhost:8006/fuzzy', {
params: {
word: sWord.value
}
});
let items = [];
let wordls = response.data.wordls;
wordls.forEach((item, i) => {
if (i<=20){
items[i] = `<a href="http://localhost:8006/search?word=${item}" target="#">${item}</a>`;
}
});
if (items.length >0){ result.value = items.join(', ');}
else {result.value = '';}
error.value = '';
} catch (err) {
result.value = '';
error.value = err.response?.data?.error || '请求出错,请稍后重试';
}
};
</script>
<style scoped>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: left;
color: #2c3e50;
margin-top: 10px;
}
</style>
4. 运行前端项目
cd mydict-web
npm run dev
> mydict-web@0.1.0 dev
> vite
VITE v6.1.1 ready in 1083 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h + enter to show help
h
Shortcuts
press r + enter to restart the server
press u + enter to show server url
press o + enter to open in browser
press c + enter to clear console
press q + enter to quit
o
注意事项
- 跨域问题:在开发环境中使用
cors
中间件解决跨域问题,在生产环境中可以通过配置反向代理等方式处理。 - MDX 文件路径:确保
app.js
中的mdict Path
指向正确的.mdx
词典文件。 - 安全性:在生产环境中,需要考虑对后端接口进行安全防护,如限制请求频率、验证请求来源等。
通过以上步骤,你就可以实现一个简单的在线英汉词典查询系统。