nodejs:express + js-mdict 作为后端,vue 3 + vite 作为前端,在线查询英汉词典

news/2025/2/23 5:45:35

向 doubao.com/chat/ 提问:
node.js + js-mdict 作为后端,vue 3 + vite 作为前端,编写在线查询英汉词典

后端部分(expressjs-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">
    &nbsp; <button @click="search">查询</button>
    &nbsp; <button @click="prefix">前缀查询</button>
    &nbsp; <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 词典文件。
  • 安全性:在生产环境中,需要考虑对后端接口进行安全防护,如限制请求频率、验证请求来源等。

通过以上步骤,你就可以实现一个简单的在线英汉词典查询系统。


http://www.niftyadmin.cn/n/5863030.html

相关文章

kotlin的Int类型调用toString()会导致空指针吗

DeepSeek R1 思考 38 秒 思考过程 用户的问题是关于Kotlin中Int类型调用toString()是否会导致空指针异常。首先&#xff0c;我需要回忆Kotlin的基本类型系统。Kotlin中的Int属于基本数据类型&#xff0c;但它在语言层面被表示为非空类型。也就是说&#xff0c;Int变量本身不能…

node.js的版本管理

nvm&#xff08;Node Version Manager&#xff09; 适用系统&#xff1a; macOS、Linux&#xff0c;在 Windows 上也有移植版本 nvm - windows。 特点&#xff1a; 使用广泛&#xff0c;能方便地安装、切换不同版本的 Node.js&#xff0c;并且可以同时管理多个版本。 安装 nv…

基于 JavaWeb 的 Spring Boot 网上商城系统设计和实现(源码+文档+部署讲解)

技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…

http、websocket与MQTT协议

网络通讯协议 1、http、websocket与MQTT协议 核心区别 特点httpwebsocketMQTT协议协议模型请求-响应&#xff08;单项&#xff09;全双工双向通讯发布/订阅&#xff08;多对多&#xff09;连接状态无状态&#xff08;断连接或长连接需要要手东维护&#xff09;持久长连接&am…

WPS PPT插入各种线型形状(如画直线)的时候总是有箭头,如何还原成只画直线

WPS PPT插入各种线型形状&#xff08;如画直线&#xff09;的时候总是有箭头&#xff0c;如何还原成只画直线 文章目录 WPS PPT插入各种线型形状&#xff08;如画直线&#xff09;的时候总是有箭头&#xff0c;如何还原成只画直线一、问题出现的原因二、解决方法 在使用 WPS 演…

运维Ansible面试题及参考答案

目录 简述 Ansible 的工作原理,它是如何实现对远程主机管理的? Ansible 是基于什么语言开发的?这门语言的特性对 Ansible 的功能实现有哪些帮助? 解释 Agentless 在 Ansible 中的含义,与基于 Agent 的自动化工具相比,优势体现在哪? Ansible 中的 Inventory 文件是什…

Linux7-线程

一、前情回顾 chdir();功能&#xff1a; 函数用于改变当前进程的工作目录。 参数&#xff1a;路径&#xff08;Path&#xff09;&#xff1a;这是一个字符串参数&#xff0c;表示要切换到的目标目录的路径。 返回值&#xff1a; 成功&#xff1a;在成功改变当前工作目…

MongoDB应用设计调优

应用范式设计 什么是范式 数据库范式概念是数据库技术的基本理论&#xff0c;几乎是伴随着数据库软件产品的推出而产生的。在传统关系型数据库领域&#xff0c;应用开发中遵循范式是最基本的要求。但随着互联网行业的发展&#xff0c;NoSQL开始变得非常流行&#xff0c;在许多…