AT-UIの入手某東的UI框架

leasin發表於2019-03-31

Element-UI做了兩個後臺,時間長了有點視覺勞累,挑來挑去,入選了某東的AT-UI,搭建了一個雛形的後臺骨架,下面記錄一下使用方法(此時應附地址:官網傳送門)。

本人是在vue2.0的基礎上搭建,所以,謹記:先搭建好初步的專案檔案,再來看下面的介紹。

我就想在這裡插一張圖

安裝

npm install at-ui

npm install at-ui-style
複製程式碼

是的,它需要安裝兩個,而Element只需要安裝一個,AT-UI的css依賴檔案也需要安裝, 然後在main.js中引入就可以了。

import AtComponents from 'at-ui'
import 'at-ui-style' // 引入元件樣式
Vue.use(AtComponents)
Vue.config.productionTip = false
複製程式碼

它封裝的元件,巢狀不是很多,有的也是直接在原dom元素上直接新增class的方式,所以從控制檯也不難找到其樣式的。

由於AT-UI只是基本封裝,比如,日期選擇器datePicker、樹形元件tree等就沒有,所以有的控制元件就需要自行安裝,推薦一個vue元件網站:vueExamples。搭建時所需外掛及使用方法,會在後面寫到。

搭建及外掛引入

頁面檔案建好,本人習慣如下圖:

目錄圖

  • view目錄以資料夾分類
  • svg目錄放入svg圖示,圖示去阿里向量庫fontawesome等網站找就好了。
  • 模擬資料的話,圖方便,沒有裝mock,在static目錄下新建一個data.js,把json資料扔進去就好,記得export出來哦。
  • 路由的話,如果涉及到選單的許可權,最好還是用動態路由比較好。

svg

安裝

npm i vue-svg-icon -D
複製程式碼

引入

main.js中引入vue-svg-icon

import Icon from 'vue-svg-icon/Icon.vue';
Vue.component('icon', Icon);
複製程式碼

不用管為什麼有個Icon.vue路徑,外掛封裝的時候,通過它匯出的。 然後就是目錄圖中的svg資料夾了,注意它的位置,和assets並列,新建一個search.svg, 放入找好的svg程式碼:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" id="icon-search" width="100%" height="100%"><path d="M124.884 109.812L94.256 79.166c-.357-.357-.757-.629-1.129-.914a50.366 50.366 0 0 0 8.186-27.59C101.327 22.689 78.656 0 50.67 0 22.685 0 0 22.688 0 50.663c0 27.989 22.685 50.663 50.656 50.663 10.186 0 19.643-3.03 27.6-8.201.286.385.557.771.9 1.114l30.628 30.632a10.633 10.633 0 0 0 7.543 3.129c2.728 0 5.457-1.043 7.543-3.115 4.171-4.157 4.171-10.915.014-15.073M50.671 85.338C31.557 85.338 16 69.78 16 50.663c0-19.102 15.557-34.661 34.67-34.661 19.115 0 34.657 15.559 34.657 34.675 0 19.102-15.557 34.661-34.656 34.661"></path></svg>
複製程式碼

然後在使用的地方直接用就好了。

 <icon name="search" scale="18"></icon>
複製程式碼

jquery

從理論上講,vue框架理應不需要jquery的,但是為了處理某些特殊的地方,還是引一下吧(除非確實不需要)。

安裝

cnpm install jquery -S
複製程式碼

引入

  • 修改 main.js
import $ from 'jquery'
複製程式碼
  • 修改webpack.base.conf.js
var webpack = require('webpack') //最上面並列新增

module.exports = {
  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
      'jquery':'jquery' //新增該項
    }
  },
  plugins:[ //新增一個plugin是webpack.ProvidePlugin,寫到resolve並列的位置
    new webpack.ProvidePlugin({ 
      $: 'jquery',
      jquery: 'jquery'
    })
  ],
}
複製程式碼

重啟

重啟之後,使用方法和原jquery一樣。

less

安裝

npm i less less-loader --save
npm i sass-resources-loader
複製程式碼

修改檔案

//build/utils.js

function lessResourceLoader() { // 增加全域性使用less函式
    var loaders = [
        cssLoader,
        'less-loader',
        {
            loader: 'sass-resources-loader',
            options: {
                resources: [
                path.resolve(__dirname, '../src/assets/css/common.less'), //定義全域性變數的檔案路徑
                ]
            }
        }
    ];
    if (options.extract) {
        return ExtractTextPlugin.extract({
            use: loaders,
            fallback: 'vue-style-loader'
        })
    } else {
        return ['vue-style-loader'].concat(loaders)
    }
}

return {
    css: generateLoaders(),
    postcss: generateLoaders(),
    // less: generateLoaders('less'),//原配置
    less: lessResourceLoader(), //更改後的配置
    sass: generateLoaders('sass', { indentedSyntax: true }),
    scss: generateLoaders('sass'),
    stylus: generateLoaders('stylus'),
    styl: generateLoaders('stylus')
  }
