当前位置: 首页 > news >正文

vue-ts-demo

 npm i -g @vue/cli

PS D:\kwai\vue3\project> vue create vue3-te-demo

element-plus

一个 Vue 3 UI 框架 | Element Plus

https://element-plus.org/zh-CN/guide/installation.html

安装:

npm install element-plus --save

完整引入使用:

使用:

按需引入使用

首先你需要安装unplugin-vue-components 和 unplugin-auto-import这两款插件

shell

npm install -D unplugin-vue-components unplugin-auto-import

修改vue.config.js中的内容为:

const { defineConfig } = require('@vue/cli-service')
const AutoImport = require('unplugin-auto-import/webpack')
const Components = require('unplugin-vue-components/webpack')
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')module.exports = defineConfig({transpileDependencies: true,configureWebpack:{plugins: [AutoImport({resolvers: [ElementPlusResolver()],}),Components({resolvers: [ElementPlusResolver()],}),],}
})

如果报错:RROR  TypeError: AutoImport is not a function TypeError,则需要降版本:

ERROR TypeError: AutoImport is not a function TypeError: AutoImport is not a-CSDN博客

使用:

重启项目

快速生成模版快捷键

登录页面

表单:

解决外边距塌陷:

css解决外边距塌陷的7种方法_css外边距塌陷-CSDN博客

 内容:

<template><div class="login-box"><el-formref="ruleFormRef":model="ruleForm"status-icon:rules="rules"label-width="80px"class="demo-ruleForm"><h2>后台管理系统</h2><el-form-item label="姓名" prop="name"><el-input v-model="ruleForm.name"  type="name" autocomplete="off" /></el-form-item><el-form-item label="密码" prop="pass"><el-input v-model="ruleForm.pass"  type="password" autocomplete="off" /></el-form-item><el-form-item><el-button  class="login-btn" type="primary" @click="submitForm(ruleFormRef)">登录</el-button><el-button class="login-btn" @click="resetForm(ruleFormRef)">重置</el-button></el-form-item></el-form></div>
</template><script setup lang="ts">
import { reactive, ref } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'const ruleFormRef = ref<FormInstance>()const rules = reactive<FormRules>({name: [{required: true,trigger: 'blur',},{min: 3,max: 5,message: 'Length should be 3 to 5',trigger: 'blur',},],pass: [{required: true,trigger: 'blur',},{min: 3,max: 5,message: 'Length should be 6 to 8',trigger: 'blur',},]
});const ruleForm = reactive({pass: '',name: '',
})const submitForm = (formEl: FormInstance | undefined) => {if (!formEl) returnformEl.validate((valid) => {if (valid) {console.log('submit!')} else {console.log('error submit!')return false}})
}const resetForm = (formEl: FormInstance | undefined) => {if (!formEl) returnformEl.resetFields()
}
</script><style scoped  lang="scss">.login-box{width: 100%;height: 100%;background:url("../assets/bg.png");padding: 1px;text-align: center;.demo-ruleForm{width: 500px;background-color: white;margin:100px auto;padding: 20px;border-radius: 20px;}.login-btn{width: 40%;}h2{margin-bottom: 16px;}}
</style>

使用ts对数据类型进行限制

新建一个文件夹type用于数据存放

export interface LoginForm{pass:string,name:string,
}export class LoginData{ruleForm:LoginForm={pass:"",name:""}
}

在之前对pass name的数据定义中,并没有规范类型

在定义类型之后:双向绑定

登录请求

axios中文网|axios API 中文文档 | axios

 npm install axios --save-dev  

新建 request/index.ts    使用index.ts的好处

https://juejin.cn/post/7221004205271646245

index.ts

