Vue框架


Vue3组件库 ---- vue-cli、axios拦截器、proxy跨域代理


前面已经分享了路由的基础部分,路由就是组件和hash地址之间的对应关系,在SPA程序中,可以使用嵌套路由实现组件的嵌套,并且通过编程式导航实现组件的切换,router重要的就是router-link和router-view两个标签,和router.js中配置路由规则

接下来就是介绍vue-cli和proxy等,这部分结束之后,vue3的大部分就介绍完了,零碎的只是等写项目的时候补充;至于vue框架的深入 ---- 😀 暂时没有这个打算,因为深入的是SSM和Boot和SQL;前端只要会用即可

vue-cli

vue-cli就是vue脚手架,是vue官方提供的,快速生成vue工程化项目的工具,直接使用,基于webpack,功能丰富并且易于扩展,支持创建vue2和vue3的项目 ----- 这样就不需要像之前分析webpack那样从一个空白项目,配置config,配置locator等一系列操作

之前分享ES6的时候就已经安装了这个脚手架了,npm i -g @cli/vue

PS C:\Windows\system32> vue -V
@vue/cli 4.5.15

运行-V命令显示就说明安装成功

增加powerShell执行权限,运行vue脚本
因为普通窗口没有执行权限,所以不能执行vue命令和npm命令;这个时候首先

  • 以管理员身份运行PowerShell
  • 执行 set-ExecutionPolicy RemoteSigned 同时键入Y
PS C:\Windows\system32>  set-ExecutionPolicy RemoteSigned

执行策略更改
执行策略可帮助你防止执行不信任的脚本。更改执行策略可能会产生安全风险,如 https:/go.microsoft.com/fwlink/?LinkID=135170
中的 about_Execution_Policies 帮助主题所述。是否要更改执行策略?
[Y] 是(Y)  [A] 全是(A)  [N] 否(N)  [L] 全否(L)  [S] 暂停(S)  [?] 帮助 (默认值为“N”): Y

HbuilderX的终端增加管理员权限

这个很easy,直接点击属性,选择以管理员身份运行程序即可

创建项目

之前基于Vite创建spa的时候,运行的是npm init vite-app XXX; vue-cli提供了创建项目的两种方式:

  • 基于命令行创建 vue create XXX 就可以创建【标准脚手架,命令头都是vue,之前的VIte是npm】

在终端上运行命令的时候,是交互式命令,需要对一些选项进行选择;

vue create vuecli-demo

Vue CLI v4.5.15
┌──────────────────────────────────────────┐
│                                          │
│   New version available 4.5.15 → 5.0.3   │
│     Run npm i -g @vue/cli to update!     │
│                                          │
└──────────────────────────────────────────┘

? Please pick a preset: (Use arrow keys)    请选择预设,使用键值
> Default ([Vue 2] babel, eslint)  【默认预设:创建vue2项目】
  Default (Vue 3) ([Vue 3] babel, eslint)  【默认预设:创建vue3项目】
  Manually select features  手动选择功能【预设

这里就选择手动选择功能【其他随意】
------------------------------------------------------------
? Please pick a preset: Manually select features
? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to invert selection)     空格为选择,a为全选,i为反选,取消选择
>(*) Choose Vue version
 (*) Babel
 ( ) TypeScript
 ( ) Progressive Web App (PWA) Support
 ( ) Router
 ( ) Vuex
 ( ) CSS Pre-processors
 (*) Linter / Formatter
 ( ) Unit Testing
 ( ) E2E Testing
 
 这里选择version/babel/Css即可
 ---------------------------------------
 接下里选择版本3.x,less
 --------------------------------------
 接下来选择存储插件的配置信息
 ? Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys)
> In dedicated config files  将babel等插件存储在单独的文件中
  In package.json  放在包管理文件中 
