简介:
vue 是尤雨溪大神的作品,在国内已位列三大框架之一。
个人感觉vue集合了Angular1和React优点的项目
注意 因占位符冲突,本文用\{\{ \}\}
代替 双大括号(去掉\
)
HTML/CSS/JS Prettify
安装后 tools->HTML/CSS/JS Prettify->set prettify preference
:在”allowed_file_extensions”: ["htm", "html", "xhtml", "shtml", "xml", "svg","vue"]
加上vue就好了npm config set registry https://registry.npm.taobao.org
npm install -g vue-cli webpack
npm install -g vue-cli
vue init webpack-simple vue2demo
# vue init webpack vue2demo (完整的项目骨架)
cd vue2demo && npm install&&npm run dev
启动文件;start.bat
@echo off
start cmd /k "cd vue2demo&&npm run dev"
start http://172.168.1.70:5555
构建项目结构:
cd src&&mkdir components&&cd assets&&mkdir img style js&&cd ../components&&mkdir main common page&&touch main.vue bus.js&& cd main&&mkdir Nav top && touch nav.vue top.vue foot.vue&& cd ../common && touch directive.js filter.js && cd ../page &&touch edit.vue home.vue list.vue map.vue && cd ../.. && touch components/router.js
安装全家桶及相关依赖:
npm install --save vue-router vue-resource vuex jquery@2.2.3 iview
npm install --save-dev css-loader style-loader less less-loader url-loader html-webpack-plugin
修改webpack.config.js
var HtmlWebpackPlugin = require('html-webpack-plugin')
// npm install --sava-dev html-webpack-plugin
module: {
//文件处理
rules: [{
test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|otf|svg|svgz)(\?.+)?$/,
use: [{
loader: 'url-loader',
options: {
limit: 10000
}
}]
}, {
test: /\.less$/, loader: 'style-loader!css-loader!less-loader' },{
test: /\.css$/, loader: 'style-loader!css-loader' }
]
},
resolve: {
//拓展名
extensions: ['.js', '.vue', '.json'],
//简写
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': path.resolve(__dirname, './src')
},
//去哪找依赖
modules: [
path.join(__dirname, './node_modules')
]
},
devServer: {
historyApiFallback: true,
noInfo: true,
port: 3333,
host: '172.168.1.70'
},
//注册全局变量
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery"
}),
// new webpack.optimize.CommonsChunkPlugin('common'),
new HtmlWebpackPlugin()
])
// new webpack.optimize.CommonsChunkPlugin('common.js'),
//在.vue文件中引入第三方非NPM模块 exports-loader
//var Showbo = require("exports?Showbo!./path/to/showbo.js");
<template>
<div class="main">
<h1>\{\{ $route.params.id2 \}\} </h1>
<!-- <router-link :to="{path:'details',query: {id:el.tog_line_id}}">
id = this.$route.query.id; -->
<router-link to="/子路由">子路由</router-link>
<hr>pathDemo
<router-view name='pathDemo'></router-view>
<hr>default
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
name: 'main',
data() {
return {
msg: 'main'
}
},
watch: {
'$route' (to, from) {
// 对路由参数变化作出响应...
}
}
}
</script>
<style scoped>
</style>
<template>
<div class="main">
<h1>\{\{ $route.params.name \}\} </h1>
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
name: 'main',
data() {
return {
msg: 'main'
}
},
watch: {
'$route' (to, from) {
// 对路由参数变化作出响应...
}
}
}
</script>
<style scoped>
</style>
<template>
<div id="app">
<h1>\{\{msg\}\}</h1>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
msg: 'Welcome'
}
}
}
</script>
import Vue from 'vue'
import App from './App'
import router from './components/router'
import Vuex from 'vuex'
import VueResource from 'vue-resource'
Vue.use(VueResource)
Vue.use(router)
Vue.use(Vuex)
new Vue({
el: '#app',
router,
render: h => h(App)
})
import Vue from 'vue'
import Router from 'vue-router'
import demo from './page/demo'
import demo2 from './page/demo2'
Vue.use(Router)
export default new Router({
routes: [{
path: '/',
component: demo
}, {
// /user/:username/post/:post_id
path: '/demo/:id',
component: demo,
children: [{
path: '/:name',
components: {
pathDemo: demo2,
default: demo
}
}]
}]
})
var HtmlWebpackPlugin = require('html-webpack-plugin')
var glob = require('glob')
var entryJS = glob.sync('./src/*.js').reduce(function(prev, curr) {
prev[curr.slice(6, -3)] = curr;
return prev;
}, {});
//entryJS['common'] = ['vue', 'jquery', 'iview', 'router', 'Vuex', 'VueResource', 'directive']
entryJS['common'] = ['vue', 'jquery', 'router'];
var htmls = glob.sync('./*.html').map(function(item) {
return new HtmlWebpackPlugin({
filename: item,
template: item,
inject: false,
chunks: [item.slice(2, -5), 'common']
});
});
//...
entry: entryJS,
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
// filename: '[name].[hash].js'
filename: '[name].js'
},
//...
//注册全局变量
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery"
}),
new webpack.optimize.CommonsChunkPlugin('common')
// , new HtmlWebpackPlugin()
]).concat(htmls);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>vue2demo-index</title>
</head>
<body>
<div id="index"></div>
<script type="text/javascript" src="/dist/common.js"></script>
<script type="text/javascript" src="/dist/index.js"></script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>vue2demo-main</title>
</head>
<body>
<div id="app"></div>
<script type="text/javascript" src="/dist/common.js"></script>
<script type="text/javascript" src="/dist/main.js"></script>
</body>
</html>
import Vue from 'vue'
import App from './index.vue'
import router from './components/router'
import Vuex from 'vuex'
import VueResource from 'vue-resource'
Vue.use(VueResource)
Vue.use(router)
Vue.use(Vuex)
new Vue({
el: '#index',
router,
render: h => h(App)
})
<template>
<div id="index">
<h1>\{\{msg\}\}</h1>
<a href='./main.html'>去主页</a>
</div>
</template>
<script>
export default {
name: 'index',
data() {
return {msg: 'index'}
}
}
</script>
修改 main.js
import VueResource from 'vue-resource'
Vue.use(VueResource)
router.beforeEach((to, from, next) => {
console.log('路由跳转前的预处理');
// next({ path: '/' })
next();
})
Vue.http.interceptors.push((request, next) => {
console.log('请求前的预处理和配置');
request.method = 'GET';
next((response) => {
console.log('响应后传给then前对response修改');
if (!response.ok) {
console.log('success');
} else {
console.log('error');
}
return response;
});
});
修改 App.vue
<template>
<div id="app">
<h1>\{\{msg\}\}</h1>
<button @click.stop='resource()'>resource</button>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
msg: 'Welcome'
}
},
methods: {
resource: function() {
this.$http.post(
'https://cnodejs.org/api/v1/topics', {
emulateJSON: true,
emulateHTTP: true,
data: {
'page': 1,
'tab': 'job',
'limit': 2,
'mdrender': true
}
}).then((res) => {
// this.items=res.body.data;
// this.$set(this.$data, 'items', res.body.data);
console.log(res.body.data);
}, (res) => {
console.log(res);
}).catch((res) => {
console.log(res)
});
}
}
}
</script>
修改 App.vue
ajax: function() {
var isEmpty = function(object) {
for (var name in object) {
return false;
}
return true;
}
var data={};
var param = {
'page': 1,
'tab': 'job',
'limit': 2,
'mdrender': true
};
var trimStr = function(key, value) {
if (typeof(value) === 'string') {
return $.trim(value);
}
return value;
}
if (Object.keys(param).length > 0) {
var obj = Object.keys(param).length === 1 ? param[Object.keys(param)[0]] : param;
data = {
// data: typeof(obj) === 'string' ? trimStr(obj) : JSON.stringify(obj, trimStr)
data: typeof(obj) === 'string' ? obj : JSON.stringify(obj)
}
}
var converEmptyProperty = function(object) {
for (var i in object) {
var value = object[i];
// Object.prototype.toString.call(object) == '[object Null]' '[object Undefined]')
if (typeof value === 'object') {
if (Array.isArray(value)) {
if (value.length == 0) {
// delete object[i];
console.log(i);
object[i] = [];
continue;
}
} else if (Object.prototype.toString.call(value) == '[object Null]') {
object[i] = '';
} else if (Object.prototype.toString.call(value) == '[object Undefined]') {
object[i] = '';
} else if (isEmpty(value)) {
object[i] = {};
}
converEmptyProperty(value);
} else {
if (value === '' || value === null || value === undefined) {
object[i] = '';
} else {
// console.log('check ', i, value);
}
}
}
}
$.ajax($.extend({}, data, {
url: 'https://cnodejs.org/api/v1/topics',
type: 'get',
contentType: "application/json; charset=utf-8",
dataType: "json",
crossDomain: true,
xhrFields: {
withCredentials: true
},
beforeSend: function(jqXHR, settings) {
//请求前数据处理
if (settings.type == 'GET') {
settings.url = settings.url.replace(/\:/g, '=')
.replace(/,\"/g, '&').replace(/[{}"]/g, "")
.replace("=//","h://");
} else {
if (settings.data) {
settings.data = JSON.stringify(JSON.parse(settings.data), trimStr);
}
}
return true;
},
dataFilter: function(data, type) {
// 返回的数据处理
var preData = JSON.parse(data)
converEmptyProperty(preData);
return JSON.stringify(preData)
}
})).error(function(jx) {
console.log(jx);
});
}
ajax 文件上传
var formData = new FormData();
let _self=this;
// var name = $("input").val();
// formData.append("name",name);
let _file = $("#" + this.id)[0];
if (_file && _file.files[0]) {
formData.append("file", _file.files[0]);
$.ajax({
url: store.state.systemInfo.uploadUrl,
type: 'post',
data: formData,
// 告诉jQuery不要去处理发送的数据
processData: false,
// 告诉jQuery不要去设置Content-Type请求头
contentType: false,
enctype: 'multipart/form-data',
cache: false,
beforeSend: function() {
console.log("正在进行,请稍候");
},
success: function(data) {
console.log("error");
},
error: function(data) {
console.log("error");
}
});
}
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const vuexStore = new Vuex.Store({
state: {
dic: {}
},
mutations: {
addDic(state, dics) {
$.extend(state.dic, dics);
}
},
getters: {
getDic: (state) => (key) => {
return `${state.dic.lable}---${state.dic.code}----${key} `;
}
}
})
export { vuexStore as default };
import store from './components/common/vuexStore.js'
//....
new Vue({
el: '#app',
store,
router,
render: h => h(App)
})
<span>\{\{dicMsg\}\}</span>
<button @click.stop='addDic()'>addDic</button>
<button @click.stop='getDic1()'>getDic1</button>
<button @click.stop='getDic2()'>getDic2</button>
//.....
addDic: function() {
this.msg = this.msg + " " + this.msg;
var n = this.msg.match(/Welcome/g) || [];
this.$store.commit("addDic", {
'code': n.length,
'lable': this.msg
});
},
getDic1: function() {
this.dicMsg = this.$store.state.dic;
},
getDic2: function() {
this.dicMsg = this.$store.getters.getDic('key');
}
import Vue from 'vue'
export default new Vue();
(function(global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.VueBus = factory());
}(this, (function() {
'use strict';
function VueBus(Vue) {
var bus = new Vue();
bus.data = {host: ""}
Object.defineProperty(Vue.prototype, '$bus', {
get: function() {
return bus;
}
});
}
if (typeof window !== 'undefined' && window.Vue) {
window.Vue.use(VueBus);
}
return VueBus;
})));
使用
//引入文件
import bus from './bus.js'
//广播消息
bus.$emit('toggleLoading', {'showMsg':false});
//监听消息
bus.$on('toggleLoading', (show) => {
this.toShow = show;
});
<template>
<div class="am-u-lg-12 am-cf">
<div class="am-fr">
<ul class="am-pagination tpl-pagination">
<li :class="[(pages[0]==1)?'am-disabled':'']"><a href="javascript:;" v-on:click="prevClick()">«</a></li>
<li v-for="index in pages" :class="[(pageData.pageNo == index)?'am-active':'']">
<a href="javascript:;" v-on:click="btnClick(index)">\{\{ index \}\}</a>
</li>
<li :class="[(pages[pages.length-1]==pageData.totalPages)?'am-disabled':'']">
<a href="javascript:;" v-on:click="nextClick()">»</a></li>
<li><a>共<i>\{\{pageData.totalPages\}\}(\{\{pageData.pageNo\}\})</i>页/共<i>\{\{pageData.totalRecords\}\}</i>条</a></li>
</ul>
</div>
</div>
</template>
<script>
import $ from 'jquery';
export default {
name: 'pagePlugin',
props: {
pageData: Object,
goto: Function
},
data() {
return {
pages: []
}
},
methods: {
getData: function(event) {
this.pages = [];
let startNo = 1,
endNo = 1;
if (this.pageData.pageNo < 4) {
endNo = this.pageData.totalPages;
} else if (this.pageData.pageNo + 4 > this.pageData.totalPages) {
endNo = this.pageData.totalPages;
if (this.pageData.totalPages > 6) {
startNo = this.pageData.totalPages - 5
}
} else {
startNo = this.pageData.pageNo - 5, endNo = this.pageData.pageNo + 4;
}
for (let i = startNo; i <= endNo; i++) {
this.pages.push(i);
}
},
prevClick: function(event) {
let newPages = [];
let arrInxSize = this.pages.length - 1;
for (let a = 0; a < 11; a++) {
if ((this.pages[0] - a) > 0) {
newPages.push(this.pages[0] - a);
} else {
for (let aa = 1; aa <= this.pages.length - a; aa++) {
newPages.push(this.pages[0] + aa);
}
break;
}
}
if (newPages) {
this.pages = newPages.sort(function(a, b) {
return a - b;
});
}
},
btnClick: function(index) {
this.goto(index);
},
nextClick: function(event) {
let newPages = [];
let arrInxSize = this.pages.length - 1;
for (let a = 1; a < 11; a++) {
if ((this.pages[arrInxSize] + a) <= this.pageData.totalPages) {
newPages.push(this.pages[arrInxSize] + a);
} else {
if (newPages != []) {
for (let aa = arrInxSize; aa >= a - 1; aa--) {
newPages.push(this.pages[aa]);
}
break;
}
}
}
if (newPages) {
this.pages = newPages.sort(function(a, b) {
return a - b;
});
}
}
},
watch: {
"pageData.pageNo": 'getData',
"pageData.pageSize": 'getData'
},
mounted: function() {
this.getData();
}
/*,
created() {
bus.$on('toggleLoading', (show) => {
this.toShow = show;
});
}*/
}
</script>
import pagePlugin from './components/common/pagePlugin'
Vue.component('page-plugin', pagePlugin)
import Vue from 'vue'
import pagePlugin from './pagePlugin'
Vue.component('page-plugin', pagePlugin)
main.js
import plugin from './components/common/plugin'
<page-plugin :pageData="pageData" :goto="goto"></page-plugin>
pageData: {
'pageNo': 1,
'totalPages': 10,
'totalRecords': 100,
'pageSize': 10
}
//....
goto:function(data){
console.log(data);
},
import $ from 'jquery'
import Vue from 'vue'
Vue.directive('example', {
// deep: true, //相关属性也是一个对象
twoWay: true, //双向绑定
acceptStatement: true, //让指令像 v-on 一样接受内联语句
// isLiteral: true, //值被看成字符串,不会建立数据监视
//<div v-my-directive="a++"></div>
bind: function(el, binding, vnode) { //初始化动作
bind: function(el, binding, vnode) { //初始化动作
// this.handler = function() {
// this.set(el.value) //赋值
// }.bind(this)
// el.addEventListener('input', this.handler);
el.innerHTML=binding.value;
},
},
// this.vm.$on('rotate', () => { })
inserted() { //被绑定元素插入父节点时调用
// console.log('inserted');
},
update(el, binding, vnode, oldVnode) { //模板更新和绑定数据改变时触发
// this.vm.$emit('crop', event)
},
componentUpdated() { //模板完成一次更新周期时调用
// console.log('componentUpdated');
},
unbind: function() { //指令与元素解绑时调用
// this.vm.$off('rotate')
this.el.removeEventListener('input', this.handler)
}
})
Vue.directive('options', function(el, binding) {
let value = binding.value.value;
let lable = binding.value.lable;
let opts = binding.value.options;
let optStr = '<option value=""></option>';
if (Array.isArray(opts)) {
for (let opt of opts) {
optStr += '<option value="' + opt[value || 'id'] + '">' + opt[lable || 'lable'] + '</option>';
}
$(el).empty();
$(el).append(optStr);
} else {
console.log('指令(v-options)参数有误!!!');
}
})
// $(el).trigger("input");
// el.dispatchEvent(new Event('change', { target: el.target })) ;
// el.dispatchEvent(new Event('input', { target: el.target }));
Vue.directive('focus', {
isLiteral: true, //值被看成字符串,不会建立数据监视
// inserted:
function(el, binding) {
el.focus();
// el.style.backgroundColor = binding.value.color
}
})
Vue.directive('example2', {
params: ['a'],
paramWatchers: {
a: function (val, oldVal) {
console.log('a changed!')
}
}
})
注册 main.js
import directive from './components/common/directive.js'
<span v-example="pageData"></span>
Vue.filter( 'discount' , function(value,discount) {
return value * ( discount / 100 ) ;
// read: function (value) {
// return '$' + value.toFixed(2)
// },
// write: function (value) {
// var number = +value.replace(/[^\d.]/g, '')
// return isNaN(number) ? 0 : number
// }
});
mian.js引入
import filter from './components/common/filter.js'
调用:
<ul>
<li v-for="product in products">
\{\{ product.name \}\} - \{\{ product.price| discount(25)\}\}
</li>
</ul>
// getter,返回已注册的过滤器 var myFilter = Vue.filter(‘discount’)
编辑 main.js 引用
import demoDirective from './components/common/demoDirective'
使用
<div v-demo:hello.a.b="message" style='color:red'></div>
;(function() {
var MyPlugin = {}
MyPlugin.install = function(Vue, options) {
// 全局方法或属性
//组件外使用 Vue.test();
Vue.test = function() {
alert("123")
},
// 2. 全局资源(指令,过滤器)
// <div v-test></div>
Vue.directive('test', {
isFn: true,
acceptStatement: true,
bind(el, binding, vnode, oldVnode) {
// 逻辑...
console.log("添加全局资源");
},
update: function(fn) {},
unbind: function() {}
})
// 3. 注入组件
//在 vue 初始化对象的时候,把 Vue.mixin 的参数 复制到了初始化对象中
Vue.mixin({
created: function() {
// 逻辑...
// console.log("注入组件");
}
})
//添加实例方法
//组件内 this.doubleNumber(2);
Vue.prototype.doubleNumber = function(val) {
if (typeof val === 'number') {
return val * 2;
} else if (!isNaN(Number(val))) {
return Number(val) * 2;
} else {
return null
}
}
}
if (typeof exports == "object") {
module.exports = MyPlugin
} else if (typeof define == "function" && define.amd) {
define([], function() {
return MyPlugin
})
} else if (window.Vue) {
window.MyPlugin = MyPlugin
Vue.use(MyPlugin)
}
})()
import MyPlugin from './components/common/myPlugin.js'
Vue.use(MyPlugin)
需要安装的包
1. karma //提供测试所需的浏览器环境、监测代码改变自动重测、整合持续集成等功能
2. phantomjs-prebuilt //phantomjs,在终端运行的浏览器虚拟机
3. mocha //test framework,测试框架,运行测试
4. chai //assertion framework, 断言库,提供多种断言,与测试框架配合使用
5. sinon //测试辅助工具,提供 spy、stub、mock 三种测试手段,帮助捏造特定场景
6. karma-webpack //karma 中的 webpack 插件
7. karma-mocha //karma 中的 mocha 插件
8. karma-sinon-chai //karma 中的 sinon-chai 插件
9. sinon-chai //karma 中的 chai 插件
10. karma-sourcemap-loader //karma 中的 sourcemap 插件
11. karma-phantomjs-launcher //karma 中的 phantomjs 插件
12. karma-spec-reporter //在终端输出测试结果
13. babel-plugin-istanbul //babel插件,es6代码产生instanbul覆盖率
14. karma-coverage //Karma插件,生成代码覆盖率
安装依赖
npm install --save-dev karma phantomjs-prebuilt mocha chai sinon karma-webpack karma-mocha karma-sinon-chai sinon-chai karma-sourcemap-loader karma-phantomjs-launcher karma-spec-reporter babel-plugin-istanbul karma-coverage
"scripts": {
"test": "cross-env NODE_ENV=browse karma start karma.conf.js"
}
安装 npm install --save babel-polyfill
在main.js 头部 import 'babel-polyfill'
karma init->mocha,no require.js,PhantomJS,默认目录"test/*/Spec.js"
var path = require('path')
module.exports = function(config) {
config.set({
// basePath: '',
frameworks: ['mocha', 'sinon-chai'],
// 测试入口文件
files: ['./test/unit/index.js'],
// exclude: [ ],
// 用webpack解析,同时显示测试文件路径
preprocessors: {
'./test/unit/index.js': ['webpack', 'sourcemap'],
},
// 设置测试覆盖率输出插件
reporters: ['spec', 'coverage'],
// karma-coverage配置,配置测试覆盖率的输出目录及格式
coverageReporter: {
dir: './coverage',
reporters: [
{ type: 'lcov', subdir: '.' },
{ type: 'text-summary' },
]
},
// port: 9876,
// logLevel: config.LOG_INFO,
// concurrency: Infinity,
colors: true,
autoWatch: false,
// 设置默认打开的浏览器
browsers: ['PhantomJS'],
// 设置运行完成是否自动退出
singleRun: true,
// 是否打印webpack打包信息
webpackMiddleware: {
noInfo: true
},
// webpack 配置用来解析js文件和vue文件,如果有css请自行配置css-loader
webpack: {
module: {
loaders: [{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.vue$/,
loaders: [{
loader: 'vue-loader',
}]
},
]
},
resolve: {
//拓展名
extensions: ['.js', '.vue', '.json'],
//简写
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': path.resolve(__dirname, './src'),
'router': path.resolve(__dirname, './src/components/router'),
},
//去哪找依赖
modules: [
path.join(__dirname, './node_modules')
]
}
}
})
}
import 'babel-polyfill'
// load 所有的测试用例文件
const testsContext = require.context('.', true, /\.spec$/)
testsContext.keys().forEach(testsContext)
// load 资源文件,及src目录下的除了main.js文件的所有文件
const srcContext = require.context('../../src', true, /^\.\/(?!(main|index)(\.js)?$)/);
srcContext.keys().forEach(srcContext)
2. .babelrc
文件
{
"presets": [
["env", {
"modules": false,
"useBuiltIns": true,
"targets": {
"browsers": ["last 3 versions"]
}
}]
],
"env": {
"browse": {
"plugins": ["istanbul"]
}
}
}
3. 编写并运行测试
touch src/util.js
export function util1 (name) {
if (name) {
return name
}
return 'hello'
}
touch test/unit/utils.spec.js 测试用例
import { util1 } from '../../src/util'
describe('utils.js', () => {
it('should return name', () => {
const name = util1('teapot')
expect(name).to.equal('teapot')
})
})
运行: npm test
测试覆盖率: coverage/index.html
import Vue from 'vue'
import MainApp from '../../src/App.vue'
describe('MainApp', () => {
it('has a created hook', () => {
expect(typeof MainApp.created).to.equal('function')
})
it('sets the correct default data', () => {
const defaultData = MainApp.data()
expect(defaultData.msg).to.equal('Welcome')
})
it('sets new text when created', () => {
const vm = new Vue(MainApp).$mount()
expect(vm.msg).to.equal('Bye Spec')
})
it('renders the correct text', () => {
const Ctor = Vue.extend(MainApp)
const vm = new Ctor().$mount()
expect(vm.$el.textContent).to.equal('Bye Spec')
})
})
npm install iview --save
import iView from 'iview';
import 'iview/dist/styles/iview.css'; // 使用 CSS
Vue.use(iView);
module: {
rules: [
{ test: /iview.src.*?js$/, loader: 'babel-loader' },
{ test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ }
]
}
import Checkbox from 'iview/src/components/checkbox';
<template>
<div class="layout" :class="{'layout-hide-text': spanLeft < 5}">
<Row type="flex">
<Col :span="spanLeft" class="layout-menu-left">
<Affix>
<Menu active-name="1-2" theme="dark" width="auto" :open-names="['1']">
<div class="layout-logo-left"></div>
<Submenu name="1">
<template slot="title">
<Icon type="ios-navigate" :size="iconSize"></Icon>
<span class="layout-text">导航一</span>
</template>
<MenuItem name="1-1">选项 1</MenuItem>
<MenuItem name="1-2">选项 2</MenuItem>
<MenuItem name="1-3">选项 3</MenuItem>
</Submenu>
<Submenu name="2">
<template slot="title">
<Icon type="ios-keypad" :size="iconSize"></Icon>
<span class="layout-text">导航二</span>
</template>
<MenuItem name="2-1">选项 1</MenuItem>
<MenuItem name="2-2">选项 2</MenuItem>
</Submenu>
<Submenu name="3">
<template slot="title">
<Icon type="ios-analytics" :size="iconSize"></Icon>
<span class="layout-text">导航三</span>
</template>
<MenuItem name="3-1">选项 1</MenuItem>
<MenuItem name="3-2">选项 2</MenuItem>
</Submenu>
</Menu>
</Affix>
</Col>
<Col :span="spanRight">
<Affix>
<div class="layout-header">
<Button type="text" @click="toggleClick">
<Icon type="navicon" size="32"></Icon>
</Button>
<div class="layout-ceiling-main">
<a href="#">注册登录</a> |
<a href="#">帮助中心</a> |
<a href="#">安全中心</a> |
<a href="#">服务大厅</a>
</div>
</div>
<div class="layout-breadcrumb">
<Breadcrumb>
<BreadcrumbItem href="#">首页</BreadcrumbItem>
<BreadcrumbItem href="#">应用中心</BreadcrumbItem>
<BreadcrumbItem>某应用</BreadcrumbItem>
</Breadcrumb>
</div>
</Affix>
<div class="layout-content">
<div class="layout-content-main">内容区域</div>
<BackTop></BackTop>
</div>
<div class="layout-copy">
2011-2016 © TalkingData
</div>
</Col>
</Row>
</div>
</template>
<script>
export default {
data() {
return {
spanLeft: 5,
spanRight: 19
}
},
computed: {
iconSize() {
return this.spanLeft === 5 ? 14 : 24;
}
},
methods: {
toggleClick() {
if (this.spanLeft === 5) {
this.spanLeft = 2;
this.spanRight = 22;
} else {
this.spanLeft = 5;
this.spanRight = 19;
}
}
}
}
</script>
<style scoped>
.layout {
border: 1px solid #d7dde4;
background: #f5f7f9;
position: relative;
border-radius: 4px;
overflow: hidden;
}
.layout-breadcrumb {
padding: 10px 15px 0;
}
.layout-content {
/* max-height: 600px;*/
min-height: 200px;
margin: 15px;
overflow-x: auto;
background: #fff;
border-radius: 4px;
}
.layout-content-main {
padding: 10px;
}
.layout-menu-left {
background: #464c5b;
}
.layout-logo-left {
width: 90%;
height: 30px;
background: #5b6270;
border-radius: 3px;
margin: 15px auto;
}
.layout-ceiling-main a {
color: #9ba7b5;
}
.layout-hide-text .layout-text {
display: none;
}
.ivu-col {
transition: width .2s ease-in-out;
}
.layout-logo {
width: 100px;
height: 30px;
background: #5b6270;
border-radius: 3px;
float: left;
position: relative;
top: 15px;
left: 20px;
}
.layout-header {
height: 60px;
background: #fff;
box-shadow: 0 1px 1px rgba(0, 0, 0, .1);
}
.layout-copy {
text-align: center;
padding: 10px 0 20px;
color: #9ea7b4;
}
.layout-ceiling {
background: #464c5b;
padding: 10px 0;
overflow: hidden;
}
.layout-ceiling-main {
float: right;
margin: 15px;
}
</style>
修改main.js
import App from './main.vue';
Button: i-button
Col: i-col
Table: i-table
Input: i-input
Form: i-form
Menu: i-menu
Select: i-select
Option: i-option
Progress: i-progress
以下组件,在所有模式下,必须加前缀 i-,除非使用 iview-loader:
Switch: i-switch
Circle: i-circle
import VueI18n from 'vue-i18n';
Vue.use(VueI18n);
import iView from 'iview';
import zhLocale from 'iview/dist/locale/zh-CN';
import enLocale from 'iview/dist/locale/en-US';
//Vue.use(iView, { zhLocale });
Vue.use(iView);
Vue.config.lang = 'zh-CN';
Vue.locale('zh-CN', zhLocale);
Vue.locale('en-US', enLocale);
@import '~iview/src/styles/index.less';
// 下面是要覆盖的变量:
@primary-color: #8c0776;
main.js
import '../my-theme/index.less';
2.安装工具修改 安装工具: npm install iview-theme -g
新建目录,初始化主题: iview-theme init my-theme
最后编辑 my-theme/custom.less
文件
编译:cd my-theme&&iview-theme build -o dist/
在 main.js :import '../my-theme/dist/iview.css';
npm install iview-loader --save-dev
module: {
rules: [ {
test: /\.vue$/,
use: [{
loader: 'vue-loader',
options: {
loaders: {
'scss': 'vue-style-loader!css-loader!sass-loader',
'sass': 'vue-style-loader!css-loader!sass-loader?indentedSyntax'
}
}
},
{
loader: 'iview-loader',
options: {
prefix: true // <Switch> 和 <Circle>,设为 true 后, <i-row>、<i-select>
}
}
]
}
]
}