在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
})
}
模拟后端接口。
mockjs
安装依赖
# 使用mockjs产生随机数据
npm install mockjs --save-dev
# 使用json5解决json文件,无法添加注释问题
npm install json5 --save-dev
在vue项目中建一个文件夹mock存放mock代码
在mock文件夹下建一个testMock.js文件
const Mock = require('mockjs')
let id = Mock.mock('@id')
let obj = Mock.mock({
id: '@id'
})
console.log(obj)
使用Node来执行这个js文件,执行如下:
PS C:\source\demo\demo004\vue3-ts-demo> node .\mock\testMock.js
{ id: '130000201204237326' }
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地址
}
在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)
执行这个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()'
}
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: "李四"
})
main.ts
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
// 引入mock
require("./mock")
createApp(App).use(router).mount('#app')
测试接口
<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>
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>
请求数据 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
}
}
})
对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
接口函数请求封装
import service from '.'
// 定义一个接口,用来约束
interface loginData{
username: string,
password: string
}
export function login(data:loginData){
return service({
url: '/login',
method: 'post',
data
})
}
去官方网站看样式复制改吧改吧。

首页样式及侧边
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>
我点商品列表需要在右边展示,需要写整个HomeView的子路由。只有是子路由的时候能才在当前页面去渲染子页面。
在Views下建一个GoodsView.vue的组件。

router\index.ts


添加用户列表组件
UserView.vue
用户列表
添加用户列表子路由
{
path:"/user",
name:"user",
meta:{
isShow: true,
titile: '用户列表'
},
component: () => import(/* webpackChunkName: "user" */ '../views/UserView.vue')
}

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>
写路由,写组件。