这里建议分开,package就只放依赖包
----------------------------------------

 $ cd vuecli-demo
 $ npm run serve
  • 基于可视化界面创建 vue ui ----- 这个在之前就演示过了,不再赘述;提醒一下【1.预设中选择手动配置项目; 2.在功能页面勾选安装的功能(choose Vue Version 、 Babel、 CSS预设器,使用配置文件】

其实vue ui 本质上就是通过可视化的面板采集用户的配置信息,在后台基于命令行的方式自动初始化项目

运行依赖和开发依赖

运行依赖就是运行的生产环境时依赖的包,简单说就是项目发布后需要依赖的包: 安装的时候是npm i XX -S 显然,vue-router和axios就是运行的依赖

开发依赖是开发的时候需要使用包,一般上线之后并不需要,安装的时候npm i XX -D 之前安装的less等都是开发依赖

这个概念和后端的Maven的类似,比如provied就是生产时不需要的,比如servlet和jsp包

梳理vue3项目的目录

请添加图片描述

这里简单截图,可以看到和之前的SPA项目还是有所不同,这里的index.html是放在public文件夹下面,不是SPA; main.js是项目的入口文件,.gitgnore是Git的配置文件,babel…是babel的配置文件,package.json包管理文件 ; APP.vue是根组件,这个和之前是相同的

vue2中使用路由模块

  • 在src目录下创建router-> index.js路由模块
//导入vueRouter,导入需要创建的路由的组件
import vue from 'vue'
import VueRouter from 'vue-router'

vue.use(VueRouter)   // 嗲用vue.use(),将Router全局挂载,vue2中不需要再main.js中配置

const router = new VueRouter({   //vue2中都喜欢使用new的方式,3中是按需导入CreateRouter方法
    routes:[
        .....
    ]
})

export default router

在vue2项目中,在main.js中导入index.js,并且将router对象挂载到vue对象中

import Vue from 'vue'
import App from './App.vue'
//导入路由模块
import router from './router'

Vue.config.productionTip = false

const vue2_app = new Vue({
    render: h => h(app),
    //挂载路由模块
    //router: router
    router,
})

vue2_app.mout('#app')

其他地方和vue3都是相同的,就不再赘述

vue2中使用axios

vue2中导入anxios相同,唯一不同的就是挂载不是vue3的globalProperties; 而是Vue.prototype.$ajax = axios Vue2中使用的都是Vue的方式,把每一个组件都看作的Vue的原型链

组件库

组件库和java的类库和其他的jackson等包都是开发者将自己封装的代码发布;在实际开发中,前端开发者将自己封装的.vue组件整理、打包、发布为npm的包,其他人就可以下载和使用,

现成的组件,就是vue组件库

vue组件库和bootstrap的区别

bootstrap只是提供了纯粹的原材料(css样式,HTML结构和JS特性),需要programmer进一步的组装和改造;

vue组件库遵循了Vue的语法,高度定制的现成组件,即拿即用【用现成的…】

Element UI

一个 Vue 3 UI 框架 | Element Plus (gitee.io)

Element UI是XXX开源的一套PC端vue组件库,支持在vue2和vue3项目中使用:vue2使用Element即可,vue3使用Element Plus

  • 在项目中安装element-plus ui
npm i element-plus -S

 "element-plus": "^2.1.4",
  • 引入element-plus

programmer可以一次性完整引入所有的element-ui组件,或者按需引入需要用到的element-ui组件, 完整引入虽然操作简单,但是引入不需要的组件,不建议

//在main.js中引入element-plus

import ElementPlus from 'element-plus'

//导入element ui的组件的样式
import 'element-plus/dist/index.css'

//把ElementUI组测为vue的插件,就可以在项目中每一个组件中使用Element组件
app.use(ElementPlus)   //这里和之前的router类似

这样就可以在每一个组件中使用,使用方式就是和之前的bootstrap类似,直接去复制DOM结构到需要用到的地方即可

<template>
  <el-form :model="form" label-width="120px">
    <el-form-item label="Activity name">
      <el-input v-model="form.name" />
    </el-form-item>
    <el-form-item label="Activity zone">
      <el-select v-model="form.region" placeholder="please select your zone">
        <el-option label="Zone one" value="shanghai" />
        <el-option label="Zone two" value="beijing" />
      </el-select>
    </el-form-item>
    <el-form-item label="Activity time">
      <el-col :span="11">
        <el-date-picker
          v-model="form.date1"
          type="date"
          placeholder="Pick a date"
          style="width: 100%"
        />
      </el-col>
      <el-col :span="2" class="text-center">
        <span class="text-gray-500">-</span>
      </el-col>
      <el-col :span="11">
        <el-time-picker
          v-model="form.date2"
          placeholder="Pick a time"
          style="width: 100%"
        />
      </el-col>
    </el-form-item>
    <el-form-item label="Instant delivery">
      <el-switch v-model="form.delivery" />
    </el-form-item>
    <el-form-item label="Activity type">
      <el-checkbox-group v-model="form.type">
        <el-checkbox label="Online activities" name="type" />
        <el-checkbox label="Promotion activities" name="type" />
        <el-checkbox label="Offline activities" name="type" />
        <el-checkbox label="Simple brand exposure" name="type" />
      </el-checkbox-group>
    </el-form-item>
    <el-form-item label="Resources">
      <el-radio-group v-model="form.resource">
        <el-radio label="Sponsor" />
        <el-radio label="Venue" />
      </el-radio-group>
    </el-form-item>
    <el-form-item label="Activity form">
      <el-input v-model="form.desc" type="textarea" />
    </el-form-item>
    <el-form-item>
      <el-button type="primary" @click="onSubmit">Create</el-button>
      <el-button>Cancel</el-button>
    </el-form-item>
  </el-form>
</template>

<script lang="ts" setup>
import { reactive } from 'vue'

// do not use same name with ref
const form = reactive({
  name: '',
  region: '',
  date1: '',
  date2: '',
  delivery: false,
  type: [],
  resource: '',
  desc: '',
})

const onSubmit = () => {
  console.log('submit!')
}
</script>

----------这里就复制了一个表单的template的部分----------
样式都在index.css中

按需引入操作复杂,但是只是引入需要用到的组件,优化项目体积

按需引入

按需引入需要借助unplugin-vue-component unplugin-auto-import插件来实现

  • 运行npm install -D unplugin-vue-components unplugin-auto-import安装这个插件
  • 修改项目根目录的webpack.conofig.js配置文件【没有的可以看下面】
const AutoImport = require('unplugin-auto-import/webpack')
const Components = require('unplugin-vue-components/webpack')
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')

module.exports = {
  // ...
  plugins: [
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
  ],
}

vue-cli打包的项目,创建vue.config.js,进行配置

const AutoImport = require('unplugin-auto-import/webpack')
const Components = require('unplugin-vue-components/webpack')
const {ElementPlusResolver} = require('unplugin-vue-components/resolvers')

module.exports ={
    configureWebpack: {
        plugins: [
            AutoImport({
                resolvers: [ElementPlusResolver()],
            }),
            Components({
                resolvers: [ElementPlusResolver()],
            }),
        ]
    }
}

可以看到和上面的区别就是,要加上configureWebpack属性,这里博主踩坑了很久😢;

这样就可以按需自动导入了;不需要再去main.js中配置,就使用什么DOM,就会自动导入组件,不需要任何操作;【我们使用element的标签时,无需再使用import对组件进行导入】

比如使用

<el-button>test</el-button>

就会自动导入Button组件,不需要再import;但是需要在main.js中引入EL的样式

import 'element-plus/dist/index.css'

拦截器

拦截器interceptors会在每一次发起ajax请求和得到响应的时候自动触发

请添加图片描述

拦截器就是分为请求拦截器和响应拦截器;拦截器主要应用为: Token身份认证,Loading效果,

请求拦截器

通过axios.interceptors.request.use(成功的cb,失败的cb) 配置请求拦截器

axios.interceptors.request.use(function(config){
	console.log('拦截')
	//成功的回调
	return config                              //这里必须return这个config对象,不然会报错,请求可能异常
},function(error){
	//失败的回调,可以省略
	// return Promise.reject(error)
})

Token认证

可以使用请求拦截器进行token认证

axios.interceptors.request.use(config => {
	//为当前请求配置Token
	config.headers.Authorization = 'Beaere xxx'
	return config  //这是固定写法,一定要return
})

config中包含本次请求得所有的信息,比如请求头header等

如果打印config,可以发现

{transitional: {}, transformRequest: Array(1), transformResponse: Array(1), timeout: 0, adapter: ƒ,}adapter: ƒ xhrAdapter(config)baseURL: "https://www.escook.cn/api"data: undefinedheaders: Accept: "application/json, text/plain, */*"Authorization: "Barer xxx"[[Prototype]]: ObjectmaxBodyLength: -1maxContentLength: -1method: "get"timeout: 0transformRequest: [ƒ]transformResponse: [ƒ]transitional: {silentJSONParsing: true, forcedJSONParsing: true, clarifyTimeoutError: false}url: "/cart"validateStatus: ƒ validateStatus(status)xsrfCookieName: "XSRF-TOKEN"xsrfHeaderName: "X-XSRF-TOKEN"[[Prototype]]: Object
App.vue?3dfd:18 {status: 200, message: '获取购物车列表数据成功!', list: Array(10)}

{transitional: {}, transformRequest: Array(1), transformResponse: Array(1), timeout: 0, adapter: ƒ,}
adapter: ƒ xhrAdapter(config)
baseURL: "https://escook.cn/api"
data: undefined
headers:
Accept: "application/json, text/plain, */*"
Authorization: "Beaere xxx"

这里说明拦截器拦截成功,将请求的headers中的Authorization成功修改;这里一定要将请求的信息对象config return,不然请求失败

请求拦截器 – Loading效果

借助于element-plus的Loading效果组件,就可以实现Loading的效果展示

直接导入ElLoading,通过service方法创建实例,就可以显示Loading效果; 但是只是配置请求拦截器是不行的,不然Loading效果一直显示,需要配置响应拦截器来关闭Loading效果

import { createApp } from 'vue'
import App from './App.vue'
import  axios from "axios"

//完整引入Element
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'

import { ElLoading } from 'element-plus'
const app = createApp(App)

app.use(ElementPlus)

axios.defaults.baseURL = 'https://www.escook.cn/api'

// axios.interceptors.request.use(config => {
// 	config.headers.Authorization = 'Barer xxx'
// 	console.log(config)
// 	return config
// })

//声明变量表示Loading组件的实例对象
let loadingInstance = null

axios.interceptors.request.use(req => {
	//使用Loading组件的service()方法,创建Loading的实例,全屏展示Loading效果
	loadingInstance = ElLoading.service({fullscreen: true})
	console.log(req)
	return req
})

axios.interceptors.response.use(resp => {
	//使用实例对象关闭效果
	console.log(resp)
	loadingInstance.close()
	return resp
})

app.config.globalProperties.$ajax = axios

app.mount('#app')

这样就成功进行了Loading效果的展示

Proxy跨域代理

假设vue项目地址: http://localhost:8080/; Api接口的地址https://www.escook.cn/api/users;由于当前API没有开启CORS跨域资源共享,所以默认情况下,上面的接口不能请求成功

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GhDLNXkh-1647960595405)(C:\Users\OMEY-PC\Desktop\proxy.png)]