複製程式碼

注意:lessResourceLoader()要和generateLoaders()同級!

重啟專案

然後就可以在src/assets/css/common.less檔案裡寫全域性less了。

datepicker

安裝

npm install vue-datepicker-local
複製程式碼

使用

<template>
  <vue-datepicker-local v-model="time" />
</template>

<script>
import VueDatepickerLocal from 'vue-datepicker-local'

export default {
  components: {
    VueDatepickerLocal
  },
  data () {
    return {
      time: new Date()
    }
  }
}
</script>
複製程式碼

具體使用檢視詳細文件:github傳送門

開始

路由初始化

先給一個預設的路由配置:

import Vue from 'vue'
import Router from 'vue-router'
import login from '@/view/login/login.vue';
import notFound from '@/view/404/404.vue';
import layout from '@/view/layout/layout.vue';
Vue.use(Router)

const router = new Router({
	routes: [{
		path: '/login',
		name: '登入',
		component: login,
	},{
		path: '/',
		name: '',
		component: layout,
		children:[{
			path:'404',
			name:'404',
			component:notFound
		}]
	}, {
      path: '*', 
      redirect: '/404'
    }]
})
export default router;
複製程式碼

由於是使用動態路由,所以就需要動態引入的寫法:

  1. 一般來說,後臺管理系統都是左側選單控制路由跳轉,所以,src/components/新建一個leftSlider.vue
  2. 引入的路由格式應該和router.js保持一致,也是下面的格式:
{
    path: '/',
    name: '',
    component: layout,
    children:[{
        path:跳轉路徑,
        name:路由名稱,
        component:元件
    }]
}
複製程式碼

所以,模擬一個json資料使用就好,後期換成介面讀取就行了。

左側選單封裝如下:

<template>
    <at-menu :activeName="$route.name" inline-collapsed router theme="dark">
        <template v-for="(item,index) in routeList">
            <!--有子路由-->
            <at-submenu  v-if="item.children.length>1" :key="index" >
                <template slot="title">
                    <i class="icon icon-life-buoy"></i>
                    {{item.name}}
                </template>
                <at-menu-item  v-for="(it,inde) in item.children" :key="inde" :to="it.path" >
                    {{it.name}}
                </at-menu-item>
            </at-submenu>
            <!--無子路由-->
            <at-menu-item :to="item.children[0].path" v-else :key="index">
                <i class="icon icon-life-buoy"></i> 
                {{item.children[0].name}}
            </at-menu-item>
        </template>
    </at-menu>
</template>

<script>
    export default {
        computed: {
            routeList() {
                return this.$store.state.global.routes
            },
        },
        mounted(){
            this.openedSubmenu()
        },
        watch: {
            '$route' (to, from) {
               this.openedSubmenu()
            }
        },
        methods:{
            openedSubmenu(){
                let list = this.$store.state.global.routes;
                for(var key in list){
                    for(var jey in list[key].children){
                        if(list[key].children[jey].name == this.$route.name){
                            $(".at-menu.at-menu--dark.at-menu--inline>li:eq("+key+")")
                                .addClass('at-menu__submenu--active at-menu__submenu--opened')
                                .children('ul').show();
                            $(".at-menu.at-menu--dark.at-menu--inline>li:eq("+key+")").siblings()
                                .removeClass('at-menu__submenu--active at-menu__submenu--opened')
                                .children('ul').hide();
                                return;
                        }
                    }
                }
            },
        }
    }
</script>

<style lang="less" scoped>
    .at-menu.at-menu--dark.at-menu--inline {
        width: 240px;
        position: fixed;
        height: 100%;
        transition: width .28s;
        top: 0;
        bottom: 0;
        left: 0;
        overflow: hidden;
    }
</style>

複製程式碼

這邊不想用jquery的,但是AT-UI有一個坑,就是opened屬性不可以動態展開收起,沒辦法,想要這個效果,所以用了jquery控制它的類名模擬這個效果。

注意一點,路由引入只是資料引入,然而元件是需要引入檔案而不是單純的資料的, 所以:src/utils新建lazyload.jsleftSliderRoute.js

//lazyload.js
export default (folder,name) => () => import(`@/view/${folder}/${name}.vue`)

