复习vue3.0+ElementUI

前言

无。


环境搭建

首先安装Vue3.0

找到官方文档下面的安装方法,使用create命令创建一个新的Vue项目:

1
npm create vue@latest

安装了3.11.1版本的vue,先安装vite构建工具:

1
npm install vite

代码完成后再通过build命令可以将vue项目构建为发布的前端代码:

1
npm run build

然后就会在项目根目录下生成一个新的dist目录,里面包含了js、css、html和其他静态文件等前端文件。

ElementUI

ElementUI是一个好用的组件库,方便我这种前端菜鸟抄来构建一个看得过去的前端界面,找到官方文档下面的安装方法:

1
npm install element-plus

通过ElementUI标签,就可以方便地在前端页面引入各种组件,比如按钮,修改一下App.vue:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script setup>

</script>

<template>
<div id="app">
<el-button>{{ message }}</el-button>
</div>
</template>

<script>
export default {
data() {
return {
message: "Hello ElementUI!"
}
}
}
</script>

然后再修改一下main.js:

1
2
3
4
5
6
7
8
9
10
import './assets/main.css'

import { createApp } from 'vue'
import 'element-plus/dist/index.css'
import ElementPlus from 'element-plus'
import App from './App.vue'

const app = createApp(App);
app.use(ElementPlus);
app.mount("#app");

就能看到按钮了。

其他依赖

1
npm install axios moment qs

Vue基础语法

模板语法

两个大括号可以将数据当作纯文本填入模板:

1
<el-button>{{ message }}</el-button>

需要将数据当作HTML,即动态渲染页面时可以使用v-html:

1
<span v-html="rawHtml"></span>

不怎么用得到,动态渲染还是比较危险的行为,什么表达式注入、模板注入、XSS都是动态渲染导致的。

双大括号填充数据不能用在HTML标签的属性里面,需要使用v-bind,或者缩写:

1
2
<div v-bind:id="dynamicId"></div>
<div :id="dynamicId"></div>

也可以通过对象一次性绑定多个属性:

1
2
3
4
5
const objectOfAttrs = {
id: 'container',
class: 'wrapper',
style: 'background-color:green'
}

除了单个变量,还可以填充一个表达式,包括字符串拼接和函数调用:

1
2
3
4
5
6
7
8
9
10
11
{{ number + 1 }}

{{ ok ? 'YES' : 'NO' }}

{{ message.split('').reverse().join('') }}

<div :id="`list-${id}`"></div>

<time :title="toTitleDate(date)" :datetime="date">
{{ formatDate(date) }}
</time>

除此之外,其他v-语法也各有用处,比如v-if控制标签存在与否,v-for遍历数组并生成多个HTML代码块,具体可以看官方指令集文档

计算属性

一种特殊的属性,在每次访问它时会先经过代码块运算得到结果,比如:

1
<el-progress type="dashboard" :percentage="gotScorePercentage" :format="gotScore" :color="colors"></el-progress>

gotScorePercentage就是一个计算属性,在export default的computed中定义,访问时会先进行计算:

1
2
3
4
5
computed: {
gotScorePercentage: function() {
return 100 * (this.myScore / this.fullScore);
}
},

条件渲染

先设置一个数据:

1
2
3
4
5
data() {
return {
ok: 1
}
},

条件渲染即v-if指令:

1
2
3
4
5
6
7
8
9
10
11
12
<div v-if="ok === 3">
A
</div>
<div v-else-if="ok === 2">
B
</div>
<div v-else-if="ok === 1">
C
</div>
<div v-else>
Not A/B/C
</div>

和v-show指令:

1
<h1 v-show="ok === 1">Hello!</h1>

区别在于v-show的实现方式是切换标签的display属性,所以无论条件如何,标签都会被渲染。

而v-if是惰性的,只有条件符合时才会被渲染。

列表渲染

先设置一串数据:

1
2
3
4
5
6
7
8
data() {
return {
items: [
{msg: "Twings"},
{msg: "Aluvion"}
]
}
}

v-for指令可以遍历数组,分割成index和数据项并生成多个HTML块:

1
2
3
<li v-for="(item, index) in items">
{{ index }}: {{ item.msg }}
</li>