像这样不能进行资源共享,如果开发过程中,后端开发人员没有开启CORS,那么前端岂不是凉凉?,不是的,可以通过代理解决接口的跨域问题:

通过vue-cli创建的项目,都可以使用代理proxy
请添加图片描述

实现的步骤:

  • 将axios的请求根路径设置为vue项目的运行地址【接口请求不再跨域】
  • vue项目发现请求的接口不存在,将请求交给proxy代理
  • 代理把请求的根路径变为devServer.proxy的属性的只,发起真正的数据请求【相当于联网访问】
  • 代理将请求到的数据,转发给axios

比如,项目所在的直接发起请求

const {data:res} =  await this.$ajax.get('/api/cart')

但是当前的位置不存在api接口,那么就会请求失败,存在跨域问题: GET http://localhost:8081/api/cart 404 (Not Found)

配置proxy代理

  • 在项目根目录下创建vue.config.js的配置文件,声明配置
module.exports = {
	devServer: {
		//当前项目会将开发阶段的请求代理到https://www.escook.cn
		proxy: 'https://www.escook.cn',
	}
}

⚠: devServer.proxy的代理功能,只是在开发调试阶段生效;项目上线发布,仍然需要使用API接口的服务器开启CORS跨域资源共享 ----- 也就是后端设置:允许所有域名的请求访问