//leftSliderRoute.js
import lazyload from './lazyload.js';
export default (routes, data) => {
	generaMenu(routes, data)
}
function generaMenu(routers, data) {
	if (data) {
		data.forEach((item) => {
			let menu = Object.assign({}, item);
			let folder = componentFromFolder(menu.component);
			menu.component = lazyload(folder, menu.component)
			if (item.children) {
				menu.children = []
				generaMenu(menu.children, item.children)
			}
			routers.push(menu)
		})
	}
}
function componentFromFolder(val) {
	switch (val) {
		case 'agentList':
		case 'agency':
		case 'agency2':
		case 'agency3':
			return 'agencyManage';

		case 'agentApply':
		case 'customList':
			return 'customManage';

		case 'financeOverview':
		case 'withdrawApply':
		case 'withdrawCancel':
		case 'withdrawSuccess':
			return 'financeManage';
		case 'home':
			return 'home';
		case 'layout':
			return 'layout'

	}
}
複製程式碼

通過檔案匹配資料夾,然後import進來元件就好。記得在登入的時候呼叫一下:

import leftSliderRoute from "@/utils/leftSliderRoute.js";
    var rou = [];
    export default {
        data() {
            return {
                username: '',
                password: '',
            }
        },
        methods: {
            login() {
                if (!this.username) {
                    this.$Message.error("請輸入賬號");
                    return;
                }
                if (!this.password) {
                    this.$Message.error("請輸入密碼");
                    return;
                }
                window.sessionStorage.setItem('routeList', JSON.stringify(routes));
                this.$store.commit('addRouteList',routes);//寫到vuex裡
                leftSliderRoute(rou,routes);
                this.$router.addRoutes(rou);
                this.$router.push('/home');
                this.$Message.success('登入成功');
            }
        }
    }
複製程式碼

然後需要注意一點,路由是在登入之後從vuex裡拿的,但是使用者重新整理頁面,不可能再讀取介面然後再塞進vuex裡,所以得存到快取中,防止使用者重新整理,改造main.js

import store from './store';
import leftSliderRoute from './utils/leftSliderRoute.js';
let routeList = JSON.parse(window.sessionStorage.getItem('routeList'));
if (routeList) {
	//這裡是防止使用者手動重新整理頁面,整個app要重新載入,動態新增的路由,會消失,所以我們重新add一次
	let routes = [];
	leftSliderRoute(routes, routeList);
	router.addRoutes(routes);
	store.commit('addRouteList', routes);
} else {
	router.push('/login');
}
複製程式碼

至此,動態引入路由就完成了。

麵包屑導航

其實這個東西還是依賴路由那個data,因為要讀取children,還是內部的,要麼改造資料,要麼就老老實實寫:

//main.js
router.beforeEach((to, from, next) => {
	//麵包屑導航開始
	let arr = [{
		name: '首頁',
		path: '/home'
	}];
	to.matched.map(item => {
		item.path != '/home' && item.path && arr.push({
			name: item.name,
			path: item.path
		})
	})
	store.commit('setBread', arr) //寫進vuex
	next()
	//導航結束
})
複製程式碼

然後使用就好了。

<at-breadcrumb>
    <at-breadcrumb-item :to="index == breadList.length-1 ?'':item.path" v-for="(item,index) in breadList" :key="index">{{item.name}}</at-breadcrumb-item>
</at-breadcrumb>
複製程式碼

覆蓋樣式

很多時候框架內部的樣式不符合使用,所以需要這樣:

.operate {
    min-width: 100px;
    display: flex;
    align-items: center;
    /deep/ input {
        font-size: 13px;
        border: none;
        border-bottom: 1px solid #d9d9d9;
        border-radius: 0;
        margin: 0 8px;
    }
}
複製程式碼

使用/deep/可以修改元件自帶樣式,但是一定要上一層一定要有class

清空表單

在有table的元件裡,查詢是必不可少的,經常會有清空查詢條件的操作,有兩個方法:

  • 通過JSON.parse(JSON.stringify(data))的格式
  • 通過Object.assign({},data)的格式

兩個都是通過深拷貝,在created的時候賦值一個預設資料,作為備用,在清空的時候賦值就好了。

要注意:每次賦值都應該是深拷貝。

table

<template>
    <div class="withdrawApply">
        <div class="searchForm">
            <form class="row at-row" ref="searchForm">
                <at-select v-model="form.select"  class="col-md-2" size="large" clearable placeholder="地區">
                    <at-option value="1">深圳</at-option>
                </at-select>
                <at-input class="col-md-3" v-model="form.keyword" placeholder="關鍵字" ></at-input>
                <vue-datepicker-local  class="col-md-3" placeholder="開始日期" v-model="form.beginTime" clearable format="YYYY-MM-DD HH:mm:ss"/>
                <vue-datepicker-local  class="col-md-3" placeholder="結束日期" v-model="form.endTime" clearable format="YYYY-MM-DD HH:mm:ss"/>
                <at-button  class="col-md-1" type="primary" @click="$Message('查詢')">查詢</at-button>
                <at-button  class="col-md-1" type="warning" @click="clearForm">清空</at-button>
            </form>
        </div>
        <at-table 
        :columns="[{title:'id',key:'id'},{title:'提現金額',key:'a'},{title:'申請時間',key:'b'},{title:'機構名稱',key:'c'},{title:'申請人',key:'d'},
                {title:'銀行卡號',key:'e'},{title:'分行',key:'f'},
                {title:'狀態', render:(h, params) => {
                    return h('span',{},filterState(params.item))
                }},
                {title:'操作', render: (h, params) => {
                    return h('div', [
                        h('AtButton', {
                            props: {
                                size: 'smaller',
                                type:'error'
                            },
                            style,
                            on: {
                                click: this.refuse
                            }
                        }, '拒絕'),
                        h('AtButton', {
                            props,
                            style,
                            on: {
                                click: this.checkPay
                            }
                        }, '線下打款')
                    ])
                }}]" 
        :data="data" border pagination :page-size="8"></at-table>
    </div>
