Vue-Cli搭建项目
以下文章是针对vue2的使用介绍
环境搭建
1. 全局安装vue
npm install vue
2. 安装vue的cnpm为淘宝镜像
npm install -g cnpm –registry=https://registry.npm.taobao.org
3. 全局安装vue-cli
npm install --global vue-cli
4. 创建一个基于webpack的项目
vue init webpack vuedemo
5. 安装依赖,运行项目
cd vuedemo 切换到项目根目录
npm install 安装依赖
npm run dev 运行项目
项目运行成功,默认端口是8080
项目结构
项目的根目录下的src下的main.js为项目的入口文件
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
Vue.config.productionTip = false // vue环境下的生产提示
/* eslint-disable no-new */
new Vue({
el: '#app', // 绑定根视图
router,
components: { App }, // 加载组件
template: '<App/>' // 使用组件
})
自定义属性
<template>
<div id="app">
<!--v-bind:属性名称=属性值-->
<!--可以简写为:属性名=属性值-->
<!--p添加自定义属性id="12"-->
<p :id="msg">123</p>
</div>
</template>
<script>
export default {
name: 'App',
data () {
return {
msg: '12'
}
}
}
</script>
<style scoped>
</style>
页面样式
获取自定义属性值
<template>
<div id="app">
<!--v-bind:属性名称=属性值-->
<!--可以简写为:属性名=属性值-->
<!--p添加自定义属性id="12"-->
<p msg="msg" @click="getMsg">123</p>
</div>
</template>
<script>
export default {
name: 'App',
data () {
return {
msg: '12'
}
},
methods: {
getMsg: function (e) {
// 获取属性msg的值
console.log(e.target.getAttribute('msg'))
}
}
}
</script>
<style scoped>
</style>
template
所以可以在页面使用多个template,通过v-if来指定渲染哪个template包裹的元素
<template>
<div id="app">
<p>124</p>
<template v-if="flag">
<p>125</p>
</template>
</div>
</template>
<script>
export default {
name: 'App',
data () {
return {
msg: '12',
flag: false
}
},
methods: {
}
}
</script>
<style scoped>
</style>
页面样式
计算属性
对数据进行处理
<div id="example">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
组件
1、创建组件
HTML、JavaScript、CSS
在项目的src/components下创建vue的组件
<template>
<div>我是Hello</div>
</template>
<script>
export default {
name: 'hello'
}
</script>
<!--scoped:样式的作用域,代表样式只在当前组件中生效-->
<style scoped>
</style>
2、使用组件
在App.vue中使用组件
<template>
<div id="app">
<hello></hello>
</div>
</template>
<script>
/* 导入组件 */
import hello from './components/hello'
export default {
name: 'App',
data () {
return {
}
},
/* 注册组件 */
components: {hello},
methods: {
}
}
</script>
<style scoped>
</style>
页面效果
父组件给子组件传值
- 在父组件页面引用子组件
- 在父组件页面上的子组件绑定传递的属性和属性值
- 子组件使用props接收父组件传递的属性
父组件页面
<template>
<div>
我是父组件
<child :msg="msg"></child>
</div>
</template>
<script>
import child from "./child"
export default {
name: "parent",
data(){
return{
msg:'父亲给儿子的东西'
}
},
components:{child}
}
</script>
<style scoped>
</style>
子组件页面
<template>
<div>我是子组件
<div>{{msg}}</div>
</div>
</template>
<script>
export default {
name: "child",
data(){
return{
}
},
props:{
msg:{
type:String,
required:true
}
}
}
</script>
<style scoped>
</style>
页面显示
子组件给父组件传值
子组件通过事件向父组件传值
- 子组件定义事件触发父组件自定义事件和传值
- 父组件自定义事件函数使用形参接收子组件传递的值
子组件
<template>
<div>我是子组件
<button @click="sendMsg" type="button">发送信息</button>
</div>
</template>
<script>
export default {
name: "child",
data(){
return{
initMsg:'我是儿子的数据,还要发送给父亲'
}
},
methods:{
sendMsg(e){
//儿子准备发送好数据
this.$emit('getInfo',this.initMsg);
}
}
}
</script>
<style scoped>
</style>
父组件
<template>
<div>
<child @getInfo="getInfo"></child>
<p>{{initInfo}}</p>
</div>
</template>
<script>
import child from "./child"
export default {
name: "parent",
data(){
return{
initInfo:''
}
},
components:{child},
methods:{
getInfo(msg){
this.initInfo=msg;
}
}
}
</script>
<style scoped>
</style>
页面样式
点击发送信息按钮,页面样式如下:
插槽
插槽
<template>
<div class="container">
<p>准备实现插槽功能</p>
<!--组件渲染时,插槽的默认内容,没有提供内容时显示,有内容则不显示默认内容-->
<slot>为传递单个插槽功能</slot>
</div>
</template>
<script>
export default {
name: "useComponent",
data(){
return{
}
}
}
</script>
<style scoped>
</style>
App.vue中引用组件
<template>
<div id="app">
<useComponent>
<!--虽然数据传递给子组件显示,但是,渲染的时候,依然是在父组件中渲染的-->
</useComponent>
</div>
</template>
<script>
import parent from "./components/parent";
import useComponent from "./components/useComponent";
export default {
name: 'App',
components:{parent,useComponent}
}
</script>
<style>
</style>
具名插槽
< slot > 元素有一个特殊的 attribute:name。这个 attribute 可以用来定义额外的插槽:
一个不带 name 的 < slot > 出口会带有隐含的名字“default”
向具名插槽提供内容的时候,我们可以在一个 < template > 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称:
使用步骤
- 定义具名插槽
<template>
<div class="container">
<!--定义具名插槽的name="hello"-->
<slot name="hello"></slot>
</div>
</template>
<script>
export default {
name: "useComponent",
data(){
return{
}
}
}
</script>
<style scoped>
</style>
- 使用具名插槽
<template>
<div id="app">
<useComponent>
<!--为具名插槽name="hello"的插槽提供内容-->
<p slot="hello">hello的具名插槽</p>
</useComponent>
</div>
</template>
<script>
import parent from "./components/parent";
import useComponent from "./components/useComponent";
export default {
name: 'App',
components:{parent,useComponent}
}
</script>
<style>
</style>
页面显示
组件缓存
如果把切换出去的组件保留在内容中,保留它的状态户或避免重新渲染,可以添加一个keep-alive指令参数
组件ct1
<template>
<div class="ct1">
ct1
</div>
</template>
<script>
export default {
name: "ct1"
}
</script>
<style scoped>
</style>
组件ct2
<template>
<div class="ct2">
ct2
</div>
</template>
<script>
export default {
name: "ct2"
}
</script>
<style scoped>
</style>
App.vue
<template>
<div id="app">
<button @click="changeEvent">切换</button>
<keep-alive>
<component :is="currentView"></component>
</keep-alive>
</div>
</template>
<script>
import ct1 from "./components/ct1";
import ct2 from './components/ct2';
export default {
name: 'App',
components:{ct1,ct2},
data(){
return{
currentView:ct1,//当前组件为ct1
flag:true//切换组件的开关
}
},
methods:{
changeEvent(e){
if(this.flag==true){
this.currentView=ct2;
this.flag=false;
}else{
this.currentView=ct1;
this.flag=true;
}
}
}
}
</script>
<style scoped>
</style>
页面样式
点击按钮动态切换组件
生命周期函数
- beforeCreate
- created
- beforeMount
- mounted
- beforeUpdate
- updated
- beforeDestroy
- destroyed
<template>
<div id="app">
<button @click="changeData">改变</button>
{{myData}}
</div>
</template>
<script>
export default {
name: "ComponentDemo",
data(){
return{
myData:'改变之前的数据'
}
},
methods:{
changeData(){
this.myData='数据改变之后';
}
},
//组件被创建之前
beforeCreate() {
console.log('组件被创建之前');
},
created() {
console.log('组件被创建之后');
},
//组件被渲染之前
beforeMount() {
console.log('组件被渲染之前');
},
mounted() {
console.log('组件被渲染之后');
},
//数据改变重新渲染
beforeUpdate() {
console.log('数据改变重新渲染之前');
},
updated() {
console.log('数据改变重新渲染之后');
},
//组件销毁之前
beforeDestroy() {
console.log('组件被销毁之前');
},
destroyed() {
console.log('组件被销毁之后');
}
}
</script>
<style scoped>
</style>
页面加载时
点击按钮
路由
基础使用
所有的组件通过路由嵌套起来,然后形成页面跳转的效果
- 安装
npm install --save vue-router
- 编写代码
- 在main.js中引入router
import VueRouter from 'vue-router'
Vue.use(VueRouter)
-创建Router
// 创建router
var router = new VueRouter({
routes:[
{
path:'/',
name:'Hello',
component:HelloWorld
}
]
})
- 注入router
在src/main.js中注入路由
new Vue({
el: '#app',
router, // 注入 router
components: { App },
template: '<App/>'
})
在main.js中注入router的完整代码
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
// 引入VueRouter
import VueRouter from "vue-router"
// 引入路由组件
import HelloWorld from "./components/HelloWorld"
Vue.config.productionTip = false
// 使用VueRouter
Vue.use(VueRouter)
// 创建router
var router = new VueRouter({
routes:[
{
path:'/',
name:'Hello',
component:HelloWorld
}
]
})
/* eslint-disable no-new */
new Vue({
el: '#app',
router, // 挂载router
components: { App },
template: '<App/>'
})
- 显示路由组件
在App.vue中显示路由组件
<template>
<div id="app">
<img src="./assets/logo.png">
<!--路由显示的位置-->
<router-view></router-view>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld'
export default {
name: 'App',
components: {
HelloWorld
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
页面访问
http://localhost:8080/#/
在vue-cli中写法
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
}
]
})
在main.js中引入并注册
import router from './router' // 引入router(默认引入router文件夹下index.js)
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
在app.vue中使用router-view标签
<template>
<div id="app">
<img src="./assets/logo.png">
<router-view/>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
项目结构
router-link
命名路由
- router-link 比起写死的< a href="" >会好一些
- 无论是HTML5 history模式还是hash 模式,它的表现行为一致,当切换路由模式,或者在IE9降级使用hash模式,无须任何变动
- 在HTML5 history 模式下,router-link 会守卫点击事件,让浏览器不再重新加载页面
- 当在HTML5 history 模式下,使用base 选项之后,所有 to 属性都不需要写(基础路径)了
代码:
前提是在安装路由、在main.js中引入并注册
才可以使用router-link
1. 在vue-cli搭建的项目的router下的index.js中配置路由
export default new Router({
routes: [
{
path: '/hi',
name: 'hi',
component: hi
}
]
})
2. 在引用的页面使用命名路由
<!-- :to{name:'',} name对应router/index.js中配置的name
-->
<router-link :to="{name:'hi'}">hi</router-link>
<!--path:对应router/index.js中配置的path-->
<router-link :to="{path:'/hi'}">hi</router-link>
页面中渲染
router-link 在页面中显示为a链接
router-link 在页面中显示为span链接
<!-- :to{name:'',} name对应router/index.js中配置的name tag="span" tag渲染成指定的标签,这里在页面中渲染成span标签
-->
<router-link :to="{name:'hi'}" tag="span">hi</router-link>
重定向
在路由的配置文件中配置的路由使用redirect
官网介绍
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
redirect: 'nav' // 重定向配置
},
{
path: '/nav',
name: 'nav',
component: nav
},
]
})
路由嵌套
官网介绍
URL 中各段动态路径也按某种结构对应嵌套的各层组件
使用children
显示的位置:父级在哪里,显示的位置就在哪里
import Vue from 'vue'
import Router from 'vue-router'
import hi from '../components/hi'
import nav from '../nav'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
redirect: 'nav' // 重定向配置
},
{
path: '/nav',
name: 'nav',
component: nav,
children:[ // 嵌套路由
{
path: '/hi',
name: 'hi',
component: hi
}
]
}
]
})
在引用的页面使用router-view标签
路由参数传递
官网介绍
- 配置路由信息
{
path: '/hello',
name: 'hello',
component: hello
}
- 配置路由跳转信息
<router-link :to="{name:'hello', params: { userId: 123 }}">hello</router-link>
<router-link :to="{path:'/hello', query: { plain: 'private' }}">hello带查询参数</router-link>
路由参数获取
官网介绍
<template>
<div>
<div>hello</div>
<div>路径:{{$route.path}}</div>
<div>传递的参数params下的UserId:{{$route.params.userId}}</div>
<div>传递的查询参数plan:{{ $route.query.plain }}</div>
</div>
</template>
<script>
export default {
name: 'hello',
mounted () {
}
}
</script>
<style scoped>
</style>
路由高亮
在引用的页面下加入如下代码
.router-link-active{
/*设置路由激活状态下字体的颜色*/
color:hotpink; /*粉色*/
}
也可以在vue-cli搭建下的router/index.js中添加路由高亮的全局配置
import Vue from 'vue'
import Router from 'vue-router'
import hi from '../components/hi'
import nav from '../Nav'
import hello from '../components/hello'
Vue.use(Router)
export default new Router({
// 配置全局的路由激活状态样式
linkActiveClass: 'active', // active:激活状态下添加的class
routes: [
{
path: '/',
name: 'HelloWorld',
redirect: 'nav' // 重定向配置
},
{
path: '/nav',
name: 'nav',
component: nav,
children: [
{
path: '/hi',
name: 'hi',
component: hi
}
]
},
{
path: '/hello',
name: 'hello',
component: hello
}
]
})
路由设置单页面title
在main.js中添加如下代码
router.beforeEach((to, from, next) => {
/* 路由发生变化修改页面title */
if (to.meta.title) {
document.title = to.meta.title
}
next()
})
router > index.js
import Vue from 'vue'
import Router from 'vue-router'
import login from '@/page/login/login'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/login',
name: 'login',
component: login,
meta: {
title: '登录'
}
}
]
})
项目结构
访问页面路径http://localhost:8080/#/login,显示在router>index.js中配置的标题 登录
Vue中使用element-ui
按需引入
1、命令行切换到项目的根目录下
cd demo
2、输入cnpm i element-ui -S
3、输入cnpm install babel-plugin-component -D
4、修改 .babelrc
如下
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2"
],
"plugins": ["transform-vue-jsx", "transform-runtime", [
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]]
}
5、在项目的根目录下的src下的main.js中按需引入需要的样式
例如引入element-ui中的button,代码如下:
import { Button } from 'element-ui'
// 按需引入elementui
Vue.use(Button)
6、在需要的页面使用button
<template>
<div class="hello">
<!--使用element-ui中的button-->
<el-button>按钮</el-button>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
}
}
}
</script>
<style>
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
完整引入
在main.js中添加
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
完整代码如下:
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
// 完整引入element-ui
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
// 完整引入element-ui
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
引入成功之后,可按照 官网 介绍在要使用的页面进行元素的使用
表单的使用
基本用法
- el-form容器,通过model绑定数据
- el-form-item容器,通过label绑定标签
- 表单组件通过v-model绑定model中的数据
<template>
<div class="hello">
<!--inline:设置 inline 属性可以让表单域变为行内的表单域-->
<el-form inline :model="data">
<el-form-item label="审批人">
<el-input v-model="data.user" placeholder="审批人"></el-input>
</el-form-item>
<el-form-item>
<el-select v-model="data.region" placeholder="活动区域">
<el-option label="区域一" value="上海"></el-option>
<el-option label="区域二" value="北京"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">查询</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
data: {
user: 'sam',
region: '区域二'
}
}
},
methods: {
onSubmit () {
console.log(this.data)
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
页面显示
点击查询按钮,输出表单内容
表单校验
- 定义校验规则,可以绑定到el-form或el-form-item
<template>
<div class="hello">
<!--inline:设置 inline 属性可以让表单域变为行内的表单域
:rules:绑定校验规则
-->
<el-form inline :model="data" :rules="rules">
<!--prop:user,user等于data函数中声明的校验规则-->
<el-form-item label="审批人" prop="user">
<el-input v-model="data.user" placeholder="审批人"></el-input>
</el-form-item>
<el-form-item>
<el-select v-model="data.region" placeholder="活动区域">
<el-option label="区域一" value="上海"></el-option>
<el-option label="区域二" value="北京"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">查询</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
// 自定义校验规则
const userValidator = (rule, value, callback) => {
if (value.length > 3) {
callback()
} else {
callback(new Error('用户名长度必须大于3'))
}
}
return {
data: {
user: '',
region: ''
},
// 定义表单校验规则
rules: {
user: [
/* trigger:表单值发生改变时进行校验 */
{required: true, trigger: 'change', message: '用户名必须输入'},
{validator: userValidator, trigger: 'change'} // 自定义校验规则
]
}
}
},
methods: {
onSubmit () {
console.log(this.data)
if (!this.data.user) {
// 用户名为空给出提示信息
this.$message.error('用户名不能为空!')
} else if (this.data.user.length < 10) {
this.$message.info('用户名长度不能小于10个字符')
}
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
页面显示
elementui的表单校验
<template>
<div class="hello">
<!--inline:设置 inline 属性可以让表单域变为行内的表单域
:rules:绑定校验规则
-->
<el-form inline :model="data" :rules="rules" ref="form">
<!--prop:user,user等于data函数中声明的校验规则-->
<el-form-item label="审批人" prop="user">
<el-input v-model="data.user" placeholder="审批人"></el-input>
</el-form-item>
<el-form-item>
<el-select v-model="data.region" placeholder="活动区域">
<el-option label="区域一" value="上海"></el-option>
<el-option label="区域二" value="北京"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">查询</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
// 自定义校验规则
const userValidator = (rule, value, callback) => {
if (value.length > 3) {
callback()
} else {
callback(new Error('用户名长度必须大于3'))
}
}
return {
data: {
user: '',
region: ''
},
// 定义表单校验规则
rules: {
user: [
/* trigger:表单值发生改变时进行校验 */
{required: true, trigger: 'change', message: '用户名必须输入'},
{validator: userValidator, trigger: 'change'} // 自定义校验规则
]
}
}
},
methods: {
onSubmit () {
this.$refs.form.validate((isValid, errors) => {
// 控制台输出校验规则
console.log(isValid, errors)
})
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
页面显示
表单校验实例
<template>
<div class="login">
<div class="loginWrapper">
<div class="loginMain">
<div class="links">
<p class="accountUser">账户密码登录</p>
<p class="regUser">
<el-link :underline="false">
<router-link to="/register" tag="span" >注册用户</router-link>
</el-link>
</p>
</div>
<div class="submitForm">
<el-form ref="form" :model="form" style="width: 25%;margin: 0 auto;" :rules="rules">
<el-form-item style="margin-top: 25px;" prop="username">
<el-input v-model.trim="form.username" placeholder="账户" prefix-icon="el-icon-user" clearable></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input v-model.trim="form.password" placeholder="密码" prefix-icon="el-icon-lock" clearable type="password"></el-input>
</el-form-item>
<el-form-item >
<el-button type="primary" size="medium" style="width: 100%;" @click="login('form')">登录</el-button>
</el-form-item>
</el-form>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'login',
data () {
return {
form: {
username: '', // 账号
password: '' // 密码
},
rules: {
username: [{required: true, message: '请输入账号'}],
password: [{required: true, message: '请输入密码'}]
}
}
},
methods: {
login (formName) {
let that = this
this.$refs[formName].validate((valid) => {
if (valid) {
that.$router.push({path: '/'})
} else {
return false
}
})
}
}
}
</script>
<style scoped>
.login{
height: 100%;
background: url(/static/img/background.png);
position: fixed;
width: 100%;
background-size: 100% 100%;
}
.loginWrapper{
position: relative;
left: 0;
top: 0;
padding: 100px 0;
min-height: 100%;
box-sizing: border-box;
}
.loginMain{
margin: 0 auto;
box-sizing: border-box;
width: 80%;
}
.loginTitle{
text-align: center;
padding: 10px;
}
.cover{
padding: 130px 0;
}
.biaoti{
font-size: 26px;
color: rgba(0, 0, 0, 0.85);
font-weight: 700;
display: flex;
justify-content: center;
}
.accountUser{
display: inline-block;
font-size: 16px;
color: rgba(24, 144, 255, 1);
position: relative;
width: 19%;
}
.accountUser:after{
position: absolute;
width: 111px;
height: 2px;
background: rgba(24, 144, 255, 1);
content: '';
top: 24px;
left: -6px;
}
.regUser{
display: inline-block;
font-size: 16px;
}
.links{
display: flex;
justify-content: center;
margin: 15px auto;
}
.forgetPwd{
float: right;
padding: 5px;
margin-bottom: 10px;
}
</style>
el-menu
<template>
<div class="sidebarWrapper">
<div class="sideBar">
<el-menu
text-color="#FFF"
background-color="#002040"
:default-openeds="['10']"
active-text-color="#0089FF"
unique-opened
:default-active="$route.path"
@open="handleOpen"
@close="handleClose"
router
>
<el-submenu index="10" >
<template slot="title">
<span class="menuIcon"></span>
<span style="display: inline-block;vertical-align: middle">学习管理</span>
</template>
<el-menu-item class="menu-item is-active" index="/jobKind">
分类
</el-menu-item>
<el-menu-item class="menu-item" index="/jobManager">
<span>管理</span>
</el-menu-item>
</el-submenu>
</el-menu>
</div>
</div>
</template>
<script>
export default {
name: 'sideBar',
data () {
return {
}
},
methods: {
handleOpen (key, keyPath) {
},
handleClose (key, keyPath) {
}
}
}
</script>
<style>
.sidebarWrapper{
position: fixed;
top: 0px;
left: 0;
bottom: 0;
z-index: 1031;
width:190px;
background-color: #002040;
}
.logo {
margin: 10px auto;
width: 130px;
display: block;
}
.sideBar{
position: relative;
z-index: 1;
width: 190px;
height: 100%;
padding-bottom: 15px;
overflow-x: hidden;
overflow-y: auto;
}
.menu-item {
padding-left: 48px !important;
}
.el-menu{
border-right: 0;
}
.el-submenu__title i{
color: #FFF;
font-size: 22px;
vertical-align: middle;
}
.el-submenu__title{
font-size: 16px;
background-color: #3888E5!important;
}
.el-submenu__icon-arrow{
margin-top: 0px;
}
.menuIcon{
display: inline-block;
width: 15%;
height:45%;
background: url("../assets/gwxx.png") no-repeat;
background-size: 100%;
vertical-align: middle;
}
</style>
页面样式
重写element-ui中的Message,实现相同消息提示只弹一个
在main.js中添加如下代码
import ElementUI, {Message} from 'element-ui'
// 为了实现Class的私有属性
const showMessage = Symbol('showMessage')
/**
* 重写ElementUI的Message
* single默认值true,因为项目需求,默认只弹出一个,可以根据实际需要设置
*/
class DonMessage {
success (options, single = true) {
this[showMessage]('success', options, single)
}
warning (options, single = true) {
this[showMessage]('warning', options, single)
}
info (options, single = true) {
this[showMessage]('info', options, single)
}
error (options, single = true) {
this[showMessage]('error', options, single)
}
[showMessage] (type, options, single) {
if (single) {
// 判断是否已存在Message
if (document.getElementsByClassName('el-message').length === 0) {
Message[type](options)
}
} else {
Message[type](options)
}
}
}
Vue.prototype.$message = new DonMessage()
2.在组件中使用
this.$message.error({
message: '必填项不能为空',
type: 'error'
})
Vue-axios
- 从浏览器中创建 XMLHttpRequests
- 从 node.js 创建 http 请求
- 支持 Promise API
- 拦截请求和响应
- 转换请求数据和响应数据
- 取消请求
- 自动转换 JSON 数据
- 客户端支持防御 XSRF
使用
axios参考使用地址
1、安装
npm install axios --save
或者使用cnpm安装,命令如下
cnpm install axios --save
2、在src/main.js中添加如下代码
// 引入axios
import Axios from 'axios'
// 挂载到Vue的原型上
Vue.prototype.$axios = Axios
get请求
无参数get请求
//this.$axios.get(url) url:地址
this.$axios.get('http://www.baidu.com')
.then(res => {
//res.data 返回的响应数据
console.log(res.data)
})
.catch(error => {
//error 发送错误时返回的响应结果
console.log(error)
})
get请求传参
this.$axios.get('http://www.baidu.com', {
//params:传递的参数
params: {
wd: 'element'
}
})
.then(res => {
console.log(res.data)
})
.catch(error => {
console.log(error)
})
也可以写成如下get请求传参
axios的get请求
this.$axios({
method: 'get',
url: 'http://www.baidu.com',
params: {
wd: 'elment'
}
})
.then(res => {
console.log(res.data)
})
.catch(error => {
console.log(error)
})
post请求
// axios的post请求接收参数是form-data格式:?name='124'&age=10,后台无法接收,所以使用qs序列化参数
this.$axios.post('http://www.baidu.com', //url地址
{
wd: 'elment' //传递的参数
})
.then(res => {
console.log(res.data) //响应结果
}).catch(error => {
console.log(error)
})
关于Vue中,序列化字符串,处理发送请求的参数
使用工具qs来处理参数
使用qs
1、切换到项目根目录下执行
npm install qs --save-dev
命令安装qs
2、在main.js中进行导入qs,交给vue进行使用
// 引入qs
import qs from 'qs'
// qs挂载到Vue的原型上
Vue.prototype.$qs = qs
3、使用
在页面中使用this.$qs来调用
// axios的post请求接收参数是form-data格式:?wd='element'&age=20
this.$axios.post('http://www.baidu.com',
this.$qs.stringify({
wd: 'elment'
})
).then(function (response) {
console.log(response)
}).catch(function (error) {
console.log(error)
})
axios的post
this.$axios({
method: 'post',
url: 'http://www.baidu.com',
data: {
wd: 'element'
}
}).then(res => {
console.log(res.data)
}).catch(error => {
console.log(error)
})
拦截器
在请求或响应被 then 或 catch 处理前拦截它们。
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
跨域处理
在vue-cli项目下的config/index.js中配置跨域处理
'/proxy': {
target: 'http://tingapi.ting.baidu.com', //目标接口域名
changeOrigin: true, //是否跨域
pathRewrite: { //重写接口
'^/proxy': ''
}
}
封装axios中的请求
1.建立一个htttp.js
import axios from 'axios'
import qs from 'qs'
/**
* 封装post请求
* @param url 请求的地址
* @param data 请求的参数(form表单请求)
* @returns {Promise} 请求返回的结果
*/
export function post (url, data = {}) {
return new Promise((resolve, reject) => {
axios({
method: 'POST',
header: {'Content-Type': 'application/x-www-form-urlencoded'}, // 设置请求头
url,
data: qs.stringify(data)
}).then(response => {
resolve(response.data)
}, err => {
reject(err)
})
})
}
2.在main.js中引入
import {post} from './request/http'
// 定义全局变量
Vue.prototype.$post = post
3.直接使用封装好的请求
let url = that.globalUrl + 'user/update'
let data = {
name: that.registerForm.username, // 用户名
password: that.registerForm.password // 密码
}
that.$post(url, data).then(res => {
if (res.code === 200) {
// 跳转到首页
that.$router.push({path: '/login'})
} else {
that.$message.error(res.msg)
}
}).catch(err => {
console.log(err, 'err')
})
axios操作原生dom
通过给元素添加ref来进行操作原生的dom,然后通过$refs来获取元素
<template>
<div>
<p ref="t">23</p>
</div>
</template>
<script>
export default {
name: 'baidu',
created () {
},
mounted () {
let t = this.$refs.t
// 添加样式
t.style.color = 'red'
// 添加事件
t.addEventListener('click', function () {
console.log('干啥')
})
}
}
</script>
<style scoped>
</style>
使用jquery
切换到项目根目录,执行cnpm install --save jquery 来安装jquery
然后在需要使用的.vue结尾的文件中的script标签中引用
<template>
<div>
<p ref="t">哈哈</p>
<p :class="hello">哈哈</p>
<ul ref="ul">
<li>1</li>
<li>2</li>
</ul>
</div>
</template>
<script>
// 引入jquery
import $ from 'jquery'
export default {
name: 'test',
data () {
return {
hello: 'hello'
}
},
mounted () {
let t = this.$refs.t
// 使用jquery
$(t).css('color', 'red')
let ul = this.$refs.ul
$(ul).on('click', 'li', function (event) {
console.log($(this).html())
})
}
}
</script>
<style scoped>
.hello{
color: deepskyblue;
}
</style>
配置路径
import Vue from 'vue'
import Router from 'vue-router'
import baidu from '../components/baidu'
// 引入组件
import test from '../components/test'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'baidu',
component: baidu
},
// 配置路径
{
path: '/test',
name: 'test',
component: test
}
]
})
页面访问
http://localhost:8080/#/test
vue中使用echarts
安装命令
cnpm install echarts -S
或者
npm install echarts -S
在vue-cli搭建的vue项目中的main.js中注册echarts
import echarts from 'echarts'
Vue.prototype.echarts = echarts
图表在页面中的宽度和高度
<template>
<div id="app">
<!--指定图表的宽度和高度-->
<div id="v" :style="{width:'300px',height:'600px'}"></div>
</div>
</template>
初始化折线图
<template>
<div id="app">
<!--指定图表的宽度和高度-->
<div id="v" :style="{width:'300px',height:'600px'}"></div>
</div>
</template>
<script>
export default {
name: 'App',
mounted () {
this.drawLine()
},
methods: {
drawLine () {
let myChart = this.echarts.init(document.getElementById('v'))
// 绘制图表
myChart.setOption({
title: {text: '在vue中使用echarts'},
tooltip: {},
xAxis: {
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子']
},
yAxis: {},
series: [{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10]
}]
})
// 图表大小随着浏览器窗口变化而该改变
window.addEventListener('resize', function () {
myChart.resize()
})
}
}
}
</script>
<style>
</style>
效果图
使用Vant
1、先使用vue-cli搭建vue项目
2、使用cmd命令行切换到项目根目录执行以下代码
npm i vant -S
或者使用如下命令
cnpm i vant -S
3、导入所有组件
在vue-cli搭建的项目的src下的main.js中输入以下代码
import Vant from 'vant'
import 'vant/lib/index.css'
Vue.use(Vant)
4、在页面使用相应的组件
<template>
<div class="hello">
<van-button type="primary">按钮</van-button>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
5、浏览器访问
vue-cli使用vant官网介绍
h5 下拉刷新,上拉加载
下拉刷新
加载指定列表分页的第一页的数据
上拉加载
数据需要累加,默认加载第一页的列表分页后的数据,页面滑动到指定的位置,加载下一页的数据,滑动到底部,数据加载完成
使用vue的组件 vue-data-loading
github里vue-data-loading的描述
使用步骤
1、安装命令
使用cmd切换到项目的根目录下执行下列命令
cnpm install vue-data-loading --save
或者
npm install vue-data-loading --save
2、使用
引入组件
import VueDataLoading from 'vue-data-loading'
3、注册组件
components: {VueDataLoading}
4、在需要下拉加载的部分使用vue-data-loading 标签将数据包裹起来.
<!--列表数据的加载-->
<vue-data-loading>
<div class="tableList">
<div class="tableContent">
<ul>
<li v-for="(column,i) in tableDataCompany" :key="i" @click="goDetail(column)">
<div class="imgCover">
<el-image :src="column.imgcover" class="img"></el-image>
</div>
<div class="itemContent">
<span style="display: block;font-size: 16px;margin: 10px auto;">{{column.pname}}</span>
<div class="suitPeople">人群:<span>{{column.suitpeople}}</span></div>
<div class="suitPeople" style="font-size: 12px">
<span style="display: inline-block;vertical-align: middle">
<i class="stageIcon"></i> {{column.stage}}阶段
</span>
<span style="display: inline-block;vertical-align: middle;margin-left:10px;">
<i class="colleage"></i> {{column.curriculum}}课程
</span>
<span class="priceShow" style="font-size: 18px;display: inline-block;vertical-align: middle">
<span v-if="column.price == 0" style="color: #F5A724;">免费</span>
<span v-else style="color:red;">¥{{column.price}}</span>
</span>
</div>
</div>
</li>
</ul>
</div>
</div>
</vue-data-loading>
Vuex
官网介绍
共享组件之间的状态(数据),VUe的状态管理模式(可以做到状态共享)
Vuex流程
VueX的流程
Vuex统一管理状态的好处
- 在vuex中集中管理共享的数据,易于开发和后期维护
- 高效地实现组件之间地数据共享,提高开发效率
- 存储在vuex中地数据都是响应式地,能够实时保持数据与页面的同步
vuex使用场景
组件之间共享的数据,才存储到vuex中,对于组件中的私有数据,依旧存储在组件自身的data中即可
使用步骤
1.安装vuex依赖包
打开项目的根目录,打开cmd命令行输入
cnpm install vuex --save
或者
npm install vuex --save
2、导入vuex包
import Vuex from 'vuex'
Vue.use(Vuex)
3、创建store对象
const store = new Vuex.Store({
// state中存放的就是全局共享的数据
state:{
count:0
}
})
4、将store对象挂载到vue实例中
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
new Vue({
router,
// 将创建的共享数据对象,挂载到vue实例中
// 所有的组件,就可以直接从store中获取全局的数据了
store,
render: h => h(App) // 渲染根组件
}).$mount('#app') // $mount挂载到根元素
State
State提供唯一的公共数据源,所有共享的数据都要统一放到Store的State中进行存储
const store =new Vuex.Store({
state:{count:0}
})
组件访问State中数据
第一种方式:
// count:全局数据名称
this.$store.state.count
第二种方式
//1、从vuex中按需导入mapState函数
import {mapState} from 'vuex'
2、通过刚才导入的mapState函数,将当前组件需要的全局数据,映射为当前组件的computed计算属性
computed:{
...mapState(['count'])
}
源码
<template>
<div>
<h3>当前最新的count值为:{{count}}</h3>
<button>-1</button>
</div>
</template>
<script>
import {mapState} from 'vuex'
export default {
name: "Substraction",
computed:{
...mapState(['count'])
}
}
</script>
<style scoped>
</style>
页面效果
案例
1、main.js中注册store
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
new Vue({
router,
// 将创建的共享数据对象,挂载到vue实例中
// 所有的组件,就可以直接从store中获取全局的数据了
store,
render: h => h(App) // 渲染根组件
}).$mount('#app') // $mount挂载到根元素
2、store 中注入全局变量
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count:0
},
mutations: {
},
actions: {
},
modules: {
}
})
3、访问全局变量
<template>
<div>
<h3>当前最新的count值为:{{$store.state.count}}</h3>
<button>-1</button>
</div>
</template>
<script>
export default {
name: "Substraction"
}
</script>
<style scoped>
</style>
4、页面效果
5、项目格式
Mutation
Vuex中推荐使用Mutation变更Store中的数据
- 只能通过mutation变更Store数据,不可以直接操作Store中的数据
- 通过mutatioin虽然复杂,但可以集中监控所有数据的变化
使用
触发mutations的第一种方式
1、定义mutation
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count:0
},
mutations: {
// state形参代表当前的state
add(state){
// 变更状态
state.count++
}
},
actions: {
},
modules: {
}
})
2、在组件中使用
<template>
<div>
<h3>当前最新的count值:{{$store.state.count}}</h3>
<button @click="btnHandler1">+1</button>
</div>
</template>
<script>
export default {
name: "Adddtion",
data(){
return {
}
},
// 触发mutation
methods:{
btnHandler1(){
// 触发mutations的第一种方式
this.$store.commit('add')
}
}
}
</script>
<style scoped>
</style>
页面效果
mutation传递参数
可以在触发mutation时传递参数:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count:0
},
mutations: {
// 只有mutations中定义的函数,才能修改state中的数据
addCount(state,num){
// 变更状态
state.count +=num
}
},
actions: {
},
modules: {
}
})
在组件中调用
<template>
<div>
<h3>当前最新的count值:{{$store.state.count}}</h3>
<button @click="btnHandler1">+1</button>
</div>
</template>
<script>
export default {
name: "Adddtion",
data(){
return {
}
},
// 触发mutation
methods:{
btnHandler1(){
// 在调用commit函数
//触发mutation时携带参数
this.$store.commit('addCount',5)
}
}
}
</script>
<style scoped>
</style>
页面中点击按钮显示
触发mutations的第二种方式
// 1.从vuex中按需导入mapMutations函数
import {mapMutations} from 'vuex'
通过刚才导入的mapMutations函数,将需要的mutations函数,映射为当前组件的methods方法
//2.将指定的mutations函数,映射为当前组件的methods函数
...mapMutations(['addCount'])
3、在组件中调用
<template>
<div>
<h3>当前最新的count值为:{{count}}</h3>
<button @click="btnHandler2">+1</button>
</div>
</template>
<script>
import {mapMutations, mapState} from 'vuex'
export default {
name: "Substraction",
computed:{
...mapState(['count'])
},
methods:{
//2.将指定的mutations函数,映射为当前组件的methods函数
...mapMutations(['addCount']),
btnHandler2(){
// 通过this直接调用store的Mutation中的方法
this.addCount(12)
}
}
}
</script>
<style scoped>
</style>
页面点击按钮效果
mutations函数中,不执行异步操作
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count:0
},
mutations: {
// mutations函数中,不执行异步操作,所以异步操作使用action
add(state){
setTimeout(()=>{
state.count ++
},1000)
}
},
actions: {
},
modules: {
}
})
Action
- Action提交的是mutation,而不是直接变更状态
- Action用于处理异步操作,可以包含任意异步操作
- mutation只能做同步的处理
使用步骤
1、定义action
actions: {
// 定义action
addAsync(context){
// 在actions中,不能直接修改state中某个数据
// 必须通过context.commit()触发某个mutation才行
setTimeout(()=>{
context.commit('add')
},1000)
}
},
2、在组件中使用
<template>
<div>
<h3>当前最新的count值为:{{count}}</h3>
<button @click="btnHandler2">+1</button>
</div>
</template>
<script>
import {mapMutations, mapState} from 'vuex'
export default {
name: "Substraction",
computed:{
...mapState(['count'])
},
methods:{
//2.将指定的mutations函数,映射为当前组件的methods函数
...mapMutations(['addCount']),
btnHandler2(){
this.addCount(12)
},
handle(){
// 触发actions的第一种方式
// dispatch函数,专门用来触发action
this.$store.dispatch('addAsync')
}
}
}
</script>
<style scoped>
</style>
源码
<template>
<div>
<h3>当前最新的count值为:{{count}}</h3>
<button @click="btnHandler2">+1</button>
<button @click="handle">+1 Async</button>
</div>
</template>
<script>
import {mapMutations, mapState} from 'vuex'
export default {
name: "Substraction",
computed:{
...mapState(['count'])
},
methods:{
//2.将指定的mutations函数,映射为当前组件的methods函数
...mapMutations(['addCount']),
btnHandler2(){
this.addCount(12)
},
handle(){
// 触发actions的第一种方式
// dispatch函数,专门用来触发action
this.$store.dispatch('addAsync')
}
}
}
</script>
<style scoped>
</style>
点击页面+1 Async,count自动加1
触发actions异步任务时携带参数
1、定义action
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count:0
},
// 只有mutations中定义的函数,才能修改state中的数据
mutations: {
addN(state,step){
state.count+=step
}
},
actions: {
addNAsync(context,step){
setTimeout(()=>{
context.commit('addN',step)
},1000)
}
},
modules: {
}
})
2、在组件中调用
<template>
<div>
<button @click="handle">+1</button>
<div>{{count}}</div>
</div>
</template>
<script>
import {mapState} from "vuex";
export default {
name: "test",
computed:{
...mapState(['count'])
},
methods:{
handle(){
// 调用dispatch函数
// 触发actions时携带参数
this.$store.dispatch('addNAsync',5)
}
}
}
</script>
<style scoped>
</style>
页面效果
点击按钮之后,页面效果
action的第二种方式
this.$store.dispatch()是触发actions的第一种方式,触发actions的第二种方式:
步骤
// 1、从vuex中按需导入mapActions函数
import {mapActions} from 'vuex';
通过导入的mapActions函数,将需要的actions函数,映射为当前组件的methods方法
...mapActions(['addNAsync'])
源码
<template>
<div>
<button @click="handle">+1</button>
<button @click="btnHandle1">异步的+1</button>
<div>{{count}}</div>
</div>
</template>
<script>
// 从vuex中按需导入mapActions函数
import {mapState, mapActions} from 'vuex'
export default {
name: "test",
computed:{
...mapState(['count'])
},
methods:{
...mapActions(['addNAsync']),
handle(){
// 调用dispatch函数
// 触发actions时携带参数
this.$store.dispatch('addNAsync',5)
},
btnHandle1(){
this.addNAsync(5)
}
}
}
</script>
<style scoped>
</style>
页面效果
点击异步的+1按钮,页面效果
也可以直接调用mapActions的方法
源码如下
<template>
<div>
<button @click="handle">+1</button>
<button @click="addNAsync(5)">异步的+5</button>
<div>{{count}}</div>
</div>
</template>
<script>
import {mapState} from "vuex";
// 从vuex中按需导入mapActions函数
import {mapActions} from 'vuex';
export default {
name: "test",
computed:{
...mapState(['count'])
},
methods:{
...mapActions(['addNAsync']),
handle(){
// 调用dispatch函数
// 触发actions时携带参数
this.$store.dispatch('addNAsync',5)
}
}
}
</script>
<style scoped>
</style>
页面效果
点击异步的+5按钮之后,页面效果
Getter
Getter用于对Store中的数据进行加工处理形成新的数据
- Getter可以对Store中已有的数据加工处理之后形成新的数据,类似Vue的计算属性
- Store中数据发生变化,Getter的数据也会跟着变化
- 不修改state中的数据
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count:0
},
// 只有mutations中定义的函数,才能修改state中的数据
mutations: {
addCount(state,num){
// 变更状态
state.count +=num
},
add(state){
state.count ++
},
addN(state,step){
state.count+=step
}
},
actions: {
addNAsync(context,step){
setTimeout(()=>{
context.commit('addN',step)
},1000)
}
},
// 定义Getter
getters:{
showNum:state=>{
return state.count
}
},
modules: {
}
})
使用getters的第一种方式
this.$store.getters.showNum //(showNum:名称)
源码:
<template>
<div>
<button @click="handle">+1</button>
<button @click="addNAsync(5)">异步的+5</button>
<div>{{count}}</div>
<!--使用getters-->
<div>当前最新的值{{$store.getters.showNum}}</div>
</div>
</template>
<script>
import {mapState} from "vuex";
// 从vuex中按需导入mapActions函数
import {mapActions} from 'vuex';
export default {
name: "test",
data(){
return {
num:''
}
},
mounted() {
this.num = this.$store.getters.showNum
},
computed:{
...mapState(['count'])
},
methods:{
...mapActions(['addNAsync']),
handle(){
// 调用dispatch函数
// 触发actions时携带参数
this.$store.dispatch('addNAsync',5)
}
}
}
</script>
<style scoped>
</style>
页面效果
使用getters的第二种方式
import {mapGetters} from 'vuex';
computed:{
...mapGetters(['showNum'])
}
源码
<template>
<div>
<!--使用getters-->
<div>当前最新的值{{showNum}}</div>
</div>
</template>
<script>
import {mapGetters} from 'vuex';
export default {
name: "test",
computed:{
...mapGetters(['showNum'])
}
}
</script>
<style scoped>
</style>
页面效果
基于Vuex的案例
- 通过vue ui命令打开可视化面板
- 选择创建项目的位置为桌面
3、点击在此创建新项目按钮,
4、选择手动配置
5、配置路由和vuex
6、创建项目
7、安装依赖
安装axios
安装ant-design-vue
ant-design-vue官网介绍
8、在main.js中引入组件库
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
// 1.导入ant-design-vue组件库
import Antd from 'ant-design-vue'
// 2.导入组件库的样式
import 'ant-design-vue/dist/antd.css'
// 3.安装组件库
Vue.use(Antd)
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
页面原型
初始化页面代码
<template>
<div id="app">
<a-input placeholder="请输入任务" class="my_ipt" />
<a-button type="primary">添加事项</a-button>
<a-list bordered :dataSource="list" class="dt_list">
<a-list-item slot="renderItem" slot-scope="item">
<!-- 复选框 -->
<a-checkbox>{{item.info}}</a-checkbox>
<!-- 删除链接 -->
<a slot="actions">删除</a>
</a-list-item>
<!-- footer区域 -->
<div slot="footer" class="footer">
<!-- 未完成的任务个数 -->
<span>0条剩余</span>
<!-- 操作按钮 -->
<a-button-group>
<a-button type="primary">全部</a-button>
<a-button>未完成</a-button>
<a-button>已完成</a-button>
</a-button-group>
<!-- 把已经完成的任务清空 -->
<a>清除已完成</a>
</div>
</a-list>
</div>
</template>
<script>
export default {
name: 'app',
data () {
return {
list: [
{
id: 0,
info: 'Racing car sprays burning fuel into crowd.',
done: false
},
{ id: 1, info: 'Japanese princess to wed commoner.', done: false },
{
id: 2,
info: 'Australian walks 100km after outback crash.',
done: false
},
{ id: 3, info: 'Man charged over missing wedding girl.', done: false },
{ id: 4, info: 'Los Angeles battles huge wildfires.', done: false }
]
}
}
}
</script>
<style scoped>
#app {
padding: 10px;
}
.my_ipt {
width: 500px;
margin-right: 10px;
}
.dt_list {
width: 500px;
margin-top: 10px;
}
.footer {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>
初始化渲染数据
清空App.vue中list的数值
<template>
<div id="app">
<a-input placeholder="请输入任务" class="my_ipt" />
<a-button type="primary">添加事项</a-button>
<a-list bordered :dataSource="list" class="dt_list">
<a-list-item slot="renderItem" slot-scope="item">
<!-- 复选框 -->
<a-checkbox>{{item.info}}</a-checkbox>
<!-- 删除链接 -->
<a slot="actions">删除</a>
</a-list-item>
<!-- footer区域 -->
<div slot="footer" class="footer">
<!-- 未完成的任务个数 -->
<span>0条剩余</span>
<!-- 操作按钮 -->
<a-button-group>
<a-button type="primary">全部</a-button>
<a-button>未完成</a-button>
<a-button>已完成</a-button>
</a-button-group>
<!-- 把已经完成的任务清空 -->
<a>清除已完成</a>
</div>
</a-list>
</div>
</template>
<script>
export default {
name: 'app',
data () {
return {
list: [
]
}
}
}
</script>
<style scoped>
#app {
padding: 10px;
}
.my_ipt {
width: 500px;
margin-right: 10px;
}
.dt_list {
width: 500px;
margin-top: 10px;
}
.footer {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>
显示如下:
1、在public文件夹下新建个文件为list.json
[
{
"id": 0,
"info": "Racing car sprays burning fuel into crowd.",
"done": true
},
{ "id": 1, "info": "Japanese princess to wed commoner.", "done": false },
{
"id": 2,
"info": "Australian walks 100km after outback crash.",
"done": true
},
{ "id": 3, "info": "Man charged over missing wedding girl.", "done": false },
{ "id": 4, "info": "Los Angeles battles huge wildfires.", "done": false }
]
2、在store/index.js中引入axios加载数据
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
// 所有的任务列表
list: []
},
mutations: {
// 初始化数据
initList (state, list) {
state.list = list
}
},
actions: {
// 异步操作
// 获取列表数据
getList (context) {
axios.get('/list.json').then(({ data }) => {
console.log(data)
context.commit('initList', data)
})
}
},
modules: {
}
})
3、在App.vue中加载列表数据
<template>
<div id="app">
<a-input placeholder="请输入任务" class="my_ipt" />
<a-button type="primary">添加事项</a-button>
<a-list bordered :dataSource="list" class="dt_list">
<a-list-item slot="renderItem" slot-scope="item">
<!-- 复选框 -->
<a-checkbox>{{item.info}}</a-checkbox>
<!-- 删除链接 -->
<a slot="actions">删除</a>
</a-list-item>
<!-- footer区域 -->
<div slot="footer" class="footer">
<!-- 未完成的任务个数 -->
<span>0条剩余</span>
<!-- 操作按钮 -->
<a-button-group>
<a-button type="primary">全部</a-button>
<a-button>未完成</a-button>
<a-button>已完成</a-button>
</a-button-group>
<!-- 把已经完成的任务清空 -->
<a>清除已完成</a>
</div>
</a-list>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'app',
data () {
return {
}
},
computed: {
// ... 对象展开运算符引入state中的数据
...mapState(['list'])
},
created () {
// 触发actions
this.$store.dispatch('getList')
}
}
</script>
<style scoped>
#app {
padding: 10px;
}
.my_ipt {
width: 500px;
margin-right: 10px;
}
.dt_list {
width: 500px;
margin-top: 10px;
}
.footer {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>
4、页面样式展示以及控制台输出
完整代码
App.vue
<template>
<div id="app">
<a-input placeholder="请输入任务" class="my_ipt" :value="inputValue" @change="handleInputChange" />
<a-button type="primary" @click="addItemToList">添加事项</a-button>
<a-list bordered :dataSource="infolist" class="dt_list">
<a-list-item slot="renderItem" slot-scope="item">
<!-- 复选框,箭头函数:=> -->
<a-checkbox :checked="item.done" @change="(e) =>{
cbStatusChanged(e,item.id)
}">{{item.info}}</a-checkbox>
<!--也可以用如下写法-->
<!-- <a-checkbox :checked="item.done" @change="changeStatus($event,item)">{{item.info}}</a-checkbox>-->
<!-- 删除链接 -->
<a slot="actions" @click="removeItemById(item.id)">删除</a>
</a-list-item>
<!-- footer区域 -->
<div slot="footer" class="footer">
<!-- 未完成的任务个数 -->
<span>{{unDoneLength}}条剩余</span>
<!-- 操作按钮 -->
<a-button-group>
<a-button :type="viewKey==='all'?'primary':'default'" @click="changeList('all')">全部</a-button>
<a-button :type="viewKey==='undone'?'primary':'default'" @click="changeList('undone')">未完成</a-button>
<a-button :type="viewKey==='done'?'primary':'default'" @click="changeList('done')">已完成</a-button>
</a-button-group>
<!-- 把已经完成的任务清空 -->
<a @click="clean">清除已完成</a>
</div>
</a-list>
</div>
</template>
<script>
import { mapState, mapGetters } from 'vuex'
export default {
name: 'app',
data () {
return {
}
},
computed: {
// ... 对象展开运算符引入state中的数据
// ...mapState(['list', 'inputValue', 'viewKey']),
...mapState(['inputValue', 'viewKey']),
...mapGetters(['unDoneLength', 'infolist'])
},
created () {
// 触发actions
this.$store.dispatch('getList')
},
methods: {
// 修改页面上展示的列表的数据
changeList (key) {
this.$store.commit('changeViewKey', key)
},
// 清除已完成的任务
clean () {
this.$store.commit('cleanDone')
},
// 监听复选框选中状态变化事件
cbStatusChanged (e, id) {
// 通过e.target.checked可以接受到最新的选中状态
const param = {
id: id,
status: e.target.checked
}
this.$store.commit('changeStatus', param)
},
// 监听文本框内容变化
handleInputChange (e) {
this.$store.commit('setInputValue', e.target.value)
},
// 向列表中新增 item 项
addItemToList () {
if (this.inputValue.trim().length <= 0) {
return this.$message.warning('文本框内容不能为空!')
}
this.$store.commit('addItem')
},
// 根据Id删除对应的任务事项
removeItemById (id) {
this.$store.commit('removeItem', id)
}
}
}
</script>
<style scoped>
#app {
padding: 10px;
}
.my_ipt {
width: 500px;
margin-right: 10px;
}
.dt_list {
width: 500px;
margin-top: 10px;
}
.footer {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>
store.js
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
// 所有的任务列表
list: [],
// 文本输入框中的值
inputValue: 'AAA',
// 下一个Id
nextId: 5,
viewKey: 'all' // 视图的key值
},
mutations: {
// 初始化数据
initList (state, list) {
state.list = list
},
// 为 store 中的 inputValue 赋值
setInputValue (state, val) {
state.inputValue = val
},
// 添加列表项
addItem (state) {
const obj = {
id: state.nextId,
info: state.inputValue.trim(),
done: false
}
state.list.push(obj)
state.nextId++
state.inputValue = ''
},
removeItem (state, id) {
// 根据id查找对应的索引
const index = state.list.findIndex(x => x.id === id)
// 根据索引,删除对应的元素
if (index !== -1) {
state.list.splice(index, 1)
}
},
// 修改列表项的选中状态
changeStatus (state, param) {
const i = state.list.findIndex(x => x.id === param.id)
if (i !== -1) {
state.list[i].done = param.status
}
},
// 清除已完成的任务
cleanDone (state) {
state.list = state.list.filter(x => x.done === false)
},
// 修改视图的key
changeViewKey (state, key) {
state.viewKey = key
}
},
actions: {
// 异步操作
// 获取列表数据
getList (context) {
axios.get('/list.json').then(({ data }) => {
console.log(data)
context.commit('initList', data)
})
}
},
getters: {
// 统计未完成的任务条数
unDoneLength (state) {
return state.list.filter(x => x.done === false).length
},
// 获取list数据,根据视图主键过滤
infolist (state) {
if (state.viewKey === 'all') {
return state.list
}
if (state.viewKey === 'undone') {
return state.list.filter(x => !x.done)
}
if (state.viewKey === 'done') {
return state.list.filter(x => x.done)
}
return state.list
}
},
modules: {
}
})