在SpringBoot进行配置【后面就会上Boot】

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")  //设置所有的请求可以进行跨域
                .allowedOrigins("http://localhost:8080")  //允许跨域的ip
                .allowedMethods("*")  //请求的方法 可以不设置 有默认的
                .allowedHeaders("*"); //请求头 可以不设置 有默认的
    }
}

Proxy代理和baseURL结合问题

之前博主一直按照标准配置proxy代理,可是一直都是跨域,network部分发起的请求一直都是本机;所以emmm…了很久;后面发现是因为将axios的baseURL设置了全路径的相对路径 : https://localhost:3000; 所以请求是直接和baseURL进行结合;发现访问不到,走proxy;直接拼接;报错

删除baseURL之后,会自动按照proxy的地址为相对路径,然后组成绝对路径就可以访问到

而按照网络上80%以上的说法,配置proxy代理是这样的:

proxy: {
      '/api': {
        target: 'XXXX', //代理的目标的路径
        changeOrigin: true,  //是否跨域
        ws: true,
        pathRewrite: {  //路径重写,将拼接的路径的/api去除
          '^/api': ''
        }
      }
    }

这里的意思就是 :将发起请求的路径,比如/api/cart; 中的/api 替换为target的路径, 并且去掉api; 这样就形成了XXXX/cart

如果需要在main.js中的axios配置baseURL ; 可以简单配置 为 baseURL = ‘/api’

axios.defaults.baseURL = '/api'

const {data:res} = await this.$ajax.get('/cart')

生产环境中api请求接口baseURL配置全路径

在开发环境中使用proxy跨域代理,所以baseURL,设置为/api即可; 如果绝对路径没有api,rewirte去掉即可; 在生产环境中,可以将axios的baseURL设置 — 但是还是需要使用ngix等来解决跨域问题

axios.defaults.baseURL = 'https://www.escook.cn/api'

这样就可以成功访问 ---- 所以baseURL要区分开发环境和生产环境