import axios from "axios";const mockType = 'cbzMock'  // fastMock
const baseURL = mockType === 'cbzMock' ? 'https://mock.presstime.cn/mock/63569fbbbee0a00099ca48a1/api/vue-ts-mall-demo' : 'https://www.fastmock.site/mock/bf1fcb3c2e2945669c2c8d0ecb8009b8/api'
//创建axios实例
const service = axios.create({baseURL: 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(({ data }) => {const code : number = data.data.codeif(code != 200){return Promise.reject(data)}return data
},(err) => {console.log(err)
})export default service

api.ts

import service from "@/request/index";
import {LoginData} from "@/type/login";// 登录接口
export function login(data: LoginData) {return service({url: "/login",method: "POST",data})
}// 商品列表接口
export function getGoodsList(){return service({url: "/getGoodsList",method: "GET"})
}// 用户列表接口
export function getUserList(){return service({url: "/getUserList",method: "GET"})
}// 角色列表接口
export function getRoleList(){return service({url: "/getRoleList",method: "GET"})
}// 权限列表接口
export function getAuthorityList(){return service({url: "/getAuthorityList",method: "GET"})
}

登录操作

const submitForm = (formEl: FormInstance | undefined) => {if (!formEl) returnformEl.validate((valid) => {if (valid) {console.log('submit!')//假装成功!login(ruleForm).then( (res)=>console.log(res) ).catch( (err)=>{ console.log(err)//登录成功,切换到主页面router.replace({ name:'mainview'});});} else {console.log('error submit!')return false}})
}

主页面


 

布局容器

https://element-plus.org/zh-CN/component/container.html

header 

<template><div>main view</div><div class="common-layout"><el-container><el-header><el-row :gutter="20"><el-col :span="4">   <img src="../assets/logo.png" alt="" class="icon-logo">  <div class="grid-content ep-bg-purple" /></el-col><el-col :span="16"> <h2 class="title">后台管理系统</h2> <div class="grid-content ep-bg-purple" /></el-col><el-col :span="4"> <span class="quit">退出登录</span>  <div class="grid-content ep-bg-purple" /></el-col></el-row></el-header><el-container><el-aside width="200px">Aside</el-aside><el-main>Main</el-main></el-container></el-container></div></template><script setup lang="ts"></script><style scoped  lang="scss">
.el-header{height: 80px;background: #666;.icon-logo{height: 80px;}.title ,.quit{text-align: center;height: 80px;line-height: 80px;}}
</style>

 height 和line-height 的设置https://www.cnblogs.com/pwindy/p/13026176.html

实现上下居中的对齐,如果不设置line-height ,。则会居上

Aside

如何实现侧边栏一直到底部:

<template><div>main view</div><div class="common-layout"><el-container><el-header><el-row :gutter="20"><el-col :span="4">   <img src="../assets/logo.png" alt="" class="icon-logo">  <div class="grid-content ep-bg-purple" /></el-col><el-col :span="16"> <h2 class="title">后台管理系统</h2> <div class="grid-content ep-bg-purple" /></el-col><el-col :span="4"> <span class="quit">退出登录</span>  <div class="grid-content ep-bg-purple" /></el-col></el-row></el-header><el-container><el-aside width="200px"><el-col :span="120"><el-menuactive-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-col></el-aside><el-main>Main</el-main></el-container></el-container></div></template><script setup lang="ts">
import {Menu as IconMenu,Setting,
} from '@element-plus/icons-vue'
const handleOpen = (key: string, keyPath: string[]) => {console.log(key, keyPath)
}
const handleClose = (key: string, keyPath: string[]) => {console.log(key, keyPath)
}
</script><style scoped  lang="scss">
.el-header{height: 80px;background: #666;.icon-logo{height: 80px;}.title ,.quit{text-align: center;height: 80px;line-height: 80px;}
}.el-aside{.el-menu{height: calc(100vh - 80px);}}
</style>

可以通过计算:100vh即 100%的垂直视口 - header高度80px

.el-aside{.el-menu{height: calc(100vh - 80px);}}

动态路由

<template><div class="common-layout"><el-container><el-header><el-row :gutter="20"><el-col :span="4">   <img src="../assets/logo.png" alt="" class="icon-logo">  <div class="grid-content ep-bg-purple" /></el-col><el-col :span="16"> <h2 class="title">后台管理系统</h2> <div class="grid-content ep-bg-purple" /></el-col><el-col :span="4"> <span class="quit">退出登录</span>  <div class="grid-content ep-bg-purple" /></el-col></el-row></el-header><el-container><el-aside width="120px"><el-menuactive-text-color="#ffd04b"background-color="#545c64"class="el-menu-vertical-demo"default-active="2"text-color="#fff"router><!--router开启路由模式 可以通过标签的index 进行跳转--><el-menu-item :index="item.path"    v-for="item in subRouterList" :key="item.path"><span>{{item.meta.title}}</span></el-menu-item></el-menu></el-aside><!--设置路由出口--><el-main>  <router-view></router-view></el-main></el-container></el-container></div></template><script setup lang="ts">
import {
} from '@element-plus/icons-vue'import { useRouter } from 'vue-router';
const router = useRouter();
const subRouterList = router.getRoutes().filter( v=>
v.meta.isShow)
console.log(subRouterList)</script><style scoped  lang="scss">
.el-header{height: 80px;background: #666;.icon-logo{height: 80px;}.title ,.quit{text-align: center;height: 80px;line-height: 80px;}
}.el-aside{.el-menu{height: calc(100vh - 80px);}
}</style>

子路由设置:children

import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import GoodsView from '@/views/GoodsView.vue'
import LoginView from '../views/LoginView.vue'
import MainView from '../views/MainView.vue'
import UserView from '@/views/UserView.vue'
const routes: Array<RouteRecordRaw> = [{path: '/mainview',name: 'mainview',component: MainView,children:[{path:'goods',name:'goods',meta:{isShow:true,title:"商品列表"},component:GoodsView},{path:'user',name:'user',meta:{isShow:true,title:"用户列表"},component:UserView}]},{path: '/login',name: 'login',component:LoginView}
]const router = createRouter({history: createWebHistory(process.env.BASE_URL),routes
})export default router

商品页面

搜索

列表

分页展示

列表数据的获取:

数据定义:

export interface Goods{id:number,userId:number,title:string,introduce:string,
}interface SelectData{id:number,userId:number,title:string,introduce:string,curPage:number,count:number,pageSize:number,
}export class GoodsPages{// 被选择的数据, 查询时用selected_data:SelectData ={        userId: 0,id: 0,title: "",introduce: "",curPage: 1,count: 0,pageSize: 10};// 展示的商品数据goods_list: Goods[] = []
}

数据获取:

<script setup lang="ts">
import { computed, onMounted } from 'vue';
import { getGoodsList } from '@/request/api';
import {GoodsPages } from '@/type/goods';
import { reactive } from 'vue'//定义数据
const goods_data = reactive(new GoodsPages())//当前页数据
const curPagaData = reactive(  {comList:computed( ()=>{return goods_data.goods_list.slice((goods_data.selected_data.curPage-1)*10,goods_data.selected_data.curPage*10);})} )onMounted(  ()=>{console.log('onMounted');//请求数据getGoodsList().then( (res)=>{   goods_data.goods_list = res.data.data;console.log('goodsList',goods_data.goods_list)     }).catch( (err)=>{console.log(err) })}
)</script>

分页展示逻辑:

<div>curPagaData.comList 是列表当前页展示的数据<el-table :data=curPagaData.comList border style="width: 100%"><el-table-column prop="id" label="id" width="180" />   使用列表中的字段与  每一列标题 <el-table-column prop="title" label="title" width="180" /><el-table-column prop="introduce" label="introduce" /></el-table>分页展示<el-pagination layout="prev, pager, next" v-model:page-size=goods_data.selected_data.pageSize      页面的大小:设置为10v-model:current-page=goods_data.selected_data.curPage     当前是第几页:total=goods_data.goods_list.length />   总页数:是请求到的列表的总数</div>

查询逻辑

    <div class="select-box"><el-form :inline="true" :model="goods_data.selected_data" class="demo-form-inline"><el-form-item label="用户Title"><el-input v-model="goods_data.selected_data.title" placeholder="用户Title" clearable /></el-form-item>  <el-form-item><el-button type="primary" @click="onSubmit">查询</el-button></el-form-item></el-form></div>//查询数据
const onSubmit = () => {console.log('submit! suc',goods_data.selected_data.title)//如果是空的,则使用原来的数据if (goods_data.selected_data.title === ''){goods_data.goods_list = last_data;goods_data.selected_data.curPage=1;return;}//过滤数据let filterArry:Goods[]=[];//过滤数据filterArry = goods_data.goods_list.filter( (value)=>{return value.title.indexOf(goods_data.selected_data.title) !== -1} );goods_data.goods_list = filterArry;goods_data.selected_data.curPage=1;
}onMounted(  ()=>{getGoodsList().then( (res)=>{   goods_data.goods_list = res.data.data;//记录当前的数据,用于恢复last_data = goods_data.goods_list;console.log('goodsList',goods_data.goods_list)     }).catch( (err)=>{console.log(err) })}
)

用户列表

展示

自定义列模版:展示用户角色

 <template #default="scope"><el-text class="mx-1"   v-for="item in scope.row.role"  :key="item.role"> {{item.roleName +" "}}</el-text>         

选择器 

数据定义:

/*
获取用户列表接口 GET  /getUserList
请求报文:无
响应报文:
{"data": {"code": 200,"data": [{"id": 1,"nickName": "小明","userName": "小明","role": [{"role": 1,"roleName": "管理员"},{"role": 2,"roleName": "普通用户"}]},{"id": 2,"nickName": "红红","userName": "红红","role": [{"role": 1,"roleName": "管理员"}]},{"id": 3,"nickName": "绿绿","userName": "绿绿","role": [{"role": 2,"roleName": "普通用户"}]}]}
}获取角色列表接口 GET  /getRoleList
请求报文:无
响应报文:
{"data": {"code": 200,"data": [{"roleName": "管理员","roleId": 1,"authority": [1,2,4,5,6,7,8,9,11,13,14,15,16]},{"roleName": "普通用户","roleId": 2,"authority": [1,3,4,6,7,8,9,11,12,13]}]}
}*/interface RoloInfo{role:number,roleName:string,
}interface UserInfo{id:number,nickName:string,userName:stringrole:RoloInfo[],
}interface RoleType{roleName:string,roleId:number,authority:Array<number>[],
}interface QueryUser{nickName: string,  // 用户别名role: number  // 角色编号
}export class UserPage{roleList:RoleType[]=[];userList:UserInfo[]=[];selectedData:QueryUser={nickName:'',role:0}
}

vue:

<template><div class="select-box"><el-form :inline="true" :model="userPage.selectedData" class="demo-form-inline"><el-form-item label="用户Title"><el-input v-model="userPage.selectedData.nickName" placeholder="用户nickName" clearable /></el-form-item> <el-form-item label="用户roleid"><el-select v-model="userPage.selectedData.role" placeholder="全部" style="width: 240px"><el-option:key="0"label="全部":value="0"/><el-optionv-for="item in userPage.roleList":key="item.roleId":label="item.roleName":value="item.roleId"/></el-select></el-form-item>           <el-form-item><el-button type="primary" @click="onSubmit">查询</el-button></el-form-item></el-form></div><div><el-table :data=userPage.userList border style="width: 100%"><el-table-column prop="id" label="编号" width="180" /><el-table-column prop="nickName" label="用户昵称" width="180" /><el-table-column prop="role" label="用户角色" ><template #default="scope"><el-text class="mx-1"   v-for="item in scope.row.role"  :key="item.role"> {{item.roleName +" "}}</el-text>             </template></el-table-column></el-table></div>
</template><script setup lang="ts">
import { getRoleList, getUserList } from '@/request/api';
import { UserInfo, UserPage } from '@/type/userinfo';
import { onMounted, reactive } from 'vue';const userPage = reactive(new UserPage())
let lastUserList:UserInfo[]=[];onMounted(()=>{getUserList().then((res)=>{userPage.userList = res.data.data;lastUserList =userPage.userList;console.log("userList",userPage.userList);})getRoleList().then((res)=>{userPage.roleList =res.data.data;console.log("roleList",userPage.roleList);})
})const onSubmit=()=>{console.log('onsubmit',userPage.selectedData.role,userPage.selectedData.nickName);if (userPage.selectedData.role===0&& userPage.selectedData.nickName===''){userPage.userList = lastUserList;return;}let list:UserInfo[]= userPage.userList.filter((value)=>{let found = false;if (userPage.selectedData.role===0){return value.nickName.indexOf(userPage.selectedData.nickName)!=-1;}if (userPage.selectedData.nickName===''){for(let i=0;i<value.role.length;++i){if (value.role[i].role === userPage.selectedData.role){return true;}}}if (value.nickName.indexOf(userPage.selectedData.nickName)!=-1){for(let i=0;i<value.role.length;++i){if (value.role[i].role === userPage.selectedData.role){return true;}}}return found;});userPage.userList = list;
}</script><style scoped></style>

编辑

编辑按钮

           <el-table-column prop="role" label="操作" ><template #default="scope"><el-button type="primary"  @click="handleEditUser(scope.row)">编辑</el-button></template> </el-table-column>

弹出编辑框

<!-- 编辑用户的弹出窗--><el-dialog v-model="userPage.editShow" title="编辑用户信息"><el-form :model="userPage.editUser"><el-form-item label="用户昵称" label-width="120px"><el-input v-model="userPage.editUser.nickName" autocomplete="off" /></el-form-item><el-form-item label="用户角色" label-width="120px"><el-select multiple v-model="userPage.editUser.role" class="m-2" size="large" placeholder="请选择角色"><el-optionv-for="item in userPage.roleList":key="item.roleId":label="item.roleName":value="item.roleId"/></el-select></el-form-item></el-form><template #footer><span class="dialog-footer"><el-button @click="userPage.editShow = false">取消</el-button><el-button type="primary" @click="ensureEditUser">修改</el-button></span></template></el-dialog>

数据结构

// 用户编辑接口
export interface UserEdit {id: number,  // 用户idnickName: string,  // 用户昵称role: number[],   // 用户角色userName: string  // 用户名
}export class UserPage{roleList:RoleType[]=[];userList:UserInfo[]=[];selectedData:QueryUser={nickName:'',role:0}editShow = false  // 是否显示编辑用户弹出窗// 编辑用户时用到的对象editUser: UserEdit = {id: 0,nickName: "",role: [],userName: ""}
}

// 编辑用户弹窗
const handleEditUser = (row: UserInfo) => {userPage.editShow = trueuserPage.editUser = {id: row.id,nickName: row.nickName,role: row.role.map((value) => value.role),userName: ""}
}const ensureEditUser = () => {console.log(userPage.editUser)userPage.editShow = false;let obj= userPage.userList.find( (value)=>{ value.id === userPage.editUser.id})for(let i=0;i<userPage.userList.length;++i){if (userPage.editUser.id === userPage.userList[i].id){userPage.userList[i].nickName= userPage.editUser.nickName;userPage.userList[i].role =[];for(let item of userPage.roleList){if (userPage.editUser.role.find( (value)=>( value === item.roleId) )){userPage.userList[i].role.push({role:item.roleId,roleName:item.roleName})}}}}console.log(userPage.userList)  
}

查询

let lastUserList:UserInfo[]=[];
const onSubmit=()=>{console.log('onsubmit',userPage.selectedData.role,userPage.selectedData.nickName);if (userPage.selectedData.role===0&& userPage.selectedData.nickName===''){userPage.userList = lastUserList;return;}let list:UserInfo[]= userPage.userList.filter((value)=>{let found = false;if (userPage.selectedData.role===0){return value.nickName.indexOf(userPage.selectedData.nickName)!=-1;}if (userPage.selectedData.nickName===''){for(let i=0;i<value.role.length;++i){if (value.role[i].role === userPage.selectedData.role){return true;}}}if (value.nickName.indexOf(userPage.selectedData.nickName)!=-1){for(let i=0;i<value.role.length;++i){if (value.role[i].role === userPage.selectedData.role){return true;}}}return found;});userPage.userList = list;
}

代码

通过百度网盘分享的文件:src.zip
链接:https://pan.baidu.com/s/1p9YR2W0-DuIZs1_UJwDh6g?pwd=kdhs 
提取码:kdhs

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 二十种编程语言庆祝中秋节
  • 若依笔记(六):前后端token鉴权体系
  • 初始爬虫1(补充)
  • 【YashanDB认证】探索YashanDB的学习历程
  • QT学习与数据库连接
  • 鸿蒙Harmony应用开发,数据驾驶舱网络请求(Axios) 封装
  • 数据库事务的详解
  • Apache Cordova开发教程-入门基础
  • eth-trunk 笔记
  • 电源模块启动过冲测试项目该如何在ATECLOUD中搭建?
  • a√斗地主之顺子
  • 传输层协议——udp/tcp
  • STL,智能指针和线程安全,线程安全的单例模式和懒汉饿汉的实现,以及读者写者问题
  • 如何用 Helm Chart 安装指定版本的 GitLab Runner?
  • 安卓手机安装Termux,变成linux服务器
  • 【编码】-360实习笔试编程题(二)-2016.03.29
  • 【挥舞JS】JS实现继承,封装一个extends方法
  • 【刷算法】从上往下打印二叉树
  • Angular 响应式表单 基础例子
  • Apache Pulsar 2.1 重磅发布
  • canvas 五子棋游戏
  • CODING 缺陷管理功能正式开始公测
  • javascript 哈希表
  • JavaScript工作原理(五):深入了解WebSockets,HTTP/2和SSE,以及如何选择
  • javascript面向对象之创建对象
  • Java编程基础24——递归练习
  • js继承的实现方法
  • Making An Indicator With Pure CSS
  • nfs客户端进程变D,延伸linux的lock
  • Spring技术内幕笔记(2):Spring MVC 与 Web
  • Vue 重置组件到初始状态
  • Web Storage相关
  • 从setTimeout-setInterval看JS线程
  • 动态魔术使用DBMS_SQL
  • 后端_MYSQL
  • 回流、重绘及其优化
  • 基于遗传算法的优化问题求解
  • 使用SAX解析XML
  • 世界上最简单的无等待算法(getAndIncrement)
  • 2017年360最后一道编程题
  • #我与Java虚拟机的故事#连载14:挑战高薪面试必看
  • (06)Hive——正则表达式
  • (2)Java 简介
  • (js)循环条件满足时终止循环
  • (leetcode学习)236. 二叉树的最近公共祖先
  • (备忘)Java Map 遍历
  • (独孤九剑)--文件系统
  • (附源码)ssm高校志愿者服务系统 毕业设计 011648
  • (全注解开发)学习Spring-MVC的第三天
  • (三)Honghu Cloud云架构一定时调度平台
  • (一)基于IDEA的JAVA基础12
  • (转) 深度模型优化性能 调参
  • (转)visual stdio 书签功能介绍
  • 、写入Shellcode到注册表上线
  • ..回顾17,展望18