首页 > 其他分享 >考试系统前端项目复盘

考试系统前端项目复盘

时间:2023-09-09 18:22:35浏览次数:41  
标签:... const 前端 路径 js pdf return 复盘 考试

前段时间和朋友做了一个局域网考试系统,总共有3个端:考生端、监考端、管理端。

框架与相关的库

先简单说明一下我使用的框架和相关的库:

  • 构建工具:Vite

  • 框架:Vue3

  • UI组件库:element-plus

  • 网络请求库:axios

  • 路由跳转:vue-router

  • 状态管理:pinia

  • CSS扩展语言:sass

  • 其它与项目功能需求相关的库这里就不一一列出了

多端非根路径部署

考虑到每一个用户理论上只会使用其中一个端,如果将三个端绑定在一个Vue项目上,则会导致“捆绑销售”。因此,将三个端用三个Vue项目完成,然后让后端开发人员使用nginx配置好映射。最后我需要再写一个根路径的入口页面,用于跳转到三个端。

  • /:根路径,页面的内容主要是三个按钮,分别跳转到三个端;
  • /admin:管理端;
  • /teacher:监考端;
  • /student:考生端。

三个端的路径经由nginx配置之后,指向三个Vue项目的index.html,然后再加载各自的main.js

与以往将前端项目部署在根路径的情况不同,将前端项目部署在非根路径需要做相关配置。

主要是需要修改vite.config.jsvue-router的配置文件。

以管理端为例,由于其项目部署在/admin,因此需要配置项目的base

vite.config.js

export default defineConfig({
    ...
    base: '/admin/',
    ...
})

vue-router配置文件

const router = createRouter({
    ...
    history: createWebHistory(import.meta.env.BASE_URL),
	...
})

使用history模式,需要后端在nginx上做配置。而createWebHistory函数的参数需要传入base,即上面配置的/admin/

而余下的routes配置,就根据以往的编写方式就可以。

例如,管理端的登录页面,在配置了base: '/admin/'的情况下,在配置登录页面的路由的时候,只需要写/login

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      redirect: {name: 'login'}
    },
    {
      path: '/login',
      name: 'login',
      component: ()=>import('../views/LoginView.vue')
    }
  ]
})

实际上,从整个考试系统的角度看来,它匹配到的路径应该是:/admin/login

这是因为/admin/会先被nginx的配置捕获到,然后指向管理端这个Vue项目,返回管理端的index.htmlmain.js给用户(该系统的管理员),然后路径后续的/login会因为main.js中引入的路由配置文件,匹配上LoginView.vue,即登录页面。

盒子的最大宽度

页面中的文字依据来源可以分为两种:

  • 静态文本:即本身固化在代码中的文本;
  • 动态文本:由用户输入并显示在页面中的文本。

静态文本,例如侧边导航栏的按钮的文本,文本的字数是固定的。因此,侧边导航栏的宽度可以写成固定的。

而动态文本,是由用户输入的,并且大多数时候没有严格的字数限制。

我一开始犯了一个错,就是只使用flex:3flex:7简单地将页面分为左右布局,然后左边是一个列表,每一项都是一行用户输入的数据,即不做换行处理。

当用户输入了长文本之后,左边的列表会被子元素撑大,从而导致页面的左右布局比例被破坏。

因此这里由用户输入的数据构成的列表,应该使用css设置一个max-width,限制其最大宽度。

对象的深拷贝

使用JSON简单地实现了对象的深拷贝

// 存储对象的数组
list: []

// 添加新对象
list.push(JSON.stringify(newItem))

// 获取对象
function getItem(params){
    ...do some search
    return Json.parse(target)
}

pinia 实现试题管理模块

这里的试题是指添加试题时的阶段,即需要提供读与写操作。

image-20230909155800269

  • state:
state: ()=>({
    // 题目列表,存储题目对象,使用JSON简单实现了对象的深拷贝
    qList: [],
    // 当前编辑的题目的指针
    currIdx: -1
}),
  • getter:(返回常用数据)
getters: {
    // 题目数量
    count(){
        return this.qList.length
    },
    // 当前编辑的题目是否存在“上一题”
    hasPrev(){
        return this.currIdx>0
    },
    hasNext(){
        return this.currIdx<this.count
    }
},
  • actions:向外提供操作方法
