本文基于工作项目开发,做的整理笔记
前段时间公司有的小伙伴刚开始学习vue,就直接着手用在新项目上,以项目实战步步为营,不断推进vue的学习和使用。时间短,需求多,又是刚刚上手,遇到的坑和困难也真不少,感觉每天都在疯狂地解决问题。说真的,每种技术的学习和使用,在实际项目的开发上得到了充分检验,个人能力也在快速的成长。
前一段时间,写过文章“Vue教程--使用官方脚手架构建实例”,主要是针对PC端,架构而写。当初的目的,也是想做为一个入门的教程,但是根据反馈和自己后面的感受,发现并不是很好,并没有做到真正的一步步上手。
今天决定专门针对Wap端去做这样一个demo,整体架构的搭建,并含有一些通用的功能。其中,部分知识点请回看前面那篇文章。对比来看,此篇应该更为详细,步步为营。
前提条件:
你已经了解vue的基础知识,尝试过使用vue-cli官方脚手架搭建项目。
编码环境:
system:OS X EI Capitan 10.12.5
npm:5.4.2
node:v8.8.0
vue-cli:@lastest
相关技术栈:
vue2 + vuex + vue-router + webpack + ES6/7 + fetch/axios + sass + flex + svg
相关地址:
项目代码github地址:https://github.com/YuxinChou/vue-wap-demo
项目在线地址:http://www.knowing365.com
(可用手机扫描下文中二维码,或用chrome浏览器模拟手机访问)
参考项目:https://github.com/bailicangdu/vue2-elm
目录
| - 0.传送门
| - 1.安装
| - 2.项目说明
| - 3.项目搭建
| - Step1. 初始化
| - Step2. 母版页Layout
| - Step3. 配置rem
| - Step4. 配置sass
| - Step5. 顶部导航header
| - Step6. 引入iconfont
| - Step7. 侧边菜单sidebar
| - Step8. 底部导航footer
| - Step9. 返回顶部backToTop(组件)
| - Step10. 仓库存储store
| - Step11. 侧边菜单状态保存
| - Step12. 搜索栏searchBar(组件)
| - Step13. 页面添加
| - Step14. 弹窗提示(组件)
| - ---------------------------------- 下内容为详解2
| - Step15. 完善login页面(fetch请求数据)
| - Step16. 合理引入svg
| - Step17. 用axios实现请求(取代原生fetch)
| - Step18. 登录状态存入仓库
| - Step19. 滚动加载更多(组件)
| - Step20. 回到指定位置(组件)
| - Step21. 完善消息列表页面
| - Step22. 顶部菜单改造(slot的使用)
| - --------------------------------- 下内容为详解3
| - Step23. 完善其他页面
| - Step24. 权限检查
| - Step25. 页面切换动画transition
| - Step26. 轮播展示(swiper)
| - Step26. 分享功能(vue-social-share)
| - Step28. ...
| - 4.项目部署
| - a)本地部署
| - b)服务器部署
| - 5.后续
0.传送门
官网“起步”传送门:http://cn.vuejs.org/v2/guide/#起步
1.安装
你已经安装了npm,它是随node.js安装的,装了node.js也就有了它。
node.js安装下载地址:http://nodejs.cn/download/
# 检查node.js是否安装,若有则显示版本号
$ node -v
# 检查npm的版本号
$ npm -v
# 若要更新npm,使用
$ npm install npm@latest -g
一般,我们还会安装淘宝镜像cnpm,因为在墙内,有时候使用npm安装会很慢,所以需要。
# 安装cnpm,并指定镜像地址
$ npm install -g cnpm --registry=https://registry.npm.taobao.org
写文章时,我的目前版本如下:
Yuxin's MacBook Pro:Vue yuxin$ node -v
v8.8.0
Yuxin's MacBook Pro:Vue yuxin$ npm -v
5.4.2
Yuxin's MacBook Pro:Vue yuxin$ cnpm -v
cnpm@4.5.0 (/usr/local/lib/node_modules/cnpm/parse_argv.js)
npm@3.10.10 (/usr/local/lib/node_modules/cnpm/node_modules/npm/lib/npm.js)
node@8.8.0 (/usr/local/bin/node)
npminstall@2.29.1 (/usr/local/lib/node_modules/cnpm/node_modules/npminstall/lib/index.js)
prefix=/usr/local
darwin x64 16.6.0
registry=https://registry.npm.taobao.org
vue的安装:
# 最新稳定版
$ npm install vue
# 它的命令行工具
$ npm install --global vue-cli
我们接下来就使用它的命令行工具 vue-cli 来构建项目。
(类似的还有在react.js方面,快速上手会使用到‘蚂蚁金服’的Ant-design,使用antd-init或者dva-cli,都是命令行构建项目。)
2.项目说明
以【QQ手机APP】为UI界面参考,从母版页,列表页,左侧菜单,上下菜单,顶部导航,滚动加载,权限等页面及功能一一按步骤开发完成。那么这里的思路是:
- 先搭建母版页Layout
- 顶部导航header
- 侧边菜单sidebar
- 上下菜单navbar(扩展)
- 底部导航footer
- 页面添加
- 功能(返回顶部,滚动加载...)
- 接口请求
- 仓库存储
- 权限检查
- ...
整体的页面效果,大概如下:
二维码访问,或者访问线上地址
(注:未作PC兼容,请使用chrome手机模式访问)
项目demo代码:GitHub地址
3.项目搭建
使用命令行工具,开始一点点构建项目:
Step1. 初始化
去到你的指定目录,初始化项目,名称为vue-wap-demo
(注意:ESLint那里我选择了None模式,为了简单)
# 去到放项目的文件夹地址
Yuxin's MacBook Pro:Vue yuxin$ cd /Users/yuxin/Documents/Season/Project/Vue
# 创建基于webpack模版的项目,名称为vue-wap-demo
Yuxin's MacBook Pro:Vue yuxin$ vue init webpack vue-wap-demo
? Project name vue-wap-demo //直接回车(不修改的话)
? Project description A Vue.js project //直接回车
? Author Season <yuxin0721@gmail.com> //直接回车
? Vue build standalone //直接回车
? Install vue-router? Yes //我选择了Yes
? Use ESLint to lint your code? Yes //我选择了Yes
? Pick an ESLint preset none //我选择了None模式!!!(注意:这里我用了None,为了简单)
? Setup unit tests with Karma + Mocha? No //我选择了No(暂时不需要)
? Setup e2e tests with Nightwatch? No //我选择了No(暂时不需要)
vue-cli · Generated "vue-wap-demo".
To get started:
cd vue-wap-demo
npm install
npm run dev
Documentation can be found at https://vuejs-templates.github.io/webpack
执行上面提示中的命令,把项目运行起来
# 进入项目文件夹
Yuxin's MacBook Pro:Vue yuxin$ cd vue-wap-demo
# 安装
Yuxin's MacBook Pro:vue-wap-demo yuxin$ npm install
> fsevents@1.1.2 install /Users/yuxin/Documents/Season/Project/Vue/vue-wap-demo/node_modules/fsevents
> node install
[fsevents] Success: "/Users/yuxin/Documents/Season/Project/Vue/vue-wap-demo/node_modules/fsevents/lib/binding/Release/node-v57-darwin-x64/fse.node" already installed
Pass --update-binary to reinstall or --build-from-source to recompile
> uglifyjs-webpack-plugin@0.4.6 postinstall /Users/yuxin/Documents/Season/Project/Vue/vue-wap-demo/node_modules/uglifyjs-webpack-plugin
> node lib/post_install.js
npm notice created a lockfile as package-lock.json. You should commit this file.
added 1059 packages in 26.516s
# 运行
Yuxin's MacBook Pro:vue-wap-demo yuxin$ npm run dev
> vue-wap-demo@1.0.0 dev /Users/yuxin/Documents/Season/Project/Vue/vue-wap-demo
> node build/dev-server.js
> Starting dev server...
DONE Compiled successfully in 2324ms 14:38:51
浏览器查看,效果如下:
此时,项目代码结构如下:
# 当前代码结构
vue-wap-demo
├── build // 构建相关
├── config // 配置相关
├── node_modules // 模块安装的文件夹
├── src // 核心代码
│ ├── assets // 静态资源
│ ├── components // 组件
│ ├── router // 路由
│ ├── App.vue // 入口页面
│ └── main.js // 入口 加载组件 初始化等
├── static // 第三方不打包资源
├── .babelrc // babel-loader 配置
├── .editorconfig // 代码编辑 配置项
├── .eslintignore // eslint 忽略项
├── .eslintrc.js // eslint 配置项
├── .gitignore // git 忽略项
├── favicon.ico // favicon图标
├── index.html // html模板
├── package-lock.json // package-lock.json
├── package.json // package.json
└── �README.md // 说明文档
# 注意!!!
# 下面是教程结束时的代码结构(可忽视)
vue-wap-demo
├── build // 构建相关
├── config // 配置相关
├── dist // 打包的部署文件
├── node_modules // 模块安装的文件夹
├── screenshots // 项目截图
├── src // 核心代码
│ ├── assets // 静态资源
│ ├── components // 组件
│ ├── page // 页面
│ ├── router // 路由
│ ├── service // 请求服务
│ ├── store // 仓库存储
│ ├── style // 样式
│ ├── utils // 公用方法
│ ├── App.vue // 入口页面
│ └── main.js // 入口 加载组件 初始化等
├── static // 第三方不打包资源
├── .babelrc // babel-loader 配置
├── .editorconfig // 代码编辑 配置项
├── .eslintignore // eslint 忽略项
├── .eslintrc.js // eslint 配置项
├── .gitignore // git 忽略项
├── favicon.ico // favicon图标
├── index.html // html模板
├── package-lock.json // package-lock.json
├── package.json // package.json
└── �README.md // 说明文档
Step2. 母版页Layout
初始化后,在是src/components
路径下默认有一个HelloWorld.vue
页面,我们将其改名为Layout.vue
作为母版页,调整一下内容,如下:
/**********************************************/
/* src/components/Layout.vue */
/**********************************************/
<template>
<div class="app_wrapper">
<!-- main -->
<div class="main_wrapper">
<h1 class="title">{{ msg }}</h1>
</div>
</div>
</template>
<script>
export default {
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
}
}
</script>
<style scoped>
/*layout*/
/*此时rem还没引入(Step3)*/
/*此时sass也还不能用(Step4)*/
.app_wrapper {
background-color: #f2f2f2;
}
.main_wrapper .title {
font-size: 1rem;
color: #f00;
}
</style>
同时需要修改router/index.js
的内容,把HelloWorld
的相关引用改为Layout
,如下:
/**********************************************/
/* src/router/index.js */
/**********************************************/
import Vue from 'vue'
import Router from 'vue-router'
import Layout from '@/components/Layout'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Layout',
component: Layout
}
]
})
现在我们可以看见修改后的效果。(开发的时候,一直保持项目运行就可以了)
Step3. 配置rem
在src
文件夹下创建utils
文件夹,并添加文件rem.js
,代码如下:
/**********************************************/
/* src/utils/rem.js */
/**********************************************/
(function(doc, win) {
var docEl = doc.documentElement,
resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
recalc = function() {
var clientWidth = docEl.clientWidth;
if (!clientWidth) return;
docEl.style.fontSize = 20 * (clientWidth / 320) + 'px';
};
if (!doc.addEventListener) return;
win.addEventListener(resizeEvt, recalc, false);
doc.addEventListener('DOMContentLoaded', recalc, false);
})(document, window);
// 注解:
// 设计稿为320的时候,1rem=20px
// 设计稿为640的时候,1rem=40px
// 设计稿宽768的时候,1rem=48px
将rem.js
引入到src\main.js
中,即插入代码import './utils/rem'
,最终代码如下:
/**********************************************/
/* 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'
import './utils/rem'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
template: '<App/>',
components: { App }
})
此时,再看运行后的效果,使用chrome手机模式预览,可以发现rem已经生效。
Step4. 配置sass
整个项目过程中我们将使用sass,但是目前还没有配置。我们先修改Layout.vue
,将它的style改成sass写法,如下:
/**********************************************/
/* src/components/Layout.vue */
/**********************************************/
...
<style lang="scss" scoped>
/*layout*/
.app_wrapper {
background-color: #f2f2f2;
.main_wrapper {
.title {
font-size: 1rem;
color: #f00;
}
}
}
</style>
// 注解:
// 仅修改style这里,其他代码不变
// 注意添加lang="scss"
保存修改之后,我们可以看到终端报错。根据终端显示的错误信息和安装提示,我们可以一步步解决这个问题。
# 按提示安装sass-loader
$ npm install sass-loader 横线横线save(简书双横线自动连起来了)
# 按提示安装node-sass
$ npm install node-sass 横线横线save(简书双横线自动连起来了)
再启动运行项目,没有报错,sass已经可以正常使用了。那么我们可能会疑问,为什么安装缺失的模块之后就可以用了呢?答案就在build
文件夹下的配置中,想研究一下的可以去看看其中的webpack.base.conf.js
和utilsl.js
两个文件。
Step5. 顶部导航header
在src/components
中创建header
文件夹,并创建head.vue
,其代码如下:
/**********************************************/
/* src/components/header/head.vue */
/**********************************************/
<template>
<header id="head" class='header'>
<span class="head_toggle" @click="toggleSideBar"><i class="iconfont icon-category"></i></span>
<span class="head_logo">logo</span>
</header>
</template>
<script>
export default {
data () {
return {
sidebar: false,
}
},
methods: {
toggleSideBar() {
this.sidebar = !this.sidebar;
console.log("sidebar current value: " + this.sidebar);
},
},
}
</script>
<style lang="scss" scoped>
/*header*/
.header {
background-color: #3190e8;
position: fixed;
z-index: 10;
left: 0;
top: 0;
text-align: center;
width: 100%;
height: 1.95rem;
.head_toggle {
position: absolute;
left:0.5rem;
i {
line-height: 1.95rem;
font-size: 1rem;
}
}
.head_logo {
line-height: 1.95rem;
font-size: 1rem;
color: #fff;
}
}
</style>
接着,我们在Layout.vue
中引用head
组件,代码如下:
/**********************************************/
/* src/components/Layout.vue */
/**********************************************/
<template>
<div class="app_wrapper">
<!-- head -->
<head-top></head-top>
<!-- main -->
<div class="main_wrapper">
<img src="../assets/logo.png">
<h1 class="title">{{ msg }}</h1>
</div>
</div>
</template>
<script>
import headTop from './header/head'
export default {
components: {
headTop
},
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
},
}
</script>
<style lang="scss" scoped>
/*layout*/
.app_wrapper {
background-color: #f2f2f2;
/*main_wrapper*/
.main_wrapper {
padding-top: 1.95rem;
.title {
font-size: 1rem;
color: #f00;
}
}
}
</style>
这里需要注意一点,我用的是<head-top></head-top>
,而非<header></header>
,这是因为header
是html
的关键字,类似的命名都不能使用。
其次,为什么在script
中的引用是headTop
,而在html
里的书写就变成了head-top
。这个可以去查下,根据大写字母自动转变成-
连接。
在Layout.vue
中,我们还做了一件事,就是把src/App.vue
中的logo
图片那段代码暂时移动到这里了。同时,我们把src/App.vue
中的style
样式删除,下面将引入全站样式文件。此时,src/App.vue
的代码如下:
/**********************************************/
/* src/App.vue */
/**********************************************/
<template>
<div id="app">
<router-view/>
</div>
</template>
<script>
export default {
name: 'app'
}
</script>
Step6. 引入iconfont
上面我们说引入全站样式,在src
下创建style
文件夹,并依次添加样式入口文件index.scss
,全站样式文件common.scss
,字体样式文件iconfont.scss
,mixin文件mixin.scss
,如下:
/**********************************************/
/* src/style/index.scss */
/**********************************************/
@import "./common.scss";
@import "./iconfont.scss";
@import "./mixin.scss";
/**********************************************/
/* src/style/common.scss */
/**********************************************/
body,
div,
span,
header,
footer,
nav,
section,
aside,
article,
ul,
dl,
dt,
dd,
li,
a,
p,
h1,
h2,
h3,
h4,
h5,
h6,
// i,
b,
textarea,
button,
input,
select,
figure,
figcaption,
{
padding: 0;
margin: 0;
list-style: none;
font-style: normal;
text-decoration: none;
border: none;
color: #333;
font-weight: normal;
font-family: "Microsoft Yahei";
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
-webkit-font-smoothing: antialiased;
&:hover {
outline: none;
}
}
/*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/
::-webkit-scrollbar {
width: 0px;
height: 0px;
background-color: #F5F5F5;
}
/*定义滚动条轨道 内阴影+圆角*/
::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 1px rgba(0, 0, 0, 0);
border-radius: 10px;
background-color: #F5F5F5;
}
/*定义滑块 内阴影+圆角*/
::-webkit-scrollbar-thumb {
border-radius: 10px;
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .3);
background-color: #555;
}
/*定义自动填充数据背景色*/
input:-webkit-autofill {
-webkit-box-shadow: 0 0 0px 1000px #fff inset;
-webkit-text-fill-color: #666;
}
/*定义placeholder提示的颜色*/
::-webkit-input-placeholder {
/* WebKit browsers */
// font-size: 0.6rem;
color: #999;
}
:-moz-placeholder {
/* Mozilla Firefox 4 to 18 */
// font-size: 0.6rem;
color: #999;
}
::-moz-placeholder {
/* Mozilla Firefox 19+ */
// font-size: 0.6rem;
color: #999;
}
:-ms-input-placeholder {
/* Internet Explorer 10+ */
// font-size: 0.6rem;
color: #999;
}
input[type="button"],
input[type="submit"],
input[type="search"],
input[type="reset"] {
-webkit-appearance: none;
}
textarea {
-webkit-appearance: none;
}
html,
body {
height: 100%;
width: 100%;
background-color: #f5f5f5; //transparent;
font-size: 0.6rem;
}
.clear:after {
content: '';
display: block;
clear: both;
}
.clear {
zoom: 1;
}
.back_img {
background-repeat: no-repeat;
background-size: 100% 100%;
}
.margin {
margin: 0 auto;
}
.left {
float: left;
}
.right {
float: right;
}
.hide {
display: none;
}
.show {
display: block;
}
.ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.paddingTop {
padding-top: 1.95rem;
}
@keyframes backOpacity {
0% {
opacity: 1
}
25% {
opacity: .5
}
50% {
opacity: 1
}
75% {
opacity: .5
}
100% {
opacity: 1
}
}
.animation_opactiy {
animation: backOpacity 2s ease-in-out infinite;
}
/**********************************************/
/* src/style/iconfont.scss */
/**********************************************/
// 暂时没有内容
/**********************************************/
/* src/style/mixin.scss */
/**********************************************/
// 暂时没有内容
既然已经创建了文件,我们就可以将样式用于项目,在src/main.js
中引入index.scss
,代码如下:
/**********************************************/
/* 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'
import './config/rem'
import './style/index.scss'; //仅插入这句
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
template: '<App/>',
components: { App }
})
此时,我们的iconfont.scss
文件其实已经被引用到项目中了,但是因为没有字体样式的内容,没有效果。
我们使用iconfont-阿里巴巴矢量图标库来为项目创建字体。登陆帐号后,添加字体,创建项目,下载项目字体,我们就可以拿来使用了。下载的字体文件包内容如下:
# 下载的字体文件包内容
font package
├── demo_fontclass.html // class使用方式(demo)
├── demo_symbol.html // symbol使用方式(demo)
├── demo_unicode.html // unicode使用方式(demo)
├── demo.css // 样式文件(demo)
├── iconfont.css // 字体样式
├── iconfont.eot // 字体文件
├── iconfont.js // 字体脚本
├── iconfont.svg // 字体文件
├── iconfont.ttf // 字体文件
└── iconfont.woff // 字体文件
当然,你也可以选择使用字体在线路径进行引用,不过我一般没有这么做。
在src/assets
中创建iconfont
文件夹,将iconfont.eot
,iconfont.svg
,iconfont.ttf
,iconfont.woff
放入其中,接着修改iconfont.scss
文件,内容如下:
/**********************************************/
/* src/style/iconfont.scss */
/**********************************************/
@font-face {
font-family: "iconfont";
src: url('../assets/iconfont/iconfont.eot?t=1509583688642');
/* IE9*/
src: url('../assets/iconfont/iconfont.eot?t=1509583688642#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('../assets/iconfont/iconfont.woff?t=1509583688642') format('woff'), /* chrome, firefox */
url('../assets/iconfont/iconfont.ttf?t=1509583688642') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
url('../assets/iconfont/iconfont.svg?t=1509583688642#iconfont') format('svg');
/* iOS 4.1- */
}
.iconfont {
font-family: "iconfont" !important;
font-size: 0.8rem;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-category:before {
content: "\e699";
}
.icon-fanhui:before {
content: "\e6e3";
}
.icon-tianjia:before {
content: "\e6a5";
}
.icon-tianjia-circle:before {
content: "\e68e";
}
.icon-xiaoxi:before {
content: "\e672";
}
.icon-lianxiren:before {
content: "\e63d";
}
.icon-dongtai:before {
content: "\e602";
}
.icon-404:before {
content: "\e60c";
}
.icon-search:before {
content: "\e620";
}
.icon-vip:before {
content: "\e603";
}
.icon-wallet:before {
content: "\e625";
}
.icon-grab:before {
content: "\e604";
}
.icon-favor:before {
content: "\e61f";
}
.icon-photo:before {
content: "\e63f";
}
.icon-file:before {
content: "\e621";
}
.icon-setting:before {
content: "\e8ea";
}
.icon-night:before {
content: "\e653";
}
.icon-profile:before {
content: "\e631";
}
.icon-phone:before {
content: "\e626";
}
.icon-totop:before {
content: "\e600";
}
.icon-QQ:before {
content: "\e647";
}
.icon-arrow-down:before {
content: "\e6a6";
}
.icon-arrow-right:before {
content: "\e6a7";
}
注意:这里的代码和字体文件,已经包含整个项目所用到的全部字体。
此时,我们看运行后的项目效果,header
的toggleButton
按钮已经显示字体。我们又发现不对了,这里是字体按钮,可是在线demo和效果截图中,这个按钮是一个头像图片,这个下一步我们再替换下。
Step7. 侧边菜单sidebar
在src/components
中创建sidebar
文件夹,并创建sidebar.vue
,其代码如下:
/**********************************************/
/* src/components/sidebar/sidebar.vue */
/**********************************************/
<template>
<div class="sidebar">
<div class="top">
<div class="top_info">
<img class="top_image" src="https://cn.bing.com/az/hprichbg/rb/GreatSaltLake_ZH-CN12553220159_1920x1080.jpg"/>
<span class="top_name">Season</span>
<p class="top_sign">太多曾沾沾自喜誓必珍惜的情谊,败给了时光的腐朽</p>
</div>
</div>
<div class="menu">
<ul>
<li>
<i class="iconfont icon-vip"></i>我的超级会员
</li>
<li>
<i class="iconfont icon-wallet"></i>QQ钱包
</li>
<li>
<i class="iconfont icon-grab"></i>个性装扮
</li>
<li>
<i class="iconfont icon-favor"></i>我的收藏
</li>
<li>
<i class="iconfont icon-photo"></i>我的相册
</li>
<li>
<i class="iconfont icon-file"></i>我的文件
</li>
</ul>
</div>
<div class="tool">
<span @click="handleSidebar('setting')">
<i class="iconfont icon-setting"></i>设置
</span>
<span @click="handleSidebar('login')">
<i class="iconfont icon-night"></i>夜间
</span>
</div>
</div>
</template>
<script>
export default {
methods: {
handleSidebar(name) {
// 暂时不要管这里,后面你可以留意下
// 作用就是改变侧边菜单状态,并存储进仓库
// this.$store.dispatch('ToggleSideBar');
// 跳转对应页面
this.$router.push({ path: '/'+name });
}
}
}
</script>
<style lang="scss" scoped>
/*sidebar*/
.sidebar {
position: fixed;
top: 0;
bottom: 0;
left: 0;
height: 100%;
color: #999;
z-index: 110;
background-color: #fff;
i {
margin-right: 0.5rem;
}
.top {
position: relative;
width: 100%;
height: 30%;
background-color: #333;
background-image: url(https://cn.bing.com/az/hprichbg/rb/CoastalBeech_ZH-CN8739604309_1920x1080.jpg);
background-position: 50% 50%;
background-size: cover;
.top_info {
position: absolute;
left: 0;
bottom: 0;
padding: 0 0.8rem 0.6rem;
width: 100%;
.top_image {
width: 1.8rem;
height: 1.8rem;
border-radius: 1.8rem;
border: 0.12rem solid #fff;
}
.top_name {
padding-left: 0.4rem;
vertical-align: top;
line-height: 2.2rem;
font-size: 1.2rem;
font-weight: 600;
color: #fff;
}
.top_sign {
padding: 0.2rem 0;
font-size: 0.6rem;
color: #fff;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
.menu {
width: 100%;
height: 70%;
padding: 0.8rem 0 2rem 0;
ul {
padding: 0 0.8rem;
li {
height: 1.85rem;
line-height: 1.85rem;
}
}
}
.tool {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
background-color: #fff;
line-height: 2rem;
span {
display: inline-block;
padding: 0 0.8rem;
i {
color: #666;
}
}
}
}
</style>
接着,我们在Layout.vue
中引用sidebar
组件,代码如下:
/**********************************************/
/* src/components/Layout.vue */
/**********************************************/
<template>
<div class="app_wrapper hideSidebar">
<!-- head -->
<head-top></head-top>
<!-- sidebar -->
<sidebar></sidebar>
<!-- main -->
<div class="main_wrapper">
<img src="../assets/logo.png">
<h1 class="title">{{ msg }}</h1>
</div>
</div>
</template>
<script>
import headTop from './header/head'
import sidebar from './sidebar/sidebar'
export default {
components: {
headTop,
sidebar
},
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
},
}
</script>
<style lang="scss">
/*layout*/
.app_wrapper {
overflow-x: hidden;
.header {
transition: all .28s ease-out;
transform: translateX(13rem);
}
.sidebar {
width: 13rem;
transition: all .28s ease-out;
transform: translate(0);
}
/*main_wrapper*/
.main_wrapper {
padding-top: 1.95rem;
transition: all .28s ease-out;
transform: translateX(13rem);
}
&.hideSidebar {
.header {
transform: translateX(0);
}
.sidebar {
transform: translateX(-13rem);
}
.main_wrapper {
transform: translateX(0);
}
}
}
</style>
现在,我们把sidebar
组件引入了,但是发现控制样式的类hideSidebar
受到顶部导航header
的变量sidebar控制,需要将变量放置在Layout.vue
中,并且在侧边菜单显示的时候需要一个遮罩层masking
。
在src/components
中创建masking
文件夹,并创建masking.vue
,其代码如下:
/**********************************************/
/* src/components/masking/masking.vue */
/**********************************************/
<template>
<div class="masking" @click="toggleSideBar"></div>
</template>
<script>
export default {
methods: {
toggleSideBar() {
//暂时先注释,后面store之后那步再打开
// this.$store.dispatch('ToggleSideBar');
},
},
}
</script>
<style lang="scss" scoped>
/*masking*/
.masking {
display: block;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 100;
}
</style>
修改Layout.vue
,引入masking
组件,并添加sidebar
变量及toggleSideBar
方法,稍后header
组件调用。
/**********************************************/
/* src/components/Layout.vue */
/**********************************************/
<template>
<div class="app_wrapper" :class="{hideSidebar:sidebar}">
<!-- head -->
<head-top @toggleSideBar="toggleSideBar"></head-top>
<!-- sidebar -->
<sidebar></sidebar>
<!-- masking -->
<masking></masking>
<!-- main -->
<div class="main_wrapper">
<img src="../assets/logo.png">
<h1 class="title">{{ msg }}</h1>
</div>
</div>
</template>
<script>
import headTop from './header/head'
import sidebar from './sidebar/sidebar'
import masking from './masking/masking'
export default {
components: {
headTop,
sidebar,
masking
},
data() {
return {
sidebar: false,
msg: 'Welcome to Your Vue.js App'
}
},
methods: {
toggleSideBar() {
this.sidebar = !this.sidebar;
console.log("sidebar current value: " + this.sidebar);
},
},
}
</script>
<style lang="scss">
/*layout*/
.app_wrapper {
overflow-x: hidden;
.header {
transition: all .28s ease-out;
transform: translateX(13rem);
}
.sidebar {
width: 13rem;
transition: all .28s ease-out;
transform: translate(0);
}
/*main_wrapper*/
.main_wrapper {
padding-top: 1.95rem;
transition: all .28s ease-out;
transform: translateX(13rem);
}
&.hideSidebar {
.header {
transform: translateX(0);
}
.sidebar {
transform: translateX(-13rem);
}
.masking {
display: none;
}
.main_wrapper {
transform: translateX(0);
}
}
}
</style>
修改header.vue
文件,代码如下:
/**********************************************/
/* src/components/header/head.vue */
/**********************************************/
<template>
<header class='header'>
<span class="head_toggle" @click="toggleSideBar">
<img class="top_image" src="https://cn.bing.com/az/hprichbg/rb/GreatSaltLake_ZH-CN12553220159_1920x1080.jpg"/>
</span>
<span class="head_text">LOGO</span>
</header>
</template>
<script>
export default {
methods: {
toggleSideBar() {
// this.sidebar = !this.sidebar;
// console.log("sidebar current value: " + this.sidebar);
this.$emit('toggleSideBar');
},
},
}
</script>
<style lang="scss" scoped>
/*header*/
.header {
background-color: #3190e8;
background: -webkit-linear-gradient(right top, #61b8f8 , #5e8bf7); /* Safari 5.1 - 6.0 */
background: -o-linear-gradient(bottom left, #61b8f8, #5e8bf7); /* Opera 11.1 - 12.0 */
background: -moz-linear-gradient(bottom left, #61b8f8, #5e8bf7); /* Firefox 3.6 - 15 */
background: linear-gradient(to bottom left, #61b8f8 , #5e8bf7); /* 标准的语法 */
position: fixed;
left: 0;
top: 0;
text-align: center;
width: 100%;
height: 1.95rem;
z-index: 10;
/*头像菜单按钮*/
.head_toggle {
position: absolute;
left:0.5rem;
img {
width: 1.5rem;
height: 1.5rem;
border-radius: 1rem;
margin-top: 0.2rem;
}
i {
line-height: 1.95rem;
font-size: 1rem;
}
}
/*文字*/
.head_text {
line-height: 1.95rem;
font-size: 0.7rem;
color: #fff;
display: inline-block;
}
}
</style>
现在来看一下效果,我们要的功能已经实现了。
Step8. 底部导航footer
在src/components
中创建footer
文件夹,并创建footer.vue
,其代码如下:
/**********************************************/
/* src/components/footer/footer.vue */
/**********************************************/
<template>
<div class='footer'>
<router-link to="/messages" class="footer_menu" :class="{active:(activeIndex==0)}">
<i class="iconfont icon-xiaoxi"></i>
<span>消息</span>
</router-link>
<router-link to="/contacts" class="footer_menu" :class="{active:(activeIndex==1)}">
<i class="iconfont icon-lianxiren"></i>
<span>联系人</span>
</router-link>
<router-link to="/dynamics" class="footer_menu" :class="{active:(activeIndex==2)}">
<i class="iconfont icon-dongtai"></i>
<span>动态</span>
</router-link>
</div>
</template>
<script>
export default {
props: ['activeIndex'],
}
</script>
<style lang="scss" scoped>
.footer {
background-color: #fff;
position: fixed;
z-index: 10;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 1.95rem;
display: flex;
box-shadow: 0 -0.03rem 0.05rem rgba(0, 0, 0, .1);
.footer_menu {
flex: 1;
display: flex;
text-align: center;
flex-direction: column;
align-items: center;
.iconfont {
display: block;
font-size: 1rem;
margin-top: 0.2rem;
color: #666;
}
span {
display: block;
font-size: 0.4rem;
color: #666;
}
&.active {
.iconfont {
color: #68b7f9;
}
span {
color: #68b7f9;
}
}
}
}
</style>
接着,我们在Layout.vue
中引用footer
组件,代码如下:
/**********************************************/
/* src/components/Layout.vue */
/**********************************************/
<template>
<div class="app_wrapper" :class="{hideSidebar:sidebar}">
<!-- head -->
<head-top @toggleSideBar="toggleSideBar"></head-top>
<!-- sidebar -->
<sidebar></sidebar>
<!-- masking -->
<masking></masking>
<!-- footer -->
<foot-menu :activeIndex="0"></foot-menu>
<!-- main -->
<div class="main_wrapper">
<img src="../assets/logo.png">
<h1 class="title">{{ msg }}</h1>
</div>
</div>
</template>
<script>
import headTop from './header/head'
import sidebar from './sidebar/sidebar'
import masking from './masking/masking'
import footMenu from '@/components/footer/footer'
export default {
components: {
headTop,
sidebar,
masking,
footMenu
},
data () {
return {
sidebar: false,
msg: 'Welcome to Your Vue.js App'
}
},
methods: {
toggleSideBar() {
this.sidebar = !this.sidebar;
console.log("sidebar current value: " + this.sidebar);
},
},
}
</script>
<style lang="scss">
/*layout*/
.app_wrapper {
overflow-x: hidden;
.header {
transition: all .28s ease-out;
transform: translateX(13rem);
}
.sidebar {
width: 13rem;
transition: all .28s ease-out;
transform: translate(0);
}
/*main_wrapper*/
.main_wrapper {
padding-top: 1.95rem;
transition: all .28s ease-out;
transform: translateX(13rem);
}
.footer {
transition: all .28s ease-out;
transform: translateX(13rem);
}
&.hideSidebar {
.header {
transform: translateX(0);
}
.sidebar {
transform: translateX(-13rem);
}
.masking {
display: none;
}
.main_wrapper {
transform: translateX(0);
}
.footer {
transform: translateX(0);
}
}
}
</style>
那么做到这里,我们就已经能够将一个APP的基本结构用组件方式拼装起来,这个母版页将可以用在多个页面。我们已经掌握了这部分技能。
但是,现在我们再回去看一下需要实现的demo,我们发现并不是所有页面都有footer
,每个页面的header
也不大相同。我们未来需要调整母版页Layout
的代码,把顶部导航和底部导航丢到各个具体页面上。
Step9. 返回顶部backToTop(组件)
返回顶部也是一个常用的功能,看一下实现。在src/components
中创建common
文件夹,并创建backToTop.vue
,其代码如下:
/**********************************************/
/* src/components/common/backToTop.vue */
/**********************************************/
<template>
<div v-show="visible" id="back_top" @click="backTop">
<i class="iconfont icon-totop"></i>
</div>
</template>
<script>
export default {
props: {
scrollHeight: {
type: Number,
default: 100
},
timeSpan: {
type: Number,
default: 200
}
},
data() {
return {
wHeight: 0,
visible: false,
ret: 0,
obj: null,
speed: 0,
times: 0,
time: 0,
}
},
mounted () {
window.addEventListener('scroll',this.hasScroll);
},
methods: {
hasScroll(){
const scrollTop = this.getScroll(window);
this.visible = scrollTop > this.scrollHeight;
},
getScroll(w){
this.ret = w.pageYOffset
const method = 'scrollTop'
if(typeof this.ret !== 'number'){
let d= w.document;
this.ret = d.documentElemelnt[method]
if(typeof this.ret !== 'number'){
this.ret = d.body[method]
}
}
return this.ret
},
backTop(){
const initerval = 30
let num = this.timeSpan/initerval
this.time = 0
this.times = num;
this.speed = this.ret / num
this.obj = setInterval(this.setScroll,initerval)
},
setScroll(){
if(this.time > this.times || this.ret<=0){
clearInterval(this.obj)
return
}
this.time++
this.ret -= this.speed
if(this.ret<0) {
this.ret = 0;
}
document.documentElement.scrollTop = document.body.scrollTop = this.ret
}
},
}
</script>
<style lang="scss" scoped>
#back_top {
position: fixed;
bottom: 10%;
right: 1rem;
width: 1.6rem;
height: 1.6rem;
border-radius: 1.6rem;
line-height: 1.6rem;
text-align: center;
background-color: rgba(49, 49, 49, 0.23);
i {
color: #666;
}
}
</style>
和header
、footer
一样,在Layout.vue
中引用就好了,这里不单独贴代码了。
Step10. 仓库存储store
为了后面能把header
和footer
移出Layout
,放入每个单独的页面,我们需要把控制侧边菜单状态的变量存放在仓库store中管理。下面看看如何实现。
在src
中创建store
文件夹,并创建以下目录及文件:
store
├── modules // 仓库模块
│ ├── app.js // 基础模块(存放sidebar状态等)
│ ├── common.js // 页面模块(存放页面数据,晚点用到)
│ └── user.js // 用户模块(存放用户信息,晚点用到)
├── getters.js // 计算属性
└── index.js // 入口文件
其中,index.js
,getters.js
,modules/app.js
的代码如下:
/**********************************************/
/* src/store/index.js */
/**********************************************/
import Vue from 'vue';
import Vuex from 'vuex';
import app from './modules/app';
import getters from './getters';
Vue.use(Vuex);
const store = new Vuex.Store({
modules: {
app
},
getters
});
export default store
/**********************************************/
/* src/store/getter.js */
/**********************************************/
const getters = {
sidebar: state => state.app.sidebar
};
export default getters
/**********************************************/
/* src/store/modules/app.js */
/**********************************************/
import Cookies from 'js-cookie';
const app = {
state: {
sidebar: !+Cookies.get('sidebarStatus')
},
mutations: {
TOGGLE_SIDEBAR: state => {
if (state.sidebar) {
Cookies.set('sidebarStatus', 1);
} else {
Cookies.set('sidebarStatus', 0);
}
state.sidebar = !state.sidebar;
},
},
actions: {
ToggleSideBar: ({ commit }) => {
commit('TOGGLE_SIDEBAR')
},
}
};
export default app;
在modules/app.js
中,我们用了一个模块js-cookie
,需要安装一下,如下:
$ npm install js-cookier --save
安装好了即可。
接着我们在main.js
中引入store,代码如下:
/**********************************************/
/* 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'
import './config/rem'
import './style/index.scss';
import store from './store/index' // 插入这句
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store, // 插入这句
template: '<App/>',
components: { App }
})
此时仓库存储就搭建好了。
Step11. 侧边菜单状态保存
先修改Layout
,将sidebar
变量改成从仓库中获取;再修改header
,调用仓库的方法改变sidebar
的值。
/**********************************************/
/* src/components/Layout.vue */
/**********************************************/
<template>
<div class="app_wrapper" :class="{hideSidebar:sidebar}">
<!-- head -->
<head-top @toggleSideBar="toggleSideBar"></head-top>
<!-- sidebar -->
<sidebar></sidebar>
<!-- masking -->
<masking></masking>
<!-- footer -->
<foot-menu :activeIndex="0"></foot-menu>
<!-- main -->
<div class="main_wrapper">
<img src="../assets/logo.png">
<h1 class="title">{{ msg }}</h1>
</div>
<!-- backToTop -->
<back-to-top></back-to-top>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import headTop from './header/head'
import sidebar from './sidebar/sidebar'
import masking from './masking/masking'
import footMenu from './footer/footer'
import backToTop from './common/backToTop'
export default {
components: {
headTop,
sidebar,
masking,
footMenu,
backToTop
},
data () {
return {
sidebar: false,
msg: 'Welcome to Your Vue.js App'
}
},
computed: {
...mapGetters([
'sidebar',
])
},
}
</script>
<style lang="scss">
/*layout*/
.app_wrapper {
overflow-x: hidden;
.header {
transition: all .28s ease-out;
transform: translateX(13rem);
}
.sidebar {
width: 13rem;
transition: all .28s ease-out;
transform: translate(0);
}
/*main_wrapper*/
.main_wrapper {
padding-top: 1.95rem;
transition: all .28s ease-out;
transform: translateX(13rem);
}
.footer {
transition: all .28s ease-out;
transform: translateX(13rem);
}
&.hideSidebar {
.header {
transform: translateX(0);
}
.sidebar {
transform: translateX(-13rem);
}
.masking {
display: none;
}
.main_wrapper {
transform: translateX(0);
}
.footer {
transform: translateX(0);
}
}
}
</style>
/**********************************************/
/* src/components/header/head.vue */
/**********************************************/
<template>
<header class='header'>
<span class="head_toggle" @click="toggleSideBar">
<img class="top_image" src="https://cn.bing.com/az/hprichbg/rb/GreatSaltLake_ZH-CN12553220159_1920x1080.jpg"/>
</span>
<span class="head_text">LOGO</span>
</header>
</template>
<script>
export default {
methods: {
toggleSideBar() {
this.$store.dispatch('ToggleSideBar');
},
},
}
</script>
<style lang="scss" scoped>
/*header*/
.header {
background-color: #3190e8;
background: -webkit-linear-gradient(right top, #61b8f8 , #5e8bf7); /* Safari 5.1 - 6.0 */
background: -o-linear-gradient(bottom left, #61b8f8, #5e8bf7); /* Opera 11.1 - 12.0 */
background: -moz-linear-gradient(bottom left, #61b8f8, #5e8bf7); /* Firefox 3.6 - 15 */
background: linear-gradient(to bottom left, #61b8f8 , #5e8bf7); /* 标准的语法 */
position: fixed;
left: 0;
top: 0;
text-align: center;
width: 100%;
height: 1.95rem;
z-index: 10;
/*头像菜单按钮*/
.head_toggle {
position: absolute;
left:0.5rem;
img {
width: 1.5rem;
height: 1.5rem;
border-radius: 1rem;
margin-top: 0.2rem;
}
i {
line-height: 1.95rem;
font-size: 1rem;
}
}
/*文字*/
.head_text {
line-height: 1.95rem;
font-size: 0.7rem;
color: #fff;
display: inline-block;
}
}
</style>
此时,我们查看效果,这一块已经完成。
Step12. 搜索栏searchBar(组件)
查看我们要做的demo页面,发现很多页面有一个公共组件searchBar,我们先把这个处理一下。
在src/components/common
中添加searchBar.vue
,代码如下:
/**********************************************/
/* src/components/common/searchBar.vue */
/**********************************************/
<template>
<div class="search" @click="handleClick">
<div class="search_content">
<i class="iconfont icon-search"></i> 搜索
</div>
</div>
</template>
<script>
export default {
methods: {
handleClick() {
this.$router.push({ path: '/search' });
}
}
}
</script>
<style lang="scss" scoped>
.search {
padding: 0.5rem;
background-color: #fff;
.search_content {
text-align: center;
font-size: 0.5rem;
line-height: 2;
border-radius: 0.1rem;
color: #999;
background-color: #eee;
i {
font-size: 0.5rem;
color: #999;
}
}
}
</style>
代码中有一个事件,它的作用就是跳转到搜索页面,那么稍后我们添加这个页面,并添加相关路由。
Step13. 页面添加
现在我们先整理一下Layout.vue
,把header
和footer
移出去,等下将他们放在各个页面中。这里,我们还把页面的实际内容代码,用<router-view></router-view>
替换,这个位置将渲染页面的具体内容。整理后的Layout.vue
,代码如下:
/**********************************************/
/* src/components/Layout.vue */
/**********************************************/
<template>
<div class="app_wrapper" :class="{hideSidebar:sidebar}">
<!-- main -->
<router-view></router-view>
<!-- sidebar -->
<sidebar></sidebar>
<!-- masking -->
<masking></masking>
<!-- backToTop -->
<back-to-top></back-to-top>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import sidebar from './sidebar/sidebar'
import masking from './masking/masking'
import backToTop from './common/backToTop'
export default {
components: {
sidebar,
masking,
backToTop,
},
computed: {
...mapGetters([
'sidebar',
])
},
}
</script>
<style lang="scss">
/*layout*/
.app_wrapper {
overflow-x: hidden;
.header {
transition: all .28s ease-out;
transform: translateX(13rem);
}
.sidebar {
width: 13rem;
transition: all .28s ease-out;
transform: translate(0);
}
/*main_wrapper*/
.main_wrapper {
padding-top: 1.95rem;
transition: all .28s ease-out;
transform: translateX(13rem);
}
.footer {
transition: all .28s ease-out;
transform: translateX(13rem);
}
&.hideSidebar {
.header {
transform: translateX(0);
}
.sidebar {
transform: translateX(-13rem);
}
.masking {
display: none;
}
.main_wrapper {
transform: translateX(0);
}
.footer {
transform: translateX(0);
}
}
}
</style>
在src
中创建page
文件夹,并创建以下文件:
page
├── messages
│ └── messages.vue // 消息页面
├── contacts
│ └── contacts.vue // 联系人页面
├── dynamics
│ └── dynamics.vue // 动态页面
├── search
│ └── search.vue // 搜索页面
├── login
│ └── login.vue // 登陆页面
├── error
│ └── pageNotFound.vue // 404页面
├── ...
├── ...
└── 其他请看源码
所有页面的代码先如下:
/**********************************************/
/* 上述新创建页面 */
/**********************************************/
<template>
<div>
这里是<对应页面名称>页面(区分一下)
</div>
</template>
<script>
export default {
}
</script>
<style lang="scss" scoped>
</style>
上面代码,表示每个页面只是带有一句描述的空页面。下面,我们把路由补全,修改src/router/index.js
,代码如下:
/**********************************************/
/* src/router/index.js */
/**********************************************/
import Vue from 'vue'
import Router from 'vue-router'
import Layout from '@/components/Layout'
const Login = r => require.ensure([], () => r(require('../page/login/login')), 'Login')
const Messages = r => require.ensure([], () => r(require('../page/messages/messages')), 'messages')
const Contacts = r => require.ensure([], () => r(require('../page/contacts/contacts')), 'contacts')
const Dynamics = r => require.ensure([], () => r(require('../page/dynamics/dynamics')), 'dynamics')
const Search = r => require.ensure([], () => r(require('../page/search/search')), 'search')
const PageNotFound = r => require.ensure([], () => r(require('../page/error/pageNotFound')), 'pageNotFound')
Vue.use(Router)
export default new Router({
mode: "history",
routes: [
{ path: '/login', name: 'Login', component: Login },
{
path: '/',
component: Layout,
children: [
{ path: '', redirect: '/login' },
{ path: '/messages', name: 'Messages', component: Messages },
{ path: '/contacts', name: 'Contacts', component: Contacts },
{ path: '/dynamics', name: 'Dynamics', component: Dynamics },
{ path: '/search', name: 'Search', component: Search }
]
},
{ path: '*', component: PageNotFound }
]
})
下面,我们通过修改URL,看一下是否可以访问所有页面。
Step14. 弹窗提示(组件)
考虑到要用到“提示”,我们先添加一个这样的组件。在src/components/common
中添加alertTip.vue
,代码如下:
/**********************************************/
/* src/components/common/alertTip.vue */
/**********************************************/
<template>
<div class="alet_container">
<section class="tip_text_container">
<div class="tip_icon">
<span></span>
<span></span>
</div>
<p class="tip_text">{{alertText}}</p>
<div class="confrim" @click="closeTip">确认</div>
</section>
</div>
</template>
<script>
export default {
data(){
return{
positionY: 0,
timer: null,
}
},
mounted(){
},
props: ['alertText'],
methods: {
closeTip(){
this.$emit('closeTip')
}
}
}
</script>
<style lang="scss" scoped>
@keyframes tipMove{
0% { transform: scale(1) }
35% { transform: scale(.8) }
70% { transform: scale(1.1) }
100% { transform: scale(1) }
}
.alet_container{
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 200;
background-color: rgba(0,0,0,0.5);
}
.tip_text_container{
position: absolute;
top: 50%;
left: 50%;
margin-top: -6rem;
margin-left: -6rem;
width: 12rem;
animation: tipMove .4s ;
background-color: rgba(255,255,255,1);
border: 1px;
padding-top: .6rem;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
border: 1px;
border-radius: 0.25rem;
.tip_icon{
width: 3rem;
height: 3rem;
border: 0.15rem solid #f8cb86;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
span:nth-of-type(1){
width: 0.12rem;
height: 1.5rem;
background-color: #f8cb86;
}
span:nth-of-type(2){
width: 0.2rem;
height: 0.2rem;
border: 1px;
border-radius: 50%;
margin-top: .2rem;
background-color: #f8cb86;
}
}
.tip_text{
font-size: 0.7rem;
color: #333;
line-height: .9rem;
text-align: center;
margin-top: .8rem;
padding: 0 .4rem;
}
.confrim{
font-size: 0.8rem;
color: #fff;
font-weight: bold;
margin-top: .8rem;
background-color: #4cd964;
width: 100%;
text-align: center;
line-height: 1.8rem;
border: 1px;
border-bottom-left-radius: 0.25rem;
border-bottom-right-radius: 0.25rem;
}
}
</style>
之后login
页面请求的时候,我们再看看是如何使用的。
由于文章篇幅限制,请继续阅读Vue教程--Wap端项目搭建从0到1(详解2)。
学习是一条漫漫长路,每天不求一大步,进步一点点就是好的。