官網(wǎng)
https://cn.vuejs.org/v2/guide/
一、單頁面Tab導航欄切換
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="vue.js"></script>
<style>
.tab{
position: relative;
}
ul{
width: 300px;
display: flex;
justify-content: space-between;
}
ul li{
border: 1px solid #000;
padding: 10px;
list-style: none;
}
ul li.active{
background-color: #ccc;
}
.tab div{
display: none;
width: 300px;
height: 300px;
}
.tab div.current{
display: block;
}
</style>
</head>
<body>
<div id="app">
<div class="tab">
<ul>
<!-- 初始化序號與當前點擊元素序號相等,添加類名 ,等價于 :style="{backgroundColor: currentIndex == index ? '#ccc': 'initial' }"-->
<li :class="currentIndex == index ? 'active':''" v-for="(item,index) in list" :key="item.id" @click="click(index)">{{ item.name }}</li>
</ul>
<div :class="currentIndex == index ? 'current':''" v-for="(item,index) in bg" :key="item.id" :style="'backgroundColor:' + item.color"></div>
</div>
</div>
</body>
<script>
new Vue ({
el: "#app",
data: {
currentIndex: 0,
// 初始化序號
list: [
{id:1,name:'張三'},
{id:2,name:'李四'},
{id:3,name:'王五'},
],
bg: [
{id:1,color:'red'},
{id:2,color:'blue'},
{id:3,color:'pink'},
]
},
methods:{
// 點擊當前導航欄元素并將當前點擊元素序號給初始化序號
click(i){
this.currentIndex = i;
}
}
})
</script>
</html>
補充案例:底部導航欄路由和圖片切換,同時使用router-link和@click點擊圖標2次才顯示,可使用props和$router.push解決
參考文獻:https://www.jb51.net/article/160601.htm
// 子組件
<template>
<ul class="tabbar-container">
<li class="tabbar-item" v-for="(item,index) in tabs" :key="index" @click="$router.push(item.path)">
<i class="tabbar-icon" :style="{backgroundImage: index == currentIndex ? 'url(' + item.reicon + ')' : 'url(' + item.icon + ')',backgroundRepeat: 'no-repeat',backgroundSize: '100%'}"></i>
{{ item.name }}
</li>
</ul>
</template>
<script>
export default {
name: "TabBar",
props:{
index: Number
},
data() {
return {
currentIndex: this.index,
tabs: [
{id:1,name:"首頁",active:true,icon:require("../../assets/images/icon/dibu-shouye.png"),reicon:require("../../assets/images/icon/dibu-shouye2.png"),path:"/home"},
{id:2,name:"分類",active:false,icon:require("../../assets/images/icon/dibu-fenlei.png"),reicon:require("../../assets/images/icon/dibu-fenlei2.png"),path:"/type"},
{id:3,name:"購物車",active:false,icon:require("../../assets/images/icon/dibu-gouwuche.png"),reicon:require("../../assets/images/icon/dibu-gouwuche2.png"),path:"/shopcar"},
{id:4,name:"消息",active:false,icon:require("../../assets/images/icon/dibu-xiaoxi.png"),reicon:require("../../assets/images/icon/dibu-xiaoxi2.png"),path:"/message"},
{id:5,name:"個人中心",active:false,icon:require("../../assets/images/icon/dibu-gerenzhongxin.png"),reicon:require("../../assets/images/icon/dibu-gerenzhongxin2.png"),path:"/my"}
]
}
}
}
</script>
// 父組件
<TabBar class="tabbar" :index='0'></TabBar>
二、模擬后端接口,驗證用戶名是否可用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="vue.js"></script>
<style>
</style>
</head>
<body>
<div id="app">
<!-- lazy:失去焦點后觸發(fā) -->
用戶名:<input type="text" v-model.lazy="username">{{ tip }}
</div>
</body>
<script>
/*步驟:
1、采用偵聽器監(jiān)聽用戶名的變化
2、調(diào)用后臺接口進行驗證
3、根據(jù)驗證的結(jié)果調(diào)整提示信息
*/
new Vue ({
el: "#app",
data: {
currentIndex: 0,
username: "",
tip: ""
},
methods:{
checkName(username){
// 模擬接口調(diào)用
setTimeout(() => {
if (username == "admin") {
this.tip = "用戶名已存在,請更換一個"
}else{
this.tip = "用戶名可以使用"
}
},2000)
}
},
watch: {
// 屬性名與方法名必須一致
username(val){
// 調(diào)用后臺接口驗證用戶名的合法性
this.checkName(val);
// 修改提示信息
this.tip = "正在驗證……"
}
}
})
</script>
</html>
三、購物車(父子組件傳值)
插槽:https://cn.vuejs.org/v2/guide/components-slots.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="vue.js"></script>
<style>
.cart-title,.cart-total{
width: 50%;
border: 1px solid #000;
padding: 10px;
box-sizing: border-box;
}
.cart-title{
margin: 0;
border-bottom: 0;
text-align: center;
}
.cart-total{
border-top: 0;
display: flex;
justify-content: space-between;
}
.cart-list{
width: 50%;
text-align: center;
border-collapse: collapse;
}
.cart-list .totalSelect{
margin-top: -10px;
}
.cart-list .btn-group .btn-num{
width: 20px;
text-align: center;
}
</style>
</head>
<body>
<div id="app">
<h3 class="cart-title">我的商品</h3>
<!-- 綁定屬性或方法,前后名稱可以不一致 -->
<car-list :list="cart_list" @del-good="delGood" @change-num="changeNum"></car-list>
<car-total :list="cart_list"></car-total>
</div>
<template id="carList">
<table class="cart-list" border="1" cellpadding="10" cellspacing="10">
<tr>
<th>商品名稱</th>
<th>商品價格</th>
<th>數(shù)量</th>
<th>操作</th>
</tr>
<tr v-for="(item,index) in list" :key="index">
<td>{{ item.goods_name }}</td>
<td>{{ item.goods_price }}</td>
<td class="btn-group">
<span class="btn btn-reduce" @click="reduce(index)">-</span>
<input class="btn-num" type="text" v-model="item.num" @blur="change(index,$event)"/>
<span class="btn btn-add" @click="add(index)">+</span>
</td>
<td>
<button class="delete" @click="del(index)">刪除</button>
</td>
</tr>
</table>
</template>
<template id="carTotal">
<div class="cart-total">
<div class="total-pay">
總計:共 {{ list.length }} 件商品,
合計:{{ totalPrice }} 元
</div>
<button class="total-pay-success">去結(jié)算</button>
</div>
</template>
</body>
<script>
new Vue ({
el: "#app",
data: {
cart_list: [{
goods_name: '小米6',
goods_price: '1699',
num: '2',
}, {
goods_name: '紅米3',
goods_price: '699',
num: '1',
}, {
goods_name: '小米8',
goods_price: '2899',
num: '1',
}],
},
methods:{
// 根據(jù)子組件傳過來的值中的type,改變數(shù)量輸入框的值
changeNum(val){
// console.log(val)
if (val.type == 'change') {
this.cart_list[val.id].num = val.num;
}else if (val.type == 'reduce') {
if (this.cart_list[val.id].num <= 0) {
this.cart_list[val.id].num = 0;
}else{
this.cart_list[val.id].num--;
}
}else{
this.cart_list[val.id].num++;
}
},
// 接收子組件傳過來的當前元素的id,刪除當前點擊元素
delGood(id){
this.cart_list.splice(id,1);
},
},
components: {
carList: {
template: '#carList',
data(){
return{
}
},
methods: {
// 將當前已減少的輸入框數(shù)量傳給父組件
reduce(id){
this.emit('change-num',id,'reduce')
},
// 將當前已增加的輸入框數(shù)量傳給父組件
add(id){
this.emit('change-num',id,'add')
},
// 將當前已改變的輸入框數(shù)量傳給父組件
change(id,event){
this.emit('change-num',id,'change',event.target.value)
},
// 將當前點擊元素id傳給父組件
del(id){
this.emit('del-good',id)
},
// 封裝子組件傳值父組件
emit(){
// console.log(arguments)
if (arguments.length > 2) {
this.$emit(arguments[0],{
id: arguments[1],
type: arguments[2],
num: arguments[3]
})
}else{
this.$emit(arguments[0],arguments[1])
}
}
},
// 接收父組件傳給子組件的值
props: ['list']
},
carTotal: {
template: '#carTotal',
computed: {
// 計算總價
totalPrice(){
let total_price = 0;
this.list.forEach(item => {
total_price += Number(item.goods_price) * Number(item.num);
})
return total_price;
}
},
props: ['list']
}
}
})
</script>
</html>
補充:兄弟組件傳值
https://www.cnblogs.com/rich23/p/7110409.html
五、異步操作
promise和ajax使用
query(url){
let p = new Promise((resolve,reject) => {
Ajax('get',url,null,success,failed);
// 返回成功數(shù)據(jù)
function success(jsondata){
let json = JSON.parse(jsondata);
resolve(json);
}
// 返回失敗數(shù)據(jù)
function failed(){
reject('服務器錯誤');
}
});
return p;
};
query('http://localhost:3000/data')
.then(res => {
// 返回成功數(shù)據(jù)
console.log(res);
// 返回下一次異步調(diào)用數(shù)據(jù)
return query('http://localhost:3000/data2');
},rej => {
// 返回失敗數(shù)據(jù)
console.log(rej);
})
.then(res => {
console.log(res);
return query('http://localhost:3000/data3');
},rej => {
console.log(rej);
})
.then(res => {
console.log(res);
},rej => {
console.log(rej);
})
// Ajax封裝
function Ajax(type, url, data, success, failed){
// 創(chuàng)建ajax對象
let xhr = null;
if(window.XMLHttpRequest){
xhr = new XMLHttpRequest();
} else {
xhr = new ActiveXObject('Microsoft.XMLHTTP')
}
//轉(zhuǎn)化為大寫
type = type.toUpperCase();
// 用于清除緩存,防止瀏覽器緩存
let random = Math.random();
// 字符串拼接傳過來的數(shù)據(jù),如'name=zhangsan&'
if(typeof data == 'object'){
var str = '';
for(var key in data){
str += key+'='+data[key]+'&';
}
data = str.replace(/&$/, '');
}
// 判斷使用方法
if(type == 'GET'){
// 根據(jù)是否傳入數(shù)據(jù)處理參數(shù)顯示
if(data){
xhr.open('GET', url + '?' + data, true);
} else {
xhr.open('GET', url + '?t=' + random, true);
}
xhr.send(null);
} else if(type == 'POST'){
xhr.open('POST', url, true);
// 如果需要像html表單那樣POST數(shù)據(jù),請使用setRequestHeader() 來添加http頭
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send(data);
}
// 檢測狀態(tài),處理返回數(shù)據(jù)
xhr.onreadystatechange = function(){
//檢測是否已經(jīng)準備好
if(xhr.readyState == 4){
//表示響應準備就緒
if((xhr.status >= 200&&xhr.status<300)||xhr.status==304){
//請求成功之后的處理
success(xhr.responseText);
} else {
//處理ajax返回異常的情形
if(failed){
failed(xhr.status);
}
}
}
}
}
fetch使用
https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch
axios使用
post報錯400:https://www.cnblogs.com/chenlw/p/9994891.html
傳參:https://blog.csdn.net/zhaofuqiangmycomm/article/details/89479904
官方文檔:https://www.kancloud.cn/yunye/axios/234846
vue:https://hacpai.com/article/1567922774522
封裝:https://www.cnblogs.com/panax/p/10942889.html
https://www.cnblogs.com/chaoyuehedy/p/9931146.html
async/await使用
https://segmentfault.com/a/1190000007535316
四、后臺管理路由
vue-router使用
https://router.vuejs.org/zh/installation.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="vue.js"></script>
<script src="vue-router.js"></script>
<style>
*{
padding: 0;
margin: 0;
}
html,body,#app,.wrapper{
height: 100%;
}
ul{
list-style: none;
}
a{
text-decoration: none;
color: #fff;
}
.wrapper{
display: flex;
flex-direction: column;
justify-content: space-between;
}
.header,.footer,.main .left{
background-color: #666;
color: #fff;
}
.header,.footer,.main .left ul li{
text-align: center;
padding: 10px 0;
}
.main{
height: 100%;
display: flex;
justify-content: space-between;
}
.main .left{
width: 20%;
}
.main .left ul li{
border-bottom: 1px solid #fff;
background-color: #ccc;
}
.main .right{
flex: 1;
text-align: center;
}
.main .right h3{
padding: 10px 0;
}
.main .right table{
border-collapse: collapse;
width: 60%;
margin: 0 auto;
}
.main .right table td,.main .right table th{
padding: 10px 0;
}
.main .right table a{
color: #000;
}
</style>
</head>
<body>
<!-- 要被vue實例控制的區(qū)域 -->
<div id="app">
<router-view></router-view>
</div>
</body>
<script>
// 定義app根組件
const App = {
template: `
<div class="wrapper">
<header class="header">后臺管理系統(tǒng)</header>
<div class="main">
<div class="left">
<ul>
<li>
<router-link to="/users">用戶管理</router-link>
</li>
<li>
<router-link to="/rights">權(quán)限管理</router-link>
</li>
<li>
<router-link to="/goods">商品管理</router-link>
</li>
<li>
<router-link to="/orders">訂單管理</router-link>
</li>
<li>
<router-link to="/settings">用戶管理</router-link>
</li>
</ul>
</div>
<div class="right">
<router-view></router-view>
</div>
</div>
<footer class="footer">版權(quán)信息</footer>
</div>
`
}
const Users = {
data(){
return{
list: [
{id:1,name:"張三",age: 10},
{id:2,name:"李四",age: 20},
{id:3,name:"王五",age: 30},
]
}
},
methods: {
goDetail(id){
this.$router.push('/userinfo/' + id)
}
},
template: `
<div>
<h3>用戶管理區(qū)域</h3>
<table border="1">
<thead>
<tr>
<th>編號</th>
<th>姓名</th>
<th>年齡</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(item,index) in list" :key="item.id">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ item.age }}</td>
<td>
<a href="javascript:;" @click="goDetail(item.id)">詳情</a>
</td>
</tr>
</tbody>
</table>
</div>
`
}
const UserInfo = {
// 接收參數(shù)
props: ['id'],
methods: {
// 返回
goBack(){
this.$router.go(-1)
}
},
template: `
<div>
<h5>用戶詳情頁-{{ id }}</h5>
<button @click="goBack()">后退</button>
</div>
`
}
const Rights = {
template: `
<div>
<h3>權(quán)限管理區(qū)域</h3>
</div>
`
}
const Goods = {
template: `
<div>
<h3>商品管理區(qū)域</h3>
</div>
`
}
const Orders = {
template: `
<div>
<h3>訂單管理區(qū)域</h3>
</div>
`
}
const Settings = {
template: `
<div>
<h3>系統(tǒng)管理區(qū)域</h3>
</div>
`
}
// 創(chuàng)建路由對象
const router = new VueRouter({
routes: [
{
path: '/',
redirect: '/users',
component: App,
children: [
{
path: '/users',
component: Users
},
{
// 傳參id
path: '/userinfo/:id',
component: UserInfo,
props: true
},
{
path: '/rights',
component: Rights
},
{
path: '/goods',
component: Goods
},
{
path: '/orders',
component: Orders
},
{
path: '/settings',
component: Settings
}
]
}
]
})
new Vue({
el: "#app",
router
})
</script>
</html>
五、登錄
token:https://blog.csdn.net/c880420/article/details/80346127
完整案例:https://www.cnblogs.com/web-record/p/9876916.html
// 根目錄下vue.config.js 解決跨域
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:3000/api',
ws: true,
changeOrigin: true,
pathRewrite:{
'^/api': ''
}
},
}
}
}
// main.js
import axios from 'axios'
Vue.prototype.$http = axios
// 添加請求頭
axios.interceptors.request.use(config => {
config.headers.Authorization = window.sessionStorage.getItem('token');
return config;
})
router.beforeEach((to,from,next) => {
// 登錄頁無需權(quán)限
if (to.path == '/login') {
next()
}else{
// 獲取token
const token = window.sessionStorage.getItem('token');
// token不存在直接跳轉(zhuǎn)登錄頁
if(!token) return next('/login');
// 存在放行
next();
}
})
// login.vue
this.$http.post('/api/login',data).then(res => {
if(res.status !== 200){
// element-ui彈窗
this.$message.error('登錄失敗');
}
this.$message.success('登錄成功');
// 緩存token
window.sessionStorage.setItem('token',res.data.token);
this.$router.push('/home')
})
// 退出,清緩存
window.sessionStorage.clear();
this.$router.push('/login');
六、增刪改查
調(diào)用后端接口實現(xiàn)圖書管理增刪改查
https://github.com/mycummity/book
實現(xiàn)功能
調(diào)用后端接口,實現(xiàn)數(shù)據(jù)庫增刪改查并將結(jié)果返回給前端
filter過濾器進行關鍵字搜索和格式化日期
watch監(jiān)聽數(shù)據(jù)變化
created初始化數(shù)據(jù)
directives自定義獲取焦點
computed計算數(shù)據(jù)
keydown設置快捷鍵
響應式布局
http://www.itdecent.cn/p/6e77c838ab71
https://www.cnblogs.com/wgl0126/p/9468804.html
https://www.cnblogs.com/baiyygynui/p/5903749.html錨鏈接
navArray: [
{
href: "#navTitle1",
name: "應用簡介"
}
]
<div
class="navItem"
:class="{'selected':currentIndex==index}"
v-for="(item,index) in navArray"
:key="index"
@click="goAnchor(item.href)"
>
<span>{{item.name}}</span>
</div>
<div class="appSketch" id="navTitle1">
</div>
goAnchor(type) {
var anchor = this.$el.querySelector(type);
// chrome
document.body.scrollTop = anchor.offsetTop - 50;
// firefox
document.documentElement.scrollTop = anchor.offsetTop - 50;
}