actions: {
    // 初始化
    init(){
        this.qList.length = 0
        this.currIdx = 0
    },
    // 写操作
    saveQuestion(q){
        this.qList[this.currIdx] = JSON.stringify(q)
    },
    // 前一道题
    goPrevQuestion(){
        if(this.hasPrev){
            return JSON.parse(this.qList[--this.currIdx] || "")
        }
    },
    // 后一道题
    goNextQuestion(){
        const q = this.qList[++this.currIdx]
        return q===undefined?undefined:JSON.parse(q)
    },
    // 上传题目列表到后端
    async uploadQuestionList(){
        for await (let q of this.qList){
            q = JSON.parse(q)
            if(this.checkCompleteness(q)){
                await uploadQuestion(q)
            }
        }
    },
    checkCompleteness(q){
        // 用于检查一道题目是否设置完整
    },
    isEmpty(q){
        // 用于检查一道题目是否没有填写任何内容
    }
}

上述代码中的checkCompletenessisEmpty函数的实现涉及到试题对象的设计,较为复杂,这里不给出代码。

上传题目列表到后端的操作中,为了实现按顺序上传,需要使用for await(... of ...),而不能使用foreach await,后者无法保证上传顺序。

vite打包配置

vite.config.js中,通过如下配置,可以去除代码中的console.log,避免将数据带到生产环境,同时将js文件和assets文件打包到不同文件夹。

export default defineConfig({
    ...
    build: {
        terserOptions: {
            compress: {
                // 生产环境时移除console.log调试代码
                drop_console:true,
                drop_debugger: true
            }
        },
        rollupOptions: {
            output: {
                //对静态文件进行打包处理(文件分类)
                chunkFileNames: 'assets/js/[name]-[hash].js',
                entryFileNames: 'assets/js/[name]-[hash].js',
                assetFileNames: 'assets/[ext]/[name]-[hash].[ext]'
            }
        }
      }
    ...
})

文件下载功能

项目中有需求是:用户点击按钮之后下载文件。使用js实现:

// 下载文件
const downloadFile = () => {
    const tempDom = document.createElement('a')
    tempDom.href = "/file/demo.txt"
    tempDom.download = 'fileName.txt'
    tempDom.click()
}

这里创建了一个DOM对象,路径href是服务器上的文件路径,download属性的字符串是用户下载到的文件名。

pdf预览功能

我写了一个pdf-previewer.html文件,并放在根路径下,然后每次不同端的项目中,需要访问pdf文件的时候,就调用:

window.open('/pdf-preview.html?url='+path)

path是后端传过来的文件路径。

pdf-previewer.html中,

  • 使用iframe标签;
  • 封装了getQueryVariable函数,用来获取访问地址携带的参数(即文件的地址);
  • 为了解决缓存问题(利用iframe打开pdf后,当再次利用iframe打开另一个pdf时会显示第一份pdf,原因是浏览器对url的缓存处理),在url上添加时间戳。

