赞
踩
1 安装View UI组件
1.1 什么是View UI
后台管理信息系统(MIS)是软件开发的一个重要领域,如OA、ERP、商城后台等等都属于MIS系统。业务人员需要在MIS系统中操作大量的表单和数据,传统的服务器(同步)页面伴随着大量刷新,用户体验很差,所以开发界喜欢选择以vue为代表的新一代前后端分离技术以实现流程的操作。
在MIS系统开发中,需要大量的表单、表格、日历、选项卡等复杂组件来完成业务功能,这些组件实现起来都比较复杂,作为普通程序员,一般会选择现成的商业组件。
业界比较成熟的后台商业组件主要有:Element UI、View UI 和 Ant Design,这些组件的功能和使用方式都大同小异,这里介绍View UI给大家使用。
官网:iView - A high quality UI Toolkit based on Vue.js
View UI的前身是iView,至今已经是4.0版,由于最新版引入了商业模式,因此代码更新较快。
1.2 安装View UI
官方的安装教程:https://www.iviewui.com/docs/guide/install
为vue项目安装View UI组件有很多方式,这里使用最简便得方法,就是直接使用vue-cli得ui向导来完成安装。新版得vue-cli不仅可以通过命令行来完成项目创建,还可以通过web可视化方式创建,View UI可以作为vue-cli的插件添加到项目中。
(1)使用vue-cli可视化项目管理器
在命令行中输入以下指令:
vue ui
(2)添加插件:axios 和 view-ui
完成上述操作后,一个包含view-ui插件库的vue工程就创建好了,正常进入项目目录执行:
npm run serve
2 项目布局:
2.1 栅格系统
类似BootStrap中的12栅格系统,View UI通用把页面分为行(Row)和列(Col),使用24栅格进行布局。
官方的栅格教程:https://www.iviewui.com/components/grid
2.2 设置路由:
(1)分层次创建组件
(2)设置父子级别路由:
- const routes = [
- 2
- {
- 3
- path: '/',
- 4
- name: 'home',
- 5
- component: Home,
- 6
- children:[
- 7
- {
- 8
- path:'/',
- 9
- name:'default',
- 10
- component:Default,
- 11
- meta:{
- 12
- title: '后台首页'
- 13
- }
- 14
- },
- 15
- {
- 16
- path:'/categories',
- 17
- name:'categories',
- 18
- component:Categories,
- 19
- meta:{
- 20
- title: '分类管理'
- 21
- }
- 22
- },
- 23
- {
- 24
- path:'/products',
- 25
- name:'products',
- 26
- component:Products,
- 27
- meta:{
- 28
- title: '商品管理'
- 29
- }
- 30
- },
- 31
- ]
- 32
- },
- 33
- {
- 34
- path:'/login',
- 35
- name:'login',
- 36
- component:Login
- 37
- }
- 38
- ]
2.3 布局
业务系统通常由比较严谨的布局,View UI为我们准备好了多种布局风格,这里使用“顶部-侧边布局”作为示例:
https://www.iviewui.com/components/layout#DB-CBBJ
- <style scoped>
- 2
- ......
- 3
- .layout-logo{
- 4
- ......
- 5
- color:#fff;
- 6
- line-height: 30px;
- 7
- text-align: center;
- 8
- }
- 9
- ......
- 10
- </style>
- 11
- <template>
- 12
- <div class="layout">
- 13
- <Layout>
- 14
- <Header>
- 15
- <Menu mode="horizontal" theme="dark" active-name="1">
- 16
- <div class="layout-logo">
- 17
- <router-link to="/"><h3>趣物网 - 后台管理</h3></router-link>
- 18
- </div>
- 19
- <div class="layout-nav">
- 20
- <MenuItem name="1">
- 21
- <Icon type="ios-navigate"></Icon>
- 22
- Item 1
- 23
- </MenuItem>
- 24
- <MenuItem name="2">
- 25
- <Icon type="ios-keypad"></Icon>
- 26
- Item 2
- 27
- </MenuItem>
- 28
- <MenuItem name="3">
- 29
- <Icon type="ios-analytics"></Icon>
- 30
- Item 3
- 31
- </MenuItem>
- 32
- <MenuItem name="4">
- 33
- <Icon type="ios-paper"></Icon>
- 34
- Item 4
- 35
- </MenuItem>
- 36
- </div>
- 37
- </Menu>
- 38
- </Header>
- 39
- <Layout :style="{padding: '0 50px'}">
- 40
- <Breadcrumb :style="{margin: '16px 0'}">
- 41
- <BreadcrumbItem>后台管理</BreadcrumbItem>
- 42
- <BreadcrumbItem>{{$route.meta.title}}</BreadcrumbItem>
- 43
- </Breadcrumb>
- 44
- <Content :style="{padding: '24px 0', minHeight: '280px', background: '#fff'}">
- 45
- <Layout>
- 46
- <Sider hide-trigger :style="{background: '#fff'}">
- 47
- <Menu active-name="1-2" theme="light" width="auto" :open-names="['1']">
- 48
- <Submenu name="1">
- 49
- <template slot="title">
- 50
- <Icon type="ios-navigate"></Icon>
- 51
- 商品信息管理
- 52
- </template>
- 53
- <MenuItem name="1-1">
- 54
- <router-link to="/categories">分类管理</router-link>
- 55
- </MenuItem>
- 56
- <MenuItem name="1-2">
- 57
- <router-link to="/products">商品管理</router-link>
- 58
- </MenuItem>
- 59
- </Submenu>
- 60
- <Submenu name="2">
- 61
- <template slot="title">
- 62
- <Icon type="ios-analytics"></Icon>
- 63
- 客户订单管理
- 64
- </template>
- 65
- </Submenu>
- 66
- </Menu>
- 67
- </Sider>
- 68
- <Content :style="{padding: '24px', minHeight: '280px', background: '#fff'}">
- 69
- <router-view></router-view>
- 70
- </Content>
- 71
- </Layout>
- 72
- </Content>
- 73
- </Layout>
- 74
- <Footer class="layout-footer-center">2011-2016 © TalkingData</Footer>
- 75
- </Layout>
- 76
- </div>
- 77
- </template>
- 78
- <script>
- 79
- export default {}
- 80
- </script>
3 常见组件的使用:
3.1 Table - 数据表格
表格组件通过columns属性绑定列,通过data属性绑定行数据。以分类管理组件(Categories.vue)为例:
- <Table border :columns="columns" :data="categories">
- 2
- <template slot="operation" slot-scope="{row}">
- 3
- <Button type="primary" @click="showEdit(row)">修改</Button>
- 4
- <Button type="error" @click="deleteCategory(row)">删除</Button>
- 5
- </template>
- 6
- </Table>
背后绑定的数据:
- export default {
- 2
- name:'categories',
- 3
- data(){
- 4
- return {
- 5
- columns:[
- 6
- {title:'分类ID', key:'id'},
- 7
- {title:'分类名称', key:'name'},
- 8
- {title:'操作', slot:'operation', align: 'center'}
- 9
- ],
- 10
- categories:[]
- 11
- }
- 12
- },
其中 coloumns中每一个对象代表一个列,title是列标题,key是该列绑定的对象属性名。
如果列中由其它组件组成,则可以定义为插槽(slot),让后再通过Table组件中的模板(template)去定制slot中的结构。
template中的slot属性需要和columns中对用列的slot属性向对应,template中的slot-scope则用于定义Table向slot中传入的上下文数据。
3.2 Form - 表单组件
表单组件可以绑定数据和数据校验。以登录组件(Login.vue)为例:
- <template>
- 2
- <div>
- 3
- <h1 class="title">趣物网-登录</h1>
- 4
- <Row>
- 5
- <Col span="8" offset="8">
- 6
- <Card>
- 7
- <p slot="title">
- 8
- <Icon type="ios-film-outline"></Icon>用户登录
- 9
- </p>
- 10
- <Form ref="loginForm" :model="user" :rules="ruleValidate" :label-width="80">
- 11
- <FormItem label="用户名" prop="username">
- 12
- <Input v-model="user.username" placeholder="用户名..." />
- 13
- </FormItem>
- 14
- <FormItem label="密码" prop="password">
- 15
- <Input type="password" v-model="user.password" placeholder="密码..." />
- 16
- </FormItem>
- 17
- <FormItem>
- 18
- <Button @click="login" type="primary">登录</Button>
- 19
- </FormItem>
- 20
- </Form>
- 21
- </Card>
- 22
- </Col>
- 23
- </Row>
- 24
- </div>
- 25
- </template>
(1)Form的 :model="user" 用于设置绑定对象,:rules="ruleValidate" 用于设置绑定验证;
(2)其中FormItem的 prop="username" 用于指定当前项需要验证的属性名,即ruleValidate中的属性名;
(3)为了方便调用验证,我们使用 ref="loginForm" 为表单对象设置了引用名,于是下面的代码可以通过 “this.$refs['loginForm'].validate( (valid)=>{...} )” 来显式调用表单验证。
- <script>
- 2
- import UserInfo from '@/js/UserInfo.js'
- 3
- const userInfo = new UserInfo();
- 4
- 5
- export default {
- 6
- name:'login',
- 7
- data(){
- 8
- return{
- 9
- user:{
- 10
- username:'',
- 11
- password:''
- 12
- },
- 13
- ruleValidate:{
- 14
- username:[{required:true, message:'请填写用户名', trigger:'blur'}],
- 15
- password:[{required:true, message:'请填写密码', trigger:'blur'}],
- 16
- }
- 17
- }
- 18
- },
- 19
- methods:{
- 20
- login(){
- 21
- this.$refs['loginForm'].validate((valid)=>{
- 22
- if(valid){
- 23
- this.axios.post('/api/auth/login', this.user).then(res=>{
- 24
- if(res.data.roleName!='管理员'){
- 25
- alert('您不是管理员,无法进入后台');
- 26
- }else{
- 27
- userInfo.saveLoginUser(res.data);
- 28
- let redirect ='/'
- 29
- if(this.$route.query.redirect){
- 30
- redirect = this.$route.query.redirect;
- 31
- }
- 32
- this.$router.push({path: redirect});
- 33
- }
- 34
-
- 35
- }).catch(()=>alert('用户名或密码有误'));
- 36
- }
- 37
- });
- 38
- }
- 39
- }
- 40
- }
- 41
- </script>
3.3 Modal - 模态框
模态框可以通过简单的布尔属性绑定实现显示和隐藏。继续实现分类管理(Categories.vue)中的分类信息编辑功能:
- <Modal
- 2
- v-model="showModal"
- 3
- title="商品分类编辑">
- 4
- <Form ref="categoryForm" :model="editCategory" :rules="editValidate" :label-width="100">
- 5
- <input type="hidden" v-model="editCategory.id" />
- 6
- <FormItem label="分类名称" prop="name">
- 7
- <Input v-model="editCategory.name" placeholder="分类名称..." />
- 8
- </FormItem>
- 9
- </Form>
- 10
- <div slot="footer">
- 11
- <Button @click="showModal=false">取消</Button>
- 12
- <Button type="primary" @click="saveCategory">保存</Button>
- 13
- </div>
- 14
- </Modal>
以下是分类编辑的代码,值得注意的是:
vue是支持双向绑定的,如果编辑对象是既显示在Table中,又可以被Form元素修改,则会产生联动问题,即使最终放弃了Form中的变更,也会导致Table中的数据发生变化,因此需要克隆一份数据副本进行修改。
- export default {
- 2
- name:'categories',
- 3
- data(){
- 4
- return {
- 5
- ......
- 6
- showModal: false,
- 7
- editCategory:{},
- 8
- editValidate:{
- 9
- name:[{required:true, message:'请填写分类名称', trigger:'blur'}],
- 10
- }
- 11
- }
- 12
- },
- 13
- methods:{
- 14
- ......
- 15
- saveCategory(){ //保存编辑信息
- 16
- this.$refs['categoryForm'].validate( valid=>{
- 17
- if(valid){
- 18
- this.axios.post('/api/admin/categories', this.editCategory).then(()=>{
- 19
- this.editCategory = {};
- 20
- this.showModal = false;
- 21
- this.loadCategories();
- 22
- });
- 23
- }
- 24
- });
- 25
- },
- 26
- showEdit(item){ //弹出编辑框并传入编辑数据
- 27
- if(item){
- 28
- this.editCategory = JSON.parse(JSON.stringify(item)); //克隆副本再做绑定
- 29
- }else{
- 30
- this.editCategory = {};
- 31
- }
- 32
- this.showModal = true;
- 33
- }
- 34
- }
- 35
- }
4 客户端权限:为路由设置拦截器
为了避免未登陆用户能访问后台页面,我们需要为后台路由设置守卫(拦截器)。利用router中的beforeEach事件钩子,我们可以添加守卫。
(1)在程序入口router/index.js中添加路由钩子
- import UserInfo from '@/js/UserInfo.js'
- 2
- const userInfo = new UserInfo()
- 3
- ......
- 4
- //设置路由拦截
- 5
- router.beforeEach((to, from, next) => {
- 6
- if (to.meta.anonymous) { // 判断该路由是否允许匿名访问
- 7
- next();
- 8
- }
- 9
- else {
- 10
- if (userInfo.isLogin()) { // 检查是否已登录,已登录继续
- 11
- next();
- 12
- }
- 13
- else {
- 14
- next({
- 15
- path: '/login',
- 16
- query: {redirect: to.fullPath} // 将跳转的路由path作为参数,保留被拦截路径URL
- 17
- })
- 18
- }
- 19
- }
- 20
- });
(2)在路由设置router.js中,允许匿名访问的路由项(比如 "/login"),添加meta自定义属性标识(比如"anonymous:true")
- const routes = [
- 2
- ......
- 3
- {
- 4
- path:'/login',
- 5
- name:'login',
- 6
- component:Login,
- 7
- meta:{
- 8
- anonymous:true //添加标识符,允许匿名
- 9
- }
- 10
- }
- 11
- ]
附:解决eslint语法报错“Parsing error: x-invalid-end-tag”问题。
问题原因:vue将标签渲染为原生html标签时,由于这些标签是自闭合的,所以有end标签会报错。
解决办法:在“.eslintrc.js” 配置文件的rules配置节中添加“'vue/no-parsing-error': [2, { 'x-invalid-end-tag': false }]”
- module.exports = {
- 2
- ......
- 3
- rules: {
- 4
- ......
- 5
- 'vue/no-parsing-error': [2, { 'x-invalid-end-tag': false }]
- 6
- },
- 7
- }
- 8
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。