• 二、VUE3项目实战二



    一、登陆函数

    在src下的request目录下建一个api.ts

    import service from '.'
    
    // 定义一个接口,用来约束
    interface loginData{
        username: string,
        password: string
    }
    
    export function login(data:loginData){
        return service({
            url: '/login',
            method: 'post',
            data
        })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    二、mock如何使用?

    模拟后端接口。
    mockjs

    安装依赖

    # 使用mockjs产生随机数据
    npm install mockjs --save-dev
    # 使用json5解决json文件,无法添加注释问题
    npm install json5 --save-dev
    
    • 1
    • 2
    • 3
    • 4

    在vue项目中建一个文件夹mock存放mock代码
    在mock文件夹下建一个testMock.js文件

    const Mock = require('mockjs')
    let id = Mock.mock('@id')
    
    let obj = Mock.mock({
        id: '@id'
    })
    
    console.log(obj)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    使用Node来执行这个js文件,执行如下:

    PS C:\source\demo\demo004\vue3-ts-demo> node .\mock\testMock.js
    { id: '130000201204237326' }
    
    • 1
    • 2

    vscode安装json5插件。
    在这里插入图片描述

    在mock文件下建一个userinfo.json5的文件,用来存放json对象。

    {
        id: '@id', //得到随机的id,对象
        username: "@cname()", //随机生成中文名字
        date: '@date()', //随机生成日期
        avatar: "@image('200x200', 'red', '#fff', 'hello,mockjs')", //生成图片
        description: '@paragraph()', // 描述
        ip: '@ip()', //ip地址
        email: '@email()', //email地址
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在mock文件夹下建一个testJSON5.js文件,用来读取userinfo.json5文件

    const fs = require('fs')
    const path = require('path')
    const JSON5 = require('json5')
    
    var json = fs.readFileSync(path.join(__dirname, './userinfo.json5'), 'utf-8')
    
    var obj = JSON5.parse(json)
    console.log(obj)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    执行这个js文件

    PS C:\source\demo\demo004\vue3-ts-demo\mock> node testJSON5.js
    {
      id: '@id',
      username: '@cname()',
      date: '@date()',
      avatar: "@image('200x200', 'red', '#fff', 'hello,mockjs')",
      description: '@paragraph()',
      ip: '@ip()',
      email: '@email()'
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    三、vue3 + TS + mockjs

    mock\index.js

    var Mock = require("mockjs")
    
    Mock.mock("/api/users", "post", {
        "users|2-5":[
            {
                "id|+1" : 1,
                name: "@cname",
            }
        ]
    })
    
    Mock.mock("/haha", "get", {
        username: "张三",
        password: "李四"
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    main.ts

    import { createApp } from 'vue'
    import App from './App.vue'
    import router from './router'
    
    // 引入mock
    require("./mock")
    
    
    createApp(App).use(router).mount('#app')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    测试接口

    <template>
        <h2>hello,world!</h2>
    </template>
    
    <script setup lang="ts">
    import { onMounted } from 'vue';
    import axios from 'axios'
    
    onMounted(()=>{
        axios.get('/haha').then((res)=>{
          console.log(res.data)
        })
    })
    
    </script>
    
    <style scoped lang="scss">
    
    </style>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    四、登录逻辑实现

    LoginView.vue

    <template>
        <div class="login-box">
            <el-form
            ref="ruleFormRef"
            :model="ruleForm"
            status-icon
            :rules="rules"
            label-width="60px"
            class="demo-ruleForm"
        >
            <h2>后台管理系统</h2>
            <el-form-item label="帐号" prop="username">
            <el-input v-model="ruleForm.username" autocomplete="off" />
            </el-form-item>
            <el-form-item label="密码" prop="password">
            <el-input
                v-model="ruleForm.password"
                type="password"
                autocomplete="off"
            />
            </el-form-item>
            <el-form-item>
            <el-button class="loginbtn" type="primary" @click="submitForm(ruleFormRef)"
                >登陆</el-button
            >
            <el-button class="loginbtn" @click="resetForm(ruleFormRef)">重置</el-button>
            </el-form-item>
        </el-form>
        </div>
    </template>
    
    <script setup lang="ts">
    import { reactive, ref } from 'vue'
    import type { FormRules,FormInstance } from 'element-plus'
    // 将我们写的函数login引入进来
    import { login } from '../request/api'
    import { useRouter } from 'vue-router'
    //import  axios from 'axios'
    const ruleFormRef = ref<FormInstance>()
    const router = useRouter() // 相当于$router,全局路由
    const ruleForm = reactive({
      username: '',
      password: '',
    })
    const rules = reactive<FormRules>({
      username: [
        { required: true, message: '请输入帐号!', trigger: 'blur' },
        { min: 3, max: 10, message: '帐号在3到10之间', trigger: 'blur' },
      ],
      password: [
        { required: true, message: '请输入密码!', trigger: 'blur' },
        { min: 3, max: 10, message: '密码在3到10之间', trigger: 'blur' },
      ],
    })
    
    
    
    const submitForm = async (formEl:FormInstance | undefined) => {
      if (!formEl) return
      await formEl.validate((valid, fields) => {
        // 对表单内容进行验证
        // valid布尔类型,为true表示验证成功,反之
        if (valid) {
            login(ruleForm).then((res)=>{
                //将token进行保存
                localStorage.setItem('token', res.data.token);
                //跳转页面,首页
                router.push('/')
            })
        } else {
          console.log('error submit!', fields)
        }
      })
    }
    
    const resetForm = (formEl: FormInstance | undefined) => {
      if (!formEl) return
      formEl.resetFields()
    }
    
    
    </script>
    
    <style lang="scss" scoped>
    .login-box{
        width: 100%;
        height: 100%;
        background: url('../assets/img0.jpg');
        display: flex;
        flex-flow:row wrap;
        justify-content: center;
        align-items: center;
        text-align: center;
        .demo-ruleForm{
            width: 500px;
            background-color: #fff;
            padding: 40px;
            border-radius: 30px;
        }
        .loginbtn{
            width: 48%;
        }
        h2{
            margin-bottom: 20px;
        }
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107

    请求数据 index.js

    var Mock = require("mockjs")
    
    Mock.mock("/api/users", "post", {
        "users|2-5":[
            {
                "id|+1" : 1,
                name: "@cname",
            }
        ]
    })
    
    Mock.mock("/login", "post", req => {
        // 将传递进来的数据保存
        const { username, password } = JSON.parse(req.body)
        if(username==='admin' && password==='1234'){
            return {
                code: 200,
                data: {
                    msg: '登陆成功',
                    success: true,
                    token: "admin_token",
                    username: 'admin'
                }
            }
        }
        return {
            code: 201,
            data: {
                msg: '登陆失败',
                success: false
            }
        }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    对axios的封装

    import axios from 'axios'
    
    // 创建axios实例
    const service = axios.create({
        baseURL: "",
        timeout: 5000,
        headers:{
            'Content-Type':'application/json;charset=utf-8'
        }
    })
    
    // 请求拦截
    service.interceptors.request.use((config)=>{
        config.headers = config.headers || {}
        if(localStorage.getItem('token')){
            config.headers.token=localStorage.getItem('token')||""
        }
        return config
    })
    
    // 响应拦截
    service.interceptors.response.use((res)=>{
        console.log(res)
        const code:number=res.data?.code
        if(code == 200){
            return res.data
        }
        return Promise.reject(res)
    },(err)=>{
        return Promise.reject(err)
    })
    
    export default service
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    接口函数请求封装

    import service from '.'
    
    // 定义一个接口,用来约束
    interface loginData{
        username: string,
        password: string
    }
    
    export function login(data:loginData){
        return service({
            url: '/login',
            method: 'post',
            data
        })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    五、使用element plus写首页头部及侧边栏

    去官方网站看样式复制改吧改吧。
    在这里插入图片描述
    首页样式及侧边
    HomeView.vue

    <template>
      <div class="home">
        <el-container>
          <el-header>
            <el-row :gutter="20">
              <el-col :span="4"><div class="grid-content ep-bg-purple" /> <img class="logo" src="../assets/logo.png"/> </el-col>
              <el-col :span="16"><div class="grid-content ep-bg-purple" /> <h2>后台管理系统</h2></el-col>
              <el-col :span="4"><div class="grid-content ep-bg-purple" /><span class="quit-login">退出登录</span></el-col>
            </el-row>
          </el-header>
          <el-container>
            <el-aside width="200px">
              <el-menu
            active-text-color="#ffd04b"
            background-color="#545c64"
            class="el-menu-vertical-demo"
            default-active="2"
            text-color="#fff"
          >
            <el-menu-item index="2">
              <el-icon><icon-menu /></el-icon>
              <span>商品列表</span>
            </el-menu-item>
          </el-menu>
            </el-aside>
            <el-main>Main</el-main>
          </el-container>
        </el-container>
      </div>
    </template>
    
    <script lang="ts">
    import { defineComponent } from 'vue';
    
    export default defineComponent({
      name: 'HomeView',
      components: {
      },
    });
    </script>
    
    <style scoped lang="scss">
    .el-header{
      height: 80px;
      background-color: #666;
      
      .logo{
        height: 80px;
      }
      h2, .quit-login{
        text-align: center;
        line-height: 80px;
        color: #fff;
      }
    }
    
    .el-aside{
      .el-menu{
        height: calc(100vh - 80px);
      }
    }
    </style>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    六、首页侧边的动态路由

    我点商品列表需要在右边展示,需要写整个HomeView的子路由。只有是子路由的时候能才在当前页面去渲染子页面。
    在Views下建一个GoodsView.vue的组件。
    在这里插入图片描述
    router\index.ts
    在这里插入图片描述
    在这里插入图片描述
    添加用户列表组件
    UserView.vue

    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    添加用户列表子路由

    {
            path:"/user",
            name:"user",
            meta:{
              isShow: true,
              titile: '用户列表'
            },
            component: () => import(/* webpackChunkName: "user" */ '../views/UserView.vue')
          }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述
    HomeView.vue

    <template>
      <div class="home">
        <el-container>
          <el-header>
            <el-row :gutter="20">
              <el-col :span="4"><div class="grid-content ep-bg-purple" /> <img class="logo" src="../assets/logo.png"/> </el-col>
              <el-col :span="16"><div class="grid-content ep-bg-purple" /> <h2>后台管理系统</h2></el-col>
              <el-col :span="4"><div class="grid-content ep-bg-purple" /><span class="quit-login">退出登录</span></el-col>
            </el-row>
          </el-header>
          <el-container>
            <el-aside width="200px">
              <el-menu
            active-text-color="#ffd04b"
            background-color="#545c64"
            class="el-menu-vertical-demo"
            default-active="2"
            text-color="#fff"
            router
          >
            <!-- router开启路由模式,通过el-menu-item index来进行跳转 -->
            <el-menu-item :index="item.path" v-for="item in list" :key="item.path">
              <span>{{item.meta.title}}</span>
            </el-menu-item>
          </el-menu>
            </el-aside>
            <el-main>Main</el-main>
          </el-container>
        </el-container>
      </div>
    </template>
    
    <script lang="ts">
    import { defineComponent } from 'vue';
    import { useRouter } from 'vue-router'
    export default defineComponent({
      setup(){
        const router = useRouter()
        console.log(router.getRoutes())
        const list = router.getRoutes().filter(r=>r.meta.isShow)
        console.log(list)
        return {list}
      }
    })
    </script>
    
    <style scoped lang="scss">
    .el-header{
      height: 80px;
      background-color: #666;
      
      .logo{
        height: 80px;
      }
      h2, .quit-login{
        text-align: center;
        line-height: 80px;
        color: #fff;
      }
    }
    
    .el-aside{
      .el-menu{
        height: calc(100vh - 80px);
      }
    }
    </style>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    写路由,写组件。

  • 相关阅读:
    Spring框架(一)Spring核心,设计理念,创建,优缺点,使用场景···
    Java 网络编程之TCP(五):分析服务端注册OP_WRITE写数据的各种场景(三)
    Linux文件查找、别名、用户组
    node的api使用——URL——get方法——网页爬虫——node事件——path路径——判断文件类型——fs四种异步封装——客户端文件验证发送
    第27期 | GPTSecurity周报
    【MyBatis】通过分页助手完成分页查询
    C语言学习系列-->字符函数和字符串函数
    Qt Installation命名问题导致vs编译Qt项目出错
    python无法import相对路径中的包?一次说清楚python的import机制~
    iOS Crash 治理:淘宝VisionKitCore 问题修复
  • 原文地址:https://blog.csdn.net/weixin_42710036/article/details/127525481