这样拼接之后为/api/cart ----> 本地按照前台路径发现访问不到,进行跨域代理;拼接proxy的target ; 所以按照我片面的推测; 之前的写法一直报错就是因为进行了错误拼接; 之前的baseURL为https://localhost:3000; 拼接形成https://escook.cnhttps://loaclhost:3000/api/cart 所以还是报错

综合列表案例

这里和之前的列表差不多,只是这里使用的是组件库

初始化项目

这里就还是使用上面的demo项目,因为没有什么污染,创建步骤见上👆

删除本来的Helloweorld组件,配置vue.config.js,设置devServer

module.exports ={
	devServer: {
		//修改dev期间的端口号
		port: 8080,
		//自动打开浏览器
		open:true,
	},

接下来就是初始化路由了: 这里就不用vue2的路由创建方式了:router目录创建index.js; 直接使用vue3的格式

import {createRouter,createWebHashHistory} from "vue-router"

const router = createRouter({
	history: createWebHashHistory(),
	routes: [
		
	]
})

export default router

------main.js中导入-------------
app.use(router)

渲染用户表格数据

首先就是创建一个UserList.vue组件

routes: [
		{path:'/',redirect:'/uses'}
		{path:'/users',component: UserList}
	]

然后在App中使用router-view占位; 配置axios ,之后发起请求请求用户数据

created() {
		this.getUserList()
	},
	data() {
		return {
			userList:[],
		}
	},
	methods:{
		async getUserList() {
			const {data:res} = await this.$ajax.get('/api/users')
			//res.status为0表示请求成功
			if(res.status !== 0) return console.log('请求用户列表失败')
			this.userList = res.data
		}
	}

但是这里会出现跨域问题,会报错

Access to XMLHttpRequest at 'http://www.escook.cn/api/users' from origin 'http://localhost:8081' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

所以就要配置proxy代理

//axios.defaults.baseURL = 'https://localhost:3000'  ---- 会和proxy冲突,会执行baseURL,不走跨域代理

module.exports ={
	devServer: {
		//修改dev期间的端口号
		port: 3000,
		//自动打开浏览器
		open:true,
		proxy: 'https://www.escook.cn'
	},
    configureWebpack: {
        plugins: [
            AutoImport({
                resolvers: [ElementPlusResolver()],
            }),
            Components({
                resolvers: [ElementPlusResolver()],
            }),
        ]
    }
}

这里配置跨域代理之后,就不要乱设置baseURL了!!!!!!!, 😢, 不然就会一直以baseURL为准,然后进行代理,访问报错404详解见上👆

然后就是使用Element-plus进行渲染,就先下载Element-plus包,下载自动按需导入的插件并在vue.config.js中进行配置

npm i Element-plus -S
npm install -D unplugin-vue-components unplugin-auto-import

const AutoImport = require('unplugin-auto-import/webpack')
const Components = require('unplugin-vue-components/webpack')
const {ElementPlusResolver} = require('unplugin-vue-components/resolvers')

module.exports ={
	devServer: {
		//修改dev期间的端口号
		port: 3000,
		//自动打开浏览器
		open:true,
		proxy: 'https://www.escook.cn'
	},
    configureWebpack: {
        plugins: [
            AutoImport({
                resolvers: [ElementPlusResolver()],
            }),
            Components({
                resolvers: [ElementPlusResolver()],
            }),
        ]
    }
}

然后使用Element-plues的表格组件进行渲染即可

<template>
	<div>
		<!-- 用户的表格   stripe属性就可以设置为隔行变色 border属性可以设置边框-->
		<el-table :data="userList" stripe border>
			<!-- 加上type= index就可以自动渲染为索引列 -->
			<el-table-column type="index" label="编号" prop="id"></el-table-column>
			<el-table-column label="年龄" prop="age"></el-table-column>
			<el-table-column label="姓名" prop="name"></el-table-column>
			<el-table-column label="职位" prop="position"></el-table-column>
			<el-table-column label="入职时间" prop="addtime"></el-table-column>
		</el-table>
	</div>
</template>

处理时间格式

这里的时间格式可以通过插槽来处理时间格式; el-table-coloum在封装时有一个默认插槽,用户可以填充插槽来规定该列内容的显示格式

<el-table-column label="入职时间">
		<!-- 通过 slot 可以获取到 row, column, $index 和 store(table 内部的状态管理)的数据 -->
		<template  #default='scope'>
			<!-- 填充到默认插槽 --- 列的内容 -->
			{{formDate(scope.row.addtime)}}
		</template>
</el-table-column>

vue3不再使用过滤器,直接使用方法调用来替代,所以直接在methods中声明方法

		formDate(date1) {
			const date = new Date(date1)   //先转为Date对象,才能使用方法
			let y = date.getFullYear()
			let m = this.padZero(date.getUTCMonth() + 1)
			let d = this.padZero(date.getDate())
			let hh = this.padZero(date.getHours())
			let mm = this.padZero(date.getMinutes())
			let ss = this.padZero(date.getMilliseconds())
			return y + "-" + m + "-" + d + " " + hh + ":" + mm + ":" + ss
		},
		padZero(n) { //补0函数
			return n > 9 ? n : '0' + n
		},

这里的补0函数,是属于组件的,和java中的实例方法一样,需要使用对象调用,不能直接使用

实现添加、删除用户

<el-table-column label="操作">
				<template #default>
					<a href="#">详情</a>&nbsp;
					<a href="#">删除</a>
				</template>
			</el-table-column>

这里一定要指定插槽的位置,就是#default,不然template中的内容不会渲染到表格中

添加用户

这里需要点击按钮,弹出对话框,对话框还是使用element-plus中的Dialog组件来进行操作

这里直接在官网上寻找即可,dialog对话框组件,将其中的span标签替换为一个表单el-form

		   <!-- 添加用户的表单  model属性绑定对象-->
		   <el-form :model ='form'>
			   <el-form-item label="姓名" label-width = 80px>
			         <el-input v-model="form.name" />
			    </el-form-item>
				<el-form-item label="年龄"  label-width = 80px>
				      <el-input v-model="form.age" />
				 </el-form-item>
				 <el-form-item label="职位" label-width = 80px>
				       <el-input v-model="form.position" />
				  </el-form-item>
		   </el-form>
		    <template #footer>
		      <span class="dialog-footer">
		        <el-button @click="dialogVisible = false">Cancel</el-button>
		        <el-button type="primary" @click="dialogVisible = false"
		          >Confirm</el-button
		        >
		      </span>
		    </template>
		  </el-dialog>

并且该组件还提供了表单验证的功能,直接在rules中进行绑定即可【validator 验证器,之前的props验证就使用进行自定义验证】

const rules = reactive({
  pass: [{ validator: validatePass, trigger: 'blur' }],
  checkPass: [{ validator: validatePass2, trigger: 'blur' }],
  age: [{ validator: checkAge, trigger: 'blur' }],
})

这里的pass、checkPass、age就是表单项,trigger是触发时机,message就是输入不正确之后的提示信息;还有type,required,min,max等就是类型,是否必要,最小长度,最大长度; 在el-table中通过rules绑定rules;并且在item项中使用prop指定校验规则

<el-form :model ='form' :rules = 'formRules'>
	<el-form-item label="姓名" label-width = 80px prop = 'name'>  prop的名称字段的名称必须一致
        
//表单的验证规则对象
			formRules:{
				name: [ //required必填之后,表单项前面就有一个*
					{required:true,message:'姓名是必填项',trigger:'blur'},
					{min:3,max:5,message:'长度是3到5个字符',trigger:'blur'}
				],
				age:[
					{required:true,message:'年龄是必填项',trigger:'blur'},
				],
				position:[
					{required:true,message:'职位是必填项',trigger:'blur'},
					{min:1,max:10,message:'长度是1到10个字符',trigger:'blur'}
				]
			}

对于自定义验证规则,就直接通过validator指定验证的函数,声明函数是在data中通过箭头函数复制的方式, 然后指明触发的时机就可

data() {
		//声明校验年龄的函数 rule是规则,value是待校验的值,cb是回调函数
		//直接调用cb代表验证通过,new Error代表失败
		let checkAge = (rule,value,cb) => {
			if(!Number.isInteger(value)) {
				return cb(new Error('请填写整数!')) //通过回调函数返回错误消息
			}
			if(value > 100 || value < 1) {
				return cb(new Error('年龄必须在1到100之间'))
			}
			//都满足,验证通过,直接回调
			cb()
		}
		
		return {
			userList:[],
                                        
                                        
                                        
				age:[
					{required:true,message:'年龄是必填项',trigger:'blur'},
					{validator:checkAge,trigger:'blur'}
				],
对话框dailog关闭时重置

这个时候就需要监听关闭的事件,直接为el-dialog添加close事件监听

@close = 'onDialogClosed'

//使用提供的form对象的resetFields方法就可以重置
<el-form :model ='form' :rules = 'formRules' ref="myAddFrom">
    
onDialogClosed() {
	//重置表单,直接调用官网提供的resetFields就可以重置
	this.$refs.myAddFrom.resetFields()
}
表单预验证

在点击确定按钮的时候,不应该直接发起ajax请求,而是要先判断数据是否合法,并且发起post请求,成功后重新获取数据

//进行表单预验证
		onAddNewUser() {
			//使用表单对象的validate方法,传递一个cb函数,参数vlaid为false就是验证失败
			this.$refs.myAddFrom.validate(async (valid) => {
				//valid代表之前的验证结果,成功就是true
				if(!valid) return
				//正确的就正常发起ajax,get就只是路径,post还需要在第二个参数传递数据,这里就直接是form对象
				const {data:res} = await this.$ajax.post('/users',this.form)
				// console.log(res) 这里的res就是ajax发起的Promise的结果,status为0代表成功
				if(res.status !== 0) return console.log('添加失败')
				console.log('添加成功')
				this.dialogVisible = false
				
				//刷新用户列表
				this.getUserList()
			})
		}
使用message组件优化提示

Element-Plus提供了消息提示,有success/warning/info/error几种提示,可以直接通过原型对象调用$message.success(‘具体消息即可’) 每个组件都挂载,所以直接通过this即可访问

if(res.status !== 0) return this.$message.error('添加失败')
this.$message.success('添加成功')

但是如果没有全局导入,只是按需导入,那么久直接导入ELMessage,然后使用这个对象的success和error方法即可

import { ElMessage } from 'element-plus'

if(res.status !== 0) return ElMessage.error('添加失败')
		ElMessage.success('添加成功')
删除用户前给提示 MessageBox弹框组件
<a href="#" @click.prevent="onRemoveUser">删除</a>

import { ElMessageBox } from 'element-plus'

//删除用户
		async onRemoveUser() {
			const confirmResult = await ElMessageBox.confirm('此操作将永久删除用户,是否继续?','提示',{
				confirmButtonText: '确定',
				cancelButtonText: '取消',
				type: 'warning'
			}).catch(err => err)
			
			if(confirmResult !== 'confirm') return ElMessage.info('取消了删除')
			//确认删除
			ElMessage.success('删除成功')
		}

如果全局引入,那么使用this.$confirm来代替ELMessageBox

这里要删除,一定要获取id,所以通过作用域插槽【默认插槽可以简写 v-slot=‘scope’】结构复制row.id,传入删除, 请求地址为/users/:id 请求类型为delete,【除了get,post】

async onRemoveUser(id) {
			const confirmResult = await ElMessageBox.confirm('此操作将永久删除用户,是否继续?','提示',{
				confirmButtonText: '确定',
				cancelButtonText: '取消',
				type: 'warning'
			}).catch(err => err)
			
			if(confirmResult !== 'confirm') return ElMessage.info('取消了删除')
			//确认删除
			const {data:res} = this.$ajax.delete('/users/' + id)
			if(res.status !== 0) return ElMessage.error('删除失败')
			ElMessage.success('删除成功')
			//刷新列表
			this.getUserList()
		}

通过路由跳转详情

这里使用router-link来进行路由

<router-link :to="'/users' + row.id">详情</router-link>

{path:'/users/:id',component:UserDetail,props:true}

<template>
  <el-card class="box-card">
    <template #header>
      <div class="card-header">
        <span>用户详情</span>
        <el-button class="button" type="text">返回</el-button>
      </div>
    </template>

其他的琐碎的东西不再赘述,这里展示关键组件UserList

<template>
	<div>
		<!-- 添加按钮 -->
		<el-button type = 'primary' @click = 'dialogVisible = true'>添加新用户</el-button>
		<!-- 用户的表格   stripe属性就可以设置为隔行变色 border属性可以设置边框-->
		<el-table :data="userList" stripe border>
			<!-- 加上type= index就可以自动渲染为索引列 -->
			<el-table-column type="index" label="编号" prop="id"></el-table-column>
			<el-table-column label="姓名" prop="name"></el-table-column>
			<el-table-column label="年龄" prop="age"></el-table-column>
			<el-table-column label="职位" prop="position"></el-table-column>
			<el-table-column label="入职时间">
				<!-- 通过 slot 可以获取到 row, column, $index 和 store(table 内部的状态管理)的数据 -->
				<template  #default='scope'>
					<!-- 填充到默认插槽 --- 列的内容 -->
					{{formDate(scope.row.addtime)}}
				</template>
			</el-table-column>
			<el-table-column label="操作">
				<template #default='{row}'>
					<router-link :to="'/users/' + row.id">详情</router-link>&nbsp;
					<a href="#" @click.prevent="onRemoveUser(row.id)">删除</a>
				</template>
			</el-table-column>
		</el-table>
		<!-- 对话框 -->
		  <el-dialog
		    v-model="dialogVisible"
		    title="添加新用户"
		    width="50%"
			@close = 'onDialogClosed'
		  >
		   <!-- 添加用户的表单  model属性绑定对象-->
		   <el-form :model ='form' :rules = 'formRules' ref="myAddFrom">
			   <el-form-item label="姓名" label-width = 80px prop = 'name'>
			         <el-input v-model="form.name" />
			    </el-form-item>
				<el-form-item label="年龄"  label-width = 80px prop = 'age'>
				      <el-input v-model.number="form.age" />
				 </el-form-item>
				 <el-form-item label="职位" label-width = 80px prop = 'position'>
				       <el-input v-model="form.position" />
				  </el-form-item>
		   </el-form>
		    <template #footer>
		      <span class="dialog-footer">
		        <el-button @click="dialogVisible = false">取消</el-button>
		        <el-button type="primary" @click="onAddNewUser">确定</el-button>
		      </span>
		    </template>
		  </el-dialog>
	</div>
</template>

<script>
import { ElMessage } from 'element-plus'
import { ElMessageBox } from 'element-plus'
	
export default {
	name:'UserList',
	created() {
		this.getUserList()
	},
	data() {
		//声明校验年龄的函数 rule是规则,value是待校验的值,cb是回调函数
		//直接调用cb代表验证通过,new Error代表失败
		let checkAge = (rule,value,cb) => {
			if(!Number.isInteger(value)) {
				return cb(new Error('请填写整数!')) //通过回调函数返回错误消息
			}
			if(value > 100 || value < 1) {
				return cb(new Error('年龄必须在1到100之间'))
			}
			//都满足,验证通过,直接回调
			cb()
		}
		
		return {
			userList:[],
			dialogVisible: false,  //控制添加对话框显示
			form: {//需要采集的信息
				name:'',
				age: '',
				position: '',
			},
			//表单的验证规则对象
			formRules:{
				name: [ //required必填之后,表单项前面就有一个*
					{required:true,message:'姓名是必填项',trigger:'blur'},
					{min:3,max:5,message:'长度是3到5个字符',trigger:'blur'}
				],
				age:[
					{required:true,message:'年龄是必填项',trigger:'blur'},
					{validator:checkAge,trigger:'blur'}
				],
				position:[
					{required:true,message:'职位是必填项',trigger:'blur'},
					{min:1,max:10,message:'长度是1到10个字符',trigger:'blur'}
				]
			}
		}
	},
	methods:{
		async getUserList() {
			const {data:res} = await this.$ajax.get('/users')
			//res.status为0表示请求成功
			if(res.status !== 0) return console.log('请求用户列表失败')
			this.userList = res.data
		},
		formDate(date1) {
			const date = new Date(date1)   //先转为Date对象,才能使用方法
			let y = date.getFullYear()
			let m = this.padZero(date.getUTCMonth() + 1)
			let d = this.padZero(date.getDate())
			let hh = this.padZero(date.getHours())
			let mm = this.padZero(date.getMinutes())
			let ss = this.padZero(date.getMilliseconds())
			return y + "-" + m + "-" + d + " " + hh + ":" + mm + ":" + ss
		},
		padZero(n) { //补0函数
			return n > 9 ? n : '0' + n
		},
		//对话框关闭的处理函数
		onDialogClosed() {
			//重置表单,直接调用官网提供的resetFields就可以重置
			this.$refs.myAddFrom.resetFields()
		},
		//进行表单预验证
		onAddNewUser() {
			//使用表单对象的validate方法,传递一个cb函数,参数vlaid为false就是验证失败
			this.$refs.myAddFrom.validate(async (valid) => {
				//valid代表之前的验证结果,成功就是true
				if(!valid) return
				//正确的就正常发起ajax,get就只是路径,post还需要在第二个参数传递数据,这里就直接是form对象
				const {data:res} = await this.$ajax.post('/users',this.form)
				// console.log(res) 这里的res就是ajax发起的Promise的结果,status为0代表成功
				if(res.status !== 0) return ElMessage.error('添加失败')
				ElMessage.success('添加成功')
				this.dialogVisible = false
				
				//刷新用户列表
				this.getUserList()
			})
		},
		//删除用户
		async onRemoveUser(id) {
			const confirmResult = await ElMessageBox.confirm('此操作将永久删除用户,是否继续?','提示',{
				confirmButtonText: '确定',
				cancelButtonText: '取消',
				type: 'warning'
			}).catch(err => err)
			
			if(confirmResult !== 'confirm') return ElMessage.info('取消了删除')
			//确认删除
			const {data:res} = this.$ajax.delete('/users/' + id)
			if(res.status !== 0) return ElMessage.error('删除失败')
			ElMessage.success('删除成功')
			//刷新列表
			this.getUserList()
		}
	},
}
</script>

<style lang="less" scoped>
	.el-table {
		margin-top: 15px;
	}
</style>

看看页面效果

请添加图片描述

关于vue3的基本知识就到这里了,后面再补充其他的🎉

Logo

前往低代码交流专区

更多推荐