前言
无。
环境搭建
首先安装Vue3.0
找到官方文档下面的安装方法,使用create命令创建一个新的Vue项目:
npm create vue@latest
安装了3.11.1版本的vue,先安装vite构建工具:
npm install vite
代码完成后再通过build命令可以将vue项目构建为发布的前端代码:
npm run build
然后就会在项目根目录下生成一个新的dist目录,里面包含了js、css、html和其他静态文件等前端文件。
ElementUI
ElementUI是一个好用的组件库,方便我这种前端菜鸟抄来构建一个看得过去的前端界面,找到官方文档下面的安装方法:
npm install element-plus
通过ElementUI标签,就可以方便地在前端页面引入各种组件,比如按钮,修改一下App.vue:
<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:
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");
就能看到按钮了。
其他依赖
npm install axios moment qs
Vue基础语法
模板语法
两个大括号可以将数据当作纯文本填入模板:
<el-button>{{ message }}</el-button>
需要将数据当作HTML,即动态渲染页面时可以使用v-html:
<span v-html="rawHtml"></span>
不怎么用得到,动态渲染还是比较危险的行为,什么表达式注入、模板注入、XSS都是动态渲染导致的。
双大括号填充数据不能用在HTML标签的属性里面,需要使用v-bind,或者缩写:
<div v-bind:id="dynamicId"></div>
<div :id="dynamicId"></div>
也可以通过对象一次性绑定多个属性:
const objectOfAttrs = {
id: 'container',
class: 'wrapper',
style: 'background-color:green'
}
除了单个变量,还可以填充一个表达式,包括字符串拼接和函数调用:
{{ 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代码块,具体可以看官方指令集文档。
计算属性
一种特殊的属性,在每次访问它时会先经过代码块运算得到结果,比如:
<el-progress type="dashboard" :percentage="gotScorePercentage" :format="gotScore" :color="colors"></el-progress>
gotScorePercentage就是一个计算属性,在export default的computed中定义,访问时会先进行计算:
computed: {
gotScorePercentage: function() {
return 100 * (this.myScore / this.fullScore);
}
},
条件渲染
先设置一个数据:
data() {
return {
ok: 1
}
},
条件渲染即v-if指令:
<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指令:
<h1 v-show="ok === 1">Hello!</h1>
区别在于v-show的实现方式是切换标签的display属性,所以无论条件如何,标签都会被渲染。
而v-if是惰性的,只有条件符合时才会被渲染。
列表渲染
先设置一串数据:
data() {
return {
items: [
{msg: "Twings"},
{msg: "Aluvion"}
]
}
}
v-for指令可以遍历数组,分割成index和数据项并生成多个HTML块:
<li v-for="(item, index) in items">
{{ index }}: {{ item.msg }}
</li>
根据文档,使用v-for时最好给它提供一个唯一的key便于渲染。
事件处理
JavaScript常用的on事件,在Vue里可以用v-on指令或者@click的简写方式监听事件,先定义一个method:
methods: {
clickButton(event) {
console.log(event)
}
},
然后再将它配置到click事件里:
<el-button @click="clickButton">{{ message }}</el-button>
这样点击按钮就能触发事件了。
表单输入绑定
Web页面中常用的表单传值,为了JavaScript异步传值需要将表单输入绑定到某个变量上面:
<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样式就恢复正常了:
@media (min-width: 1024px) {
body {
/* display: flex; */
place-items: center;
}
#app {
/* display: grid; */
grid-template-columns: 1fr 1fr;
padding: 0 2rem;
}
}
抽屉式导航栏
<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>
环形进度条
<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代码:
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
},
},
标签式导航
<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>
日历
<div v-if="activeIndex == 'index'" style="width: 95%; margin: 0 auto;">
<el-calendar v-model="dateValue" />
</div>
倒计时
<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隐藏表单
<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>