一、一些常用组件效果的实现
1)面包屑导航
主要是使用$route.mathed:一个数组,包含当前路由的所有嵌套路径片段的路由记录 。路由记录就是 routes
配置数组中的对象副本 (还有在 children
数组)。
类似的有一个router.getMatchedComponents(location?):返回目标位置或是当前路由匹配的组件数组(是数组的定义/构造类,不是实例)。通常在服务端渲染的数据预加载时时候。
(还记的vue-router中router,route,routes的区别么?)
1)router:是指一个vue-router的实例
2)route:路由信息对象,表示当前激活的路由的状态信息,可以出现
-
-
在组件内,即
this.$route
-
在
$route
观察者回调内 -
router.match(location)
的返回值 -
导航守卫的参数:
router.beforeEach((to, from, next) => { // `to` 和 `from` 都是路由对象 })
-
scrollBehavior
方法的参数:const router = new VueRouter({ scrollBehavior (to, from, savedPosition) { // `to` 和 `from` 都是路由对象 } })
-
3)routes:router的构造配置项
routes
-
-
类型:
Array<RouteConfig>
RouteConfig
的类型定义:declare type RouteConfig = { path: string; component?: Component; name?: string; // 命名路由 components?: { [name: string]: Component }; // 命名视图组件 redirect?: string | Location | Function; props?: boolean | string | Function; alias?: string | Array<string>; children?: Array<RouteConfig>; // 嵌套路由 beforeEnter?: (to: Route, from: Route, next: Function) => void; meta?: any; // 2.6.0+ caseSensitive?: boolean; // 匹配规则是否大小写敏感?(默认值:false) pathToRegexpOptions?: Object; // 编译正则的选项 }
-
<template> <el-breadcrumb class="app-levelbar" separator="/"> <el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path"> <router-link v-if='item.redirect==="noredirect"||index==levelList.length-1' to="" class="no-redirect">{{item.name}}</router-link> <router-link v-else :to="item.redirect||item.path">{{item.name}}</router-link> </el-breadcrumb-item> </el-breadcrumb> </template> <script> export default { created() { this.getBreadcrumb() }, data() { return { levelList: null } }, methods: { getBreadcrumb() { // console.log('this.$route.matched', this.$route.matched); let matched = this.$route.matched.filter(item => item.name); const first = matched[0]; if (first && (first.name !== '首页' || first.path !== '')) { matched = [{ name: '首页', path: '/' }].concat(matched) } this.levelList = matched; } }, watch: { $route() { this.getBreadcrumb(); } } }
2)历史访问记录TabView
使用vuex记录app全局的一些状态参数,诸如导航栏是否收起,浏览路径等等
import Cookies from 'js-cookie'; const app = { state: { sidebar: { opened: !+Cookies.get('sidebarStatus') }, theme: 'default', livenewsChannels: Cookies.get('livenewsChannels') || '[]', visitedViews: [] }, mutations: { TOGGLE_SIDEBAR: state => { if (state.sidebar.opened) { Cookies.set('sidebarStatus', 1); } else { Cookies.set('sidebarStatus', 0); } state.sidebar.opened = !state.sidebar.opened; }, ADD_VISITED_VIEWS: (state, view) => { if (state.visitedViews.some(v => v.path === view.path)) return state.visitedViews.push({ name: view.name, path: view.path }) }, DEL_VISITED_VIEWS: (state, view) => { let index for (const [i, v] of state.visitedViews.entries()) { if (v.path === view.path) { index = i break } } state.visitedViews.splice(index, 1) } }, actions: { ToggleSideBar: ({ commit }) => { commit('TOGGLE_SIDEBAR') }, addVisitedViews: ({ commit }, view) => { commit('ADD_VISITED_VIEWS', view) }, delVisitedViews: ({ commit }, view) => { commit('DEL_VISITED_VIEWS', view) } } }; export default app;
如上所示,通过visitedViews这个state记录浏览历史记录,需要的时候遍历这个数组即可,watch $route 每一次路由更改时,向数组中压入一条记录
<template> <div class='tabs-view-container'> <router-link class="tabs-view" v-for="tag in Array.from(visitedViews)" :to="tag.path" :key="tag.path"> <el-tag :closable="true" :type="isActive(tag.path)?'primary':''" @close='closeViewTabs(tag,$event)'> {{tag.name}} </el-tag> </router-link> </div> </template> <script> export default { computed: { visitedViews() { return this.$store.state.app.visitedViews.slice(-6) } }, methods: { closeViewTabs(view, $event) { this.$store.dispatch('delVisitedViews', view) $event.preventDefault() }, generateRoute() { if (this.$route.matched[this.$route.matched.length - 1].name) { return this.$route.matched[this.$route.matched.length - 1] } this.$route.matched[0].path = '/' return this.$route.matched[0] }, addViewTabs() { this.$store.dispatch('addVisitedViews', this.generateRoute()) }, isActive(path) { return path === this.$route.path } }, watch: { $route() { this.addViewTabs() } } } </script> <style rel="stylesheet/scss" lang="scss" scoped> .tabs-view-container { display: inline-block; vertical-align: top; margin-left: 10px; .tabs-view { margin-left: 10px; } } </style>
3)全屏显示
hmtl5有相关的screenfull的API,只不过兼容性并不好。可以借助第三方库,如https://github.com/sindresorhus/screenfull.js,使用起来其实很简单
<template> <svg @click='click' class="icon screenfull" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" t="1497503607356" viewBox="0 0 1024 1024" version="1.1" p-id="4109" :fill='fill' :width="width" :height="height"> <path d="M604.157933 512l204.484208 204.484208 82.942037-82.942037c10.364045-10.952446 26.498514-13.83817 40.309054-8.067746 13.249769 5.742794 22.465664 18.99154 22.465664 33.977859l0 258.042008c0 20.168342-16.695241 36.863582-36.863582 36.863582L659.452283 954.357873c-14.986319 0-28.236088-9.215896-33.977859-23.025413-5.770424-13.249769-2.885723-29.384237 8.067746-39.748283l82.942037-82.942037L512 604.157933 307.515792 808.642141l82.942037 82.942037c10.952446 10.364045 13.83817 26.498514 8.067746 39.748283-5.742794 13.809517-18.99154 23.025413-33.977859 23.025413L106.504686 954.357873c-20.168342 0-36.863582-16.695241-36.863582-36.863582L69.641103 659.452283c0-14.986319 9.215896-28.236088 23.025413-33.977859 13.249769-5.770424 29.384237-2.8847 39.748283 8.067746l82.942037 82.942037 204.484208-204.484208L215.357859 307.515792l-82.942037 82.942037c-6.890944 6.918573-16.10684 10.952446-25.911136 10.952446-4.593622 0-9.804297-1.14815-13.83817-2.8847-13.809517-5.742794-23.025413-18.99154-23.025413-33.977859L69.641103 106.504686c0-20.168342 16.695241-36.863582 36.863582-36.863582L364.546693 69.641103c14.986319 0 28.236088 9.215896 33.977859 23.025413 5.770424 13.249769 2.8847 29.384237-8.067746 39.748283l-82.942037 82.942037 204.484208 204.484208L716.484208 215.357859l-82.942037-82.942037c-10.952446-10.364045-13.83817-26.498514-8.067746-39.748283 5.742794-13.809517 18.99154-23.025413 33.977859-23.025413l258.042008 0c20.168342 0 36.863582 16.695241 36.863582 36.863582l0 258.042008c0 14.986319-9.215896 28.236088-22.465664 33.977859-4.593622 1.736551-9.804297 2.8847-14.397918 2.8847-9.804297 0-19.020192-4.033873-25.911136-10.952446l-82.942037-82.942037L604.157933 512z" p-id="4110" /> </svg> </template> <script> import screenfull from 'screenfull'; export default { name: 'hamburger', props: { { type: Number, default: 22 }, height: { type: Number, default: 22 }, fill: { type: String, default: '#48576a' } }, data() { return { isFullscreen: false } }, methods: { click() { if (!screenfull.enabled) { this.$message({ message: 'you browser can not work', type: 'warning' }); return false; } screenfull.toggle(); } } } </script> <style scoped> .screenfull { display: inline-block; cursor: pointer; vertical-align: -0.15em; } </style>
二、一些常用的第三方库
1)富文本编辑器:tinyMCE
2)markdown编辑器:simplemde-markdown-editor,markdown的转换使用了shutdown
3)json编辑器(代码编辑器): CodeMirror
4)列表拖拽:vue.draggalbe(基于sortable)
三、一些常用的文本过滤方法(包括时间格式化方法等)
function pluralize(time, label) { if (time === 1) { return time + label } return time + label + 's' } export function timeAgo(time) { const between = Date.now() / 1000 - Number(time); if (between < 3600) { return pluralize(~~(between / 60), ' minute') } else if (between < 86400) { return pluralize(~~(between / 3600), ' hour') } else { return pluralize(~~(between / 86400), ' day') } } export function parseTime(time, cFormat) { if (arguments.length === 0) { return null; } if ((time + '').length === 10) { time = +time * 1000 } const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'; let date; if (typeof time == 'object') { date = time; } else { date = new Date(parseInt(time)); } const formatObj = { y: date.getFullYear(), m: date.getMonth() + 1, d: date.getDate(), h: date.getHours(), i: date.getMinutes(), s: date.getSeconds(), a: date.getDay() }; const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { let value = formatObj[key]; if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1]; if (result.length > 0 && value < 10) { value = '0' + value; } return value || 0; }); return time_str; } export function formatTime(time, option) { time = +time * 1000; const d = new Date(time); const now = Date.now(); const diff = (now - d) / 1000; if (diff < 30) { return '刚刚' } else if (diff < 3600) { // less 1 hour return Math.ceil(diff / 60) + '分钟前' } else if (diff < 3600 * 24) { return Math.ceil(diff / 3600) + '小时前' } else if (diff < 3600 * 24 * 2) { return '1天前' } if (option) { return parseTime(time, option) } else { return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分' } } /* 数字 格式化*/ export function nFormatter(num, digits) { const si = [ { value: 1E18, symbol: 'E' }, { value: 1E15, symbol: 'P' }, { value: 1E12, symbol: 'T' }, { value: 1E9, symbol: 'G' }, { value: 1E6, symbol: 'M' }, { value: 1E3, symbol: 'k' } ]; for (let i = 0; i < si.length; i++) { if (num >= si[i].value) { return (num / si[i].value + 0.1).toFixed(digits).replace(/.0+$|(.[0-9]*[1-9])0+$/, '$1') + si[i].symbol; } } return num.toString(); } export function html2Text(val) { const div = document.createElement('div'); div.innerHTML = val; return div.textContent || div.innerText; } export function toThousandslsFilter(num) { return (+num || 0).toString().replace(/^-?d+/g, m => m.replace(/(?=(?!)(d{3})+$)/g, ',')); }
在main.js中引入:
import * as filters from './filters'; // 全局vue filter Object.keys(filters).forEach(key => { Vue.filter(key, filters[key]) });