1231 字
6 分钟
AI答题平台前端笔记
2025-01-03

言辞AI答题平台前端笔记#

技术选型#

  1. vue-cli
  2. element plus
  3. eslint+prettier
  4. TS

Part1#

  • 项目初始化,引入element plus组件库,开启eslint+prettier语法检查。

  • 确定布局,内容,功能。写页面时先写内容,再写功能,最后补全布局css。

  • 抽出路由为一个新文件,方便管理。

  • 定义基本布局,用户布局等多套布局。

  • 在APP.vue设定对于切换布局的判断

    • <div id="app">
        <template v-if="route.path.startsWith('/user')">
          <router-view />
        </template>
        <template v-else>
          <BasicLayout />
        </template>
      </div>
      
  • 在routes路由文件内采用子路由的形式,对于含有特定地址的路由进行布局的切换。

  • 通过meta里的字段判断路由在全局导航栏上的显隐。

Part2#

  • 安装axios请求库,安装openAPI插件,根据后端接口自动生成前端代码(详见毕业设计项目总结第一部分

    • yarn add @umijs/openapi
      
    • 在根目录新建相关配置,文档参考:@umijs/openapi

    • package.jsonscript 中添加 api: "openapi2ts": "openapi2ts",

  • 全局状态管理:官方文档

    • 示例userStore

    • import { defineStore } from "pinia";
      import { ref } from "vue";
      
      export const useLoginUserStore = defineStore("loginUser", () => {
        const loginUser = ref<API.User>({
          username: "未登录",
        });
      
        function setLoginUser(user: API.User) {
          loginUser.value = user;
        }
      
        return { loginUser, setLoginUser };
      });
      

Part3#

  • 全局权限管理

    • 设置access目录,管理登录用户的权限

      • image-20250219213514058

      • image-20250219213537175

    • 更新用户状态userStore.ts,新增fetchLoginUser函数,拿到当前登录用户的数据,没有的话,给用户权限默认值为未登录

      • image-20250219213632592
    • 对于顶部导航菜单,新增根据权限判断菜单子项的显隐

      • image-20250219213853866

Part4#

  • 在登录后页面依然无权限

    • 原因:页面路由已经计算好了,哪怕登录用户发生了变化,也不会重新渲染页面。
    • 解决方法:将全局导航栏的展示在页面的菜单变量改为计算属性,用computed封装一下。
  • 如下函数无法获取res的内容

    • async function fetchLoginUser() {
        const res = await getLoginUserUsingGet();
        if (res.data.code === 0 && res.data.data) {
          console.log(res);
          loginUser.value = res.data.data;
        } else {
          console.log(res);
          setTimeout(() => {
            loginUser.value = {
              id: 1,
              userName: "登66录",
              userRole: accessEnum.ADMIN,
            };
          }, 3000);
          loginUser.value = { userRole: accessEnum.NOT_LOGIN };
        }
      }
      
    • 原因:自己配置的axios响应拦截器返回的值直接取的data的内容,导致该处if条件判断的时候无法找到对应的字段。

    • 解决方法:修改axios全局响应拦截器的返回值,直接返回response。

  • 整合bytemd插件,实现markdown编辑器。

  • 新增登录页与注册页

    • 效果图:
      • image-20250220183215471
      • image-20250220183225429
  • 新增管理布局,用户管理页

    • 注:分页组件传值为number类型,后端传值类型需一致,否则用Number()强转。
    • image-20250223201304425
  • 继续新增应用管理,题目管理等页面

    • image-20250225130851258
    • image-20250225130910325

part5#

  • 新增主页,新建卡片组件,在主页通过v-for遍历使用。

    • image-20250225173150509
  • 新增应用详情页,根据每个应用不同的id展示的内容不同

    • image-20250225173233669
  • 添加按钮,新增创建题目页,更新题目页(这两个页面复用同一个页面布局)

    • image-20250225185347354
    • image-20250225185332447

Part6#

  • 创建题目页面的嵌套表单

    • 添加选项与删除选项的函数:

      • /**
         * 删除题目
         * @param index
         */
        const removeQuestion = (index: number) => {
          questionContent.splice(index, 1);
        };
        
        /**
         * 添加题目
         * @param index
         */
        const addQuestion = (index: number) => {
          questionContent.splice(index, 0, {
            title: "",
            options: [],
          });
        };
        
    • 实现题目的嵌套循环

      • <div v-for="(question, index) in questionContent" :key="index">
          <el-space size="large">
            <h3>题目{{ index + 1 }}</h3>
            <el-button size="small" @click="addQuestion(index + 1)">
              添加题目
            </el-button>
            <el-button
              size="small"
              type="danger"
              @click="removeQuestion(index)"
            >
              删除题目
            </el-button>
          </el-space>
          <el-form-item :label="`题目${index + 1}标题`">
            <el-input v-model="question.title" placeholder="请输入标题" />
          </el-form-item>
        
    • 添加题目选项与删除题目选项的函数

      • /**
         * 删除题目选项
         * @param item
         */
        const removeQuestionOption = (
          question: API.QuestionContentDTO,
          index: number
        ) => {
          if (!question.options) {
            question.options = [];
          }
          question.options.splice(index, 1);
        };
        
        /**
         * 添加题目选项
         * @param index
         */
        const addQuestionOption = (question: API.QuestionContentDTO, index: number) => {
          if (!question.options) {
            question.options = [];
          }
          question.options.splice(index, 0, {
            key: "",
            value: "",
          });
        };
        
  • 创建设置评分页面,将评分管理页面修改后作为组件嵌入其中。

    • image-20250226164542196

    • 子组件调用父组件方法

      • 子组件在props中注册父组件函数

      • interface Props {
          appId: number;
          doUpdate: (scoringResult: API.ScoringResultVO) => void;
        }
        
        const props = withDefaults(defineProps<Props>(), {
          appId: () => {
            return 0;
          },
        });
        
      • 通过按钮点击事件触发

      • <el-button size="small" type="success" @click="doUpdate?.(scope.row)">
          修改
        </el-button>
        
      • 父组件在子组件中把函数传递过去

      • <ScoringResultTable :appId="appId" :doUpdate="doUpdate" ref="tableRef" />
        
    • 父组件调用子组件方法

      • 子组件通过defineExpose将方法暴露给父组件

      • /**
         * 暴露给父组件
         */
        defineExpose({
          loadData,
        });
        
      • 父组件在子组件通过ref将函数引入自己定义的ref响应式变量中,最后通过.value访问。

      • <ScoringResultTable :appId="appId" :doUpdate="doUpdate" ref="tableRef" />
        
      • const tableRef = ref();
        
  • 答题模块三个页面

    • 答题页面

      • //当前题目的序号
        const current = ref(1);
        //当前题目
        const currentQuestion = ref<API.QuestionContentDTO>({});
        //当前题目选项
        const questionOptions = computed(() => {
          return currentQuestion.value.options
            ? currentQuestion.value.options.map((option) => {
                return {
                  label: `${option.key}.${option.value}`,
                  value: option.key,
                };
              })
            : [];
        });
        
    • 答题结果页面

      • image-20250227115207518
    • 我的答题页面

      • image-20250227115218930
AI答题平台前端笔记
https://lym0518.cn/posts/aiansweringplatform/
作者
TeFantasy
发布于
2025-01-03
许可协议
CC BY-NC-SA 4.0