参考自:PDF预览完整解决方案及各种兼容(VUE版) - 掘金 (juejin.cn)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>PDF预览窗口</title>
    <link rel="icon" href="/icon.png">
    <style>
        *{
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        body{
            width: 100%;
            height: 100vh;
            overflow: hidden;
        }
    </style>
</head>
<body>
    <iframe id="viewer" src="" style="width: 100%;height: 100vh;" frameborder="0"></iframe>
    <script>
        window.addEventListener ('load', () => {
            function getQueryVariable(variable) {
                let query = window.location.search.substring(1);
                let vars = query.split("&");
                for (let i = 0; i < vars.length; i++) {
                    let pair = vars[i].split("=");
                    if (pair[0] === variable) { return pair[1]; }
                }
                return (false);
            }
            
            let path = getQueryVariable('url')
            const fresh = new Date().getTime()

            path += '?fresh=' + fresh

            document.getElementById('viewer').setAttribute('src', path)
        });
    </script>
</body>
</html>

常用Message封装

el-message组件对于反馈功能很常用,封装成函数:

import { ElMessage } from 'element-plus'

const showError = (msg)=>{
    return ElMessage({
        type: 'error',
        message: msg
    })
}

const showSuccess = (msg)=>{
    return ElMessage({
        type: 'success',
        message: msg
    })
}

const showInfo = (msg)=>{
    return ElMessage({
        type: 'info',
        message: msg
    })
}

export { showError, showSuccess, showInfo }

使用CSS常量

使用CSS常量记录常用的尺寸、颜色,可以改一处,而变全局。

以下常量是我的项目中的一部分颜色,仅供参考,不具有普适性。

:root {
  --main-color: #31364d;
  --header-height: 60px;
  --border-color: #DCDFE6;
  --border-color-light: #E4E7ED;
  --border-color-darker: #CDD0D6;
  --page-background: #F2F3F5;
}

标签:...,const,前端,路径,js,pdf,return,复盘,考试
From: https://www.cnblogs.com/feixianxing/p/front-end-project-vue-multiple-ends-element-plus-rou

相关文章

  • 前端使用proxy代理解决跨域的问题
     实现方法:本地向proxy代理服务器发送请求,proxy接收本地请求,转换为目标地址相同IP和端口向目标地址发送请求。配置:(注意:因为我是本地启动的后端服务器,所以使用地址为localhost'一些为/api')vue.config.jsdevServer:{open:true,proxy:{"/localhost":{/......
  • web前端技能方法总结(css、js、jquery、html)(2)
    创建链接块display:block;列表样式在一个无序列表中,列表项的标志(marker)是出现在各列表项旁边的圆点。在有序列表中,标志可能是字母、数字或另外某种计数体系中的一个符号。要修改用于列表项的标志类型,可以使用属性list-style-type:ul{list-style-type:square;}1上面的声明把......
  • 前端几个常用的官网模版记录一下
    无意间发现的几个官网模版对于一些要求不高,不需要特定设计的官网,这几个模版套一套,改一改,轻松解决!......
  • 南京大学计算机拔尖班2023选拔考试乱写
    题目是从这里搬的第一题(20分)在黑板上写有2023个1,下面进行2022次如下操作:擦掉黑板上任意两个数\(a,b\)并写下\(a+b\)或者\(\min\{a^2,b^2\}\),最后只剩下一个数,记这个数字最大可能值为\(r\),求证\(\displaystyle2^{\frac{2023}3}<r<3^{\frac{2023}3}\)【Solution】......
  • 前端开发中如何高效渲染大数据量
    我们是袋鼠云数栈UED团队,致力于打造优秀的一站式数据中台产品。我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值。本文作者:琉易liuxianyu.cn  在日常工作中,较少的能遇到一次性往页面中插入大量数据的场景,数栈的离线开发(以下简称离线)产品中,就有类似的场景。......
  • 华为认证数通考试真题DAY4
    1、链路聚合是企业网络中的常用技术。下列描述中哪些是链路聚合技术的优点?(多选)A.提高可靠性 B.提高安全性 C.增加带宽 D.实现负载分担2、某交换机收到一个带有VLAN标签的数据帧,但发现在其MAC地址表中查询不到该数据帧的目的MAC地址,则交换机对该数据帧的处理行为是()。A.交换......
  • 前端三件套案例
    以下是一个使用前端三件套(HTML、CSS、JavaScript)编写的最简单的前端案例:<!DOCTYPEhtml><html><head><title>最简单的前端案例</title><metacharset="UTF-8"><style>/*CSS样式*/h1{color:red;......
  • Axios 的 put 请求实践:实现前端与后端的数据同步
    在前端开发中,我们经常需要与后端服务器进行数据交互。其中,PUT 请求是一种常用的方法,用于向服务器发送更新或修改数据的请求。通过发送PUT请求,我们可以更新服务器上的资源状态。Axios是一个流行的JavaScript库,用于在浏览器和 Node.js 中进行 HTTP 请求。它提供了简单易用......
  • 使用EasyExcel下载文件时,前端获取不到后端返回的文件名,无法下载到本地
    【问题描述】使用EasyExcel下载文件时,前端获取不到后端返回的文件名,无法下载到本地 【原因分析】实际上文件的流后端已经返回了,只是缺少了Content-Disposition属性返回,前端无法获取到文件名;privatestaticOutputStreamgetOutputStream(StringfileName,HttpServletRespon......
  • web前端技能方法总结(css、js、jquery、html)
    CSS设置背景(background)背景颜色background-color背景图片background-image背景重复background-repeat:repeat-x/repeat-y背景位置background-position:bottom/left/top/right/center背景关联background-attachment:fixed/scroll综合写法:.tagName{background:#ffffffurl(“a......