根据文档,使用v-for时最好给它提供一个唯一的key便于渲染。

事件处理

JavaScript常用的on事件,在Vue里可以用v-on指令或者@click的简写方式监听事件,先定义一个method:

1
2
3
4
5
methods: {
clickButton(event) {
console.log(event)
}
},

然后再将它配置到click事件里:

1
<el-button @click="clickButton">{{ message }}</el-button>

这样点击按钮就能触发事件了。

表单输入绑定

Web页面中常用的表单传值,为了JavaScript异步传值需要将表单输入绑定到某个变量上面:

1
2
<input v-model="message" placeholder="edit me" />
<el-button @click="clickButton">{{ message }}</el-button>

将输入绑定到了message变量上面,这样按钮中的消息也会跟着输入框改变。

此外,textarea、checkbox、radio和select等输入类型也是类似的绑定方式,使用multiple属性可以将多个值绑定到同一个select变量上。

生命周期钩子

如created等方法,方便初始化环境。

ElementUI Plus

发现一个非常奇怪的问题,我最上层的app div只能显示非常小的一块地区,标签栏都被压缩了。

后面发现问题出在默认引入的main.css里面,删掉body和app定义的display样式就恢复正常了:

1
2
3
4
5
6
7
8
9
10
11
12
13
@media (min-width: 1024px) {
body {
/* display: flex; */
place-items: center;
}

#app {
/* display: grid; */
grid-template-columns: 1fr 1fr;
padding: 0 2rem;
}
}

抽屉式导航栏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<el-switch v-model="isCollapse" size="large" style="margin-left: 24px; margin-bottom: 20px;"/>
<el-menu defaultActive="Reading" class="el-menu-vertical-demo" @select="handleTag" :collapse="!isCollapse">
<el-menu-item index="Reading">
<el-icon><Reading /></el-icon>
<span style="width: 120px;">查看</span>
</el-menu-item>
<el-menu-item index="Plus">
<el-icon><Plus /></el-icon>
<span>新增</span>
</el-menu-item>
<el-menu-item index="Edit">
<el-icon><Edit /></el-icon>
<span>修改</span>
</el-menu-item>
<el-menu-item index="Delete">
<el-icon><Delete /></el-icon>
<span>删除</span>
</el-menu-item>
<el-menu-item index="Search">
<el-icon><Search /></el-icon>
<el-input v-if="isCollapse" v-model="search" style="width: 120px;" placeholder="输入搜索" clearable/>
</el-menu-item>
</el-menu>

环形进度条

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div style="text-align: center;">
<el-progress type="dashboard" :percentage="lunchTime" :color="colors"></el-progress>
<br/>
<span>上午工作还有: {{ leftTimeLunch() }}</span>
</div>
<div style="text-align: center; margin-top: 100px;">
<el-progress type="dashboard" :percentage="AfterNoonTime" :color="colors"></el-progress>
<br/>
<span>下午工作还有: {{ leftAfterNoon() }}</span>
</div>
<div style="text-align: center; margin-top: 100px;">
<el-progress type="dashboard" :percentage="workFinished" :color="colors"></el-progress>
<br/>
<span>距离下班还有: {{ leftTime() }}</span>
</div>

