❓ 六、VARBook是怎么实现的
具体内容请见代码仓库
前端
前端的设计思路是通过Vue的组件化实现的
在这里给大家贴上一些代码
代码中都有相应的注释
components
Header
banner.vue
<template> <div class="banner-box"></div> </template> <script> export default { name: "banner" } </script> <style scoped> /*Logo image style*/ .banner-box { text-align: center; background-image: url("../../assets/images/Banner.svg"); background-size: 100%; background-repeat: no-repeat; margin: auto; } /*Match the banner style when accessing from the computer*/ @media (min-width: 768px) { .banner-box { width: 550px; height: 130px; } } /*Match the style of the banner when accessing from the mobile terminal*/ @media (max-width: 768px) { .banner-box { width: 420px; height: 110px } } @media (max-width: 550px) { .banner-box { width: 72%; height: 90px; } } @media (max-width: 460px) { .banner-box { width: 72%; height: 73px; } } /*end*/ </style>
Search
searchBar.vue
<template> <el-input id="searchBox" v-model="input_contents" placeholder="You need to translate the variables" @keydown.enter="openSearch" autofocus> <template #prefix> <el-icon class="el-input__icon" @click="openSearch"> <svg class="icon" width="200" height="200" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-365b8594=""> <path fill="currentColor" d="M795.904 750.72l124.992 124.928a32 32 0 01-45.248 45.248L750.656 795.904a416 416 0 1145.248-45.248zM480 832a352 352 0 100-704 352 352 0 000 704z"></path> </svg> </el-icon> </template> </el-input> </template> <script> import {ElMessage} from "element-plus"; export default { name: "searchBar", data(){ return{ // Content retrieved by users input_contents:'', } }, mounted() { // Monitor shortcut keys, focus the input box when control is pressed document.onkeydown = function (e) { if (e.keyCode == 17){ document.getElementById("searchBox").focus() } } }, methods: { openSearch(){ // Determine whether it contains Chinese and English let isNull = this.input_contents.replace(/[^^\u4E00-\u9FA5a-zA-Z]/g, ''); if (isNull) { // Start the query and pass the input to the parent component this.$emit("startSearch",this.input_contents); } else { ElMessage({ showClose: true, message: '目前仅支持搜索中/英文', type: 'warning', }) } } } } </script> <style scoped> /*Modify the default style of el-input*/ .el-input { width: 70%; max-width: 600px; margin-bottom: 10px; margin-top: 10px; } </style>
searchResult.vue
<template> <el-card class="box-card" v-if="isLoading"> <el-table :data="tableData" style="width: 100%;"> <el-table-column label="适用参考" width="180"> <template #default="scope"> <span :id="'applicable-'+scope.$index"> {{ scope.row.applicable }} </span> </template> </el-table-column> <el-table-column label="变量"> <template #default="scope"> <span class="var-style" :id="'var-'+scope.$index" @click="copy(scope.row.var,scope.$index)"> {{ scope.row.var }} </span> </template> </el-table-column> </el-table> </el-card> </template> <script> import {ElMessage, ElNotification} from "element-plus"; export default { name: "searchResult", props: { input_contents: String }, data() { return { tableData: [], isLoading: false } }, mounted() { this.updateData(); }, watch: { input_contents() { this.updateData(); }, tableData(){ this.isLoading = true; // Automatically copy the last content let isAutoCopy = window.localStorage.getItem("autoCopyId"); if (isAutoCopy) { setTimeout(() =>{ document.getElementById(isAutoCopy).click(); },500) } } }, methods: { // copy content copy: function (data, index) { const input = document.createElement("input"); input.value = data; document.body.appendChild(input); input.select(); document.execCommand("Copy"); document.body.removeChild(input); let applicable_references = document.getElementById("applicable-" + index).innerText; ElNotification({ title: '复制成功', message: "适用参考: " + applicable_references + "", type: 'success', dangerouslyUseHTMLString: true }) window.localStorage.setItem("autoCopyId", "var-" + index); }, updateData() { let params = { "input": this.input_contents } this.$api.post("/translation", params).then((res) => { if (res.data.code == 200) { let ram_tableData = [] let var_values = res.data.varData.var; let applicable = res.data.varData.applicable; for (const [index, value] of var_values.entries()) { ram_tableData.push({ "var": value, "applicable": applicable[index] }) } ram_tableData.push({ "var": res.data.translate, "applicable": "注释" }) this.tableData = ram_tableData; } else if (res.data.code == 501) { ElMessage({ showClose: true, message: '服务器过载,请稍后重试~', type: 'warning', }) } else { ElNotification({ title: '未知错误', message: '错误码: ' + res.data.code, type: 'warning', }) } }).catch((err) => { console.log(err) }) }, }, } </script> <style scoped> .el-card { max-width: 655px !important; text-align: center !important; margin: 50px auto !important; } .var-style:hover { color: #0663c4; } .var-style { color: #409eff; cursor: pointer } </style> <style> .el-table .el-table__cell { text-align: center !important; } </style>
textTips.vue
<template> <el-link disabled>tips: 按 Ctrl 键自动对焦搜索框,按 Enter 键自动搜索</el-link> </template> <script> export default { name: "textTips" } </script>
page
Home.vue
<template> <header :class="isContentOnce ? 'reduceTop' : 'increaseTop'"> <banner></banner> </header> <section> <search-bar @start-search="openSearch"></search-bar> <div class="text-tips"> <text-tips></text-tips> </div> <search-result v-if="isContentOnce" :input_contents="input"></search-result> </section> </template> <script> import banner from "@/components/Header/banner"; import searchBar from "@/components/Search/searchBar"; import textTips from "@/components/Search/textTips"; import SearchResult from "@/components/Search/searchResult"; import {ref} from "vue"; export default { components: { SearchResult, banner, searchBar, textTips }, setup() { // If it's the first search, move the head up if it's the first time const isContentOnce = ref(false); // Get the input value and pass it to the search-result component const input = ref(''); const openSearch = v => { // update input input.value = v; // The first search will be performed and the content of the search-result tag will be displayed isContentOnce.value = true; } return {openSearch, input, isContentOnce} }, data() { return {} }, } </script> <style scoped> /*Reduce the newline of the header*/ .reduceTop { margin-top: 25px; transition: all 0.3s ease-in-out; } /*Add a new line to the header, as it looks when first opened*/ .increaseTop { margin-top: 130px; transform: translateY(0px); } /*When accessing the mobile terminal, the line wrapping of the header should be reduced, which is more beautiful*/ @media (max-width: 550px) { .increaseTop { margin-top: 70px; } } /*section tag content centered*/ section { text-align: center; } .text-tips { text-align: left; max-width: 600px; margin: auto; width: 70%; } </style> <style> /*global white*/ body { color: white; } </style>
router
index.js
import {createRouter, createWebHashHistory} from 'vue-router' import Home from "@/page/Home"; const routes = [ { path: '/', name: 'Home', component:Home }, ] const router = createRouter({ history:createWebHashHistory(), routes }) export default router
main.js
import {createApp} from 'vue' import VARBook from './VARBook.vue' import router from "@/router"; import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import axios from "@/plugins/axios"; let varBook = createApp(VARBook); varBook.use(ElementPlus) varBook.use(router) varBook.mount('#varBook') varBook.config.globalProperties.$api = axios
package.json
{ "name": "varbook", "version": "0.1.0", "private": true, "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", "lint": "vue-cli-service lint" }, "dependencies": { "axios": "^0.24.0", "core-js": "^3.6.5", "element-plus": "^1.3.0-beta.1", "vue": "^3.0.0", "vue-axios": "^3.4.0", "vue-router": "^4.0.12" }, "devDependencies": { "@vue/cli-plugin-babel": "~4.5.0", "@vue/cli-plugin-eslint": "~4.5.0", "@vue/cli-service": "~4.5.0", "@vue/compiler-sfc": "^3.0.0", "babel-eslint": "^10.1.0", "eslint": "^6.7.2", "eslint-plugin-vue": "^7.0.0" }, "eslintConfig": { "root": true, "env": { "node": true }, "extends": [ "plugin:vue/vue3-essential", "eslint:recommended" ], "parserOptions": { "parser": "babel-eslint" }, "rules": {} }, "browserslist": [ "> 1%", "last 2 versions", "not dead" ] }
后端
后端的具体代码就不赘述了,内容多且不好描述,在此给大家贴上一个粗略的流程图,希望能给大家提供一些思路与灵感
关于开发进度,欢迎大家持续关注我的GitHub仓库
欢迎大家使用,VARBook正在逐步完善中…欢迎大家出谋划策
快速访问 👉
https://varbook.uiuing.com/
GitHub仓库👉
https://github.com/uiuing/VARBook
VARBook Release v1.0.0 👉
https://github.com/uiuing/VARBook/releases/tag/v1.0.0