</template>

<script>
import tableData from '../../../static/tableData.js'; //表格資料存放
import VueDatepickerLocal from 'vue-datepicker-local'
import {mapState} from 'vuex';
    export default {
        components:{
            VueDatepickerLocal
        },
        data() {
            return {
                data:tableData,
                defaultForm:null,//用來保留空表單
                form:{
                    select:'',
                    keyword:'',
                    begTime:'',
                    endTime:''
                },
            }
        },
        computed:{
            //寫了一個預設屬性配置,懶得一次一次寫,就扔vuex了
            ...mapState({
                 props:state=>state.table.props,
                 style:state=>state.table.style
            })
        },
        created(){
            //賦值預設
            this.defaultForm = Object.assign({},this.form);
        },
        methods: {
            filterState(val){
                //型別變換使用
                return val ? 'return 2':'return 1'
            },
            clearForm(){
                this.form = Object.assign({},this.defaultForm);
            },
            refuse(){
                this.$Modal.confirm({
                    title: '提示',
                    content: '是否拒絕?',
                    cancelText:'取消',
                    okText:'確定',
                    }).then(() => {
                        this.$Message.success('拒絕成功')
                    }).catch(() => {
                       
                })
            },
            checkPay(){
                this.$Modal.confirm({
                    title: '提示',
                    content: '您確認稽核打款麼',
                    cancelText:'取消',
                    okText:'確定',
                    }).then(() => {
                        this.$Message.success('打款成功')
                    }).catch(() => {
                       
                })
            }
        }
    }
</script>
複製程式碼

放出來一個元件,應該沒什麼問題了。

附加

  1. 還是layout元件吧:
<!--嗯,承重牆就長這個樣子。-->
<template>
    <div class="layout">
        <leftSlider></leftSlider>
        <div class="layout-container">
            <headerTop></headerTop>
            <router-view class="main"></router-view>
        </div>
    </div>
</template>

<script>
    import headerTop from '@/components/header.vue';
    import leftSlider from '@/components/leftSlider.vue';
    export default {
        components: {
            leftSlider,
            headerTop,
        }
    }
</script>
複製程式碼
  1. 新增了全屏效果,借鑑網上的。
function fullScreen() {
	console.log(123)
	var el = document.documentElement;
	var rfs = el.requestFullScreen || el.webkitRequestFullScreen ||
		el.mozRequestFullScreen || el.msRequestFullScreen;
	if (typeof rfs != "undefined" && rfs) {
		rfs.call(el);
	} else if (typeof window.ActiveXObject != "undefined") {
		//for IE,這裡其實就是模擬了按下鍵盤的F11,使瀏覽器全屏
		var wscript = new ActiveXObject("WScript.Shell");
		if (wscript != null) {
			wscript.SendKeys("{F11}");
		}
	}
}

function exitFullScreen() {
	console.log(456)
	var el = document;
	var cfs = el.cancelFullScreen || el.webkitCancelFullScreen ||
		el.mozCancelFullScreen || el.exitFullScreen;
	if (typeof cfs != "undefined" && cfs) {
		cfs.call(el);
	} else if (typeof window.ActiveXObject != "undefined") {
		//for IE,這裡和fullScreen相同,模擬按下F11鍵退出全屏
		var wscript = new ActiveXObject("WScript.Shell");
		if (wscript != null) {
			wscript.SendKeys("{F11}");
		}
	}
}

複製程式碼
  1. 如果你報錯Failed to mount component: template or render function not defined. 不妨可以看看報錯指南

額外使用

1.常用過濾,用來前端搜尋

searchInputEvent() {
    let result = this.searchInput ?
        this.querySearchArr.filter(item => {
            return (item.name.toLowerCase().indexOf(this.searchInput.toLowerCase()) > -1)
        }) : this.searchData;
    this.searchData = result;
}
複製程式碼

結尾

差不多一個後臺也就這麼多東西了,其他的就是按業務需求來改。17年出的框架,但是18年也就更新了一次……?,總體感覺AT-UI東西不多,不過體驗還不錯。

相關文章