JS代码:

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
data() {
nowDate: dayjs(),
restDate: dayjs().endOf("week").subtract(20, 'day').subtract(20, 'hour').subtract(20, 'minute'),
morningDate: dayjs().startOf("day").add(20, 'hour').add(20, 'minute'),
AfterNoonDate: dayjs().startOf("day").add(20, 'hour').add(20, 'minute'),
finishDate: dayjs().startOf('day').add(20, 'hour').add(20, 'minute'),
lunchDate: dayjs().startOf('day').add(20, 'hour').add(20, 'minute'),
},
methods: {
leftTime() {
if (this.nowDate > this.finishDate) {
return 0;
}
return this.nowDate < this.morningDate ? this.morningDate.to(this.finishDate, true) : this.nowDate.to(this.finishDate, true)
},
leftTimeLunch() {
if (this.nowDate > this.lunchDate) {
return 0;
}
return this.nowDate < this.morningDate ? this.morningDate.to(this.lunchDate, true): this.nowDate.to(this.lunchDate, true)
},
leftAfterNoon() {
if (this.nowDate > this.finishDate) {
return 0;
}
return this.nowDate < this.AfterNoonDate ? this.AfterNoonDate.to(this.finishDate, true) : this.nowDate.to(this.finishDate, true)
},
},
computed: {
workFinished: function() {
var diff = this.finishDate.diff(this.nowDate)
if (diff.valueOf() < 0) {
return 0;
}
var percentage = Math.ceil(100 * diff.valueOf() / (3600000 * 8)) > 100 ? 100 : Math.ceil(100 * diff.valueOf() / (3600000 * 8))
return 100 - percentage;
},
lunchTime: function() {
var diff = this.lunchDate.diff(this.nowDate)
if (diff.valueOf() < 0) {
return 0;
}
var percentage = Math.ceil(100 * diff.valueOf() / (3600000 * 3 + 60000 * 10)) > 100 ? 100 : Math.ceil(100 * diff.valueOf() / (3600000 * 3 + 60000 * 10))
return 100 - percentage
},
AfterNoonTime: function() {
var diff = this.finishDate.diff(this.nowDate)
if (diff.valueOf() < 0) {
return 0;
}
var percentage = Math.ceil(100 * diff.valueOf() / (3600000 * 2.5 + 60000 * 10)) > 100 ? 100 : Math.ceil(100 * diff.valueOf() / (3600000 * 2.5 + 60000 * 10))
return 100 - percentage
},
},

标签式导航

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
<el-menu style="display:flex;" :default-active="defaultactiveIndex" class="el-menu-demo" mode="horizontal" @select="handleSelect" textColor="#A0CFFF" activeTextColor="#409EFF">
<el-menu-item index="index" style="margin-left: 5%">主页</el-menu-item>
<el-menu-item index="note" v-bind:disabled="!isLogin" @click="note_info();">备忘录</el-menu-item>
<el-menu-item index="todo" v-bind:disabled="!isLogin" @click="todo_info();">待办</el-menu-item>
<el-menu-item index="file" v-bind:disabled="!isLogin" @click="file_info();">附件</el-menu-item>

<el-dropdown style="align-self:center; position:fixed; right: 22%">
<span class="el-dropdown-link">
<el-button type="success">{{ loginForm.name != '' ? loginForm.name : '未登录'}}</el-button>
</span>
<!--用户相关-->
<template #dropdown>
<el-dropdown-menu>
<div v-if="isLogin">
<el-dropdown-item @click="logout()">
退出
</el-dropdown-item>
</div>
<div v-if="!isLogin">
<el-dropdown-item @click="dialogFormVisible = true">
登录
</el-dropdown-item>
</div>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-menu>

日历

1
2
3
<div v-if="activeIndex == 'index'" style="width: 95%; margin: 0 auto;">
<el-calendar v-model="dateValue" />
</div>

倒计时

1
2
3
4
5
6
7
8
9
10
11
<el-countdown format="DD [天] HH:mm:ss" :value="restDate">
<template #title>
<div style="display: inline-flex; align-items: center">
<el-icon style="margin-right: 4px" :size="12">
<Calendar />
</el-icon>
距离周末还有
</div>
</template>
</el-countdown>
<div class="countdown-footer">{{ nowDate.format('YYYY-MM-DD') }}</div>

dialog隐藏表单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<el-dialog title="登录" v-model="dialogFormVisible">
<el-form v-model="loginForm">
<el-form-item label="Username:" :label-width="formLabelWidth" required>
<el-input placeholder="请输入用户" v-model="loginForm.name" maxlength="20" clearable></el-input>
</el-form-item>
<el-form-item label="Password:" :label-width="formLabelWidth" required>
<el-input placeholder="请输入密码" v-model="loginForm.pwd" maxlength="20" clearable show-password></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="logout()">取 消</el-button>
<el-button type="primary" @click="login()" v-bind:disabled="loginForm.name == '' || loginForm.pwd == ''">登 录</el-button>
</div>
</el-dialog>

Vue3.0官方文档

ElementUI Plus官方文档


复习vue3.0+ElementUI
http://yoursite.com/2024/10/25/复习vue3-0-ElementUI/
作者
Aluvion
发布于
2024年10月25日
许可协议