当前位置:   article > 正文

业务后台商业组件ViewUI(iView)入门_iviewui

iviewui

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)设置父子级别路由: 

  1. const routes = [
  2. 2
  3. {
  4. 3
  5. path: '/',
  6. 4
  7. name: 'home',
  8. 5
  9. component: Home,
  10. 6
  11. children:[
  12. 7
  13. {
  14. 8
  15. path:'/',
  16. 9
  17. name:'default',
  18. 10
  19. component:Default,
  20. 11
  21. meta:{
  22. 12
  23. title: '后台首页'
  24. 13
  25. }
  26. 14
  27. },
  28. 15
  29. {
  30. 16
  31. path:'/categories',
  32. 17
  33. name:'categories',
  34. 18
  35. component:Categories,
  36. 19
  37. meta:{
  38. 20
  39. title: '分类管理'
  40. 21
  41. }
  42. 22
  43. },
  44. 23
  45. {
  46. 24
  47. path:'/products',
  48. 25
  49. name:'products',
  50. 26
  51. component:Products,
  52. 27
  53. meta:{
  54. 28
  55. title: '商品管理'
  56. 29
  57. }
  58. 30
  59. },
  60. 31
  61. ]
  62. 32
  63. },
  64. 33
  65. {
  66. 34
  67. path:'/login',
  68. 35
  69. name:'login',
  70. 36
  71. component:Login
  72. 37
  73. }
  74. 38
  75. ]

2.3 布局

业务系统通常由比较严谨的布局,View UI为我们准备好了多种布局风格,这里使用“顶部-侧边布局”作为示例:

​​​​​​https://www.iviewui.com/components/layout#DB-CBBJ

  1. <style scoped>
  2. 2
  3. ......
  4. 3
  5. .layout-logo{
  6. 4
  7. ......
  8. 5
  9. color:#fff;
  10. 6
  11. line-height: 30px;
  12. 7
  13. text-align: center;
  14. 8
  15. }
  16. 9
  17. ......
  18. 10
  19. </style>
  20. 11
  21. <template>
  22. 12
  23. <div class="layout">
  24. 13
  25. <Layout>
  26. 14
  27. <Header>
  28. 15
  29. <Menu mode="horizontal" theme="dark" active-name="1">
  30. 16
  31. <div class="layout-logo">
  32. 17
  33. <router-link to="/"><h3>趣物网 - 后台管理</h3></router-link>
  34. 18
  35. </div>
  36. 19
  37. <div class="layout-nav">
  38. 20
  39. <MenuItem name="1">
  40. 21
  41. <Icon type="ios-navigate"></Icon>
  42. 22
  43. Item 1
  44. 23
  45. </MenuItem>
  46. 24
  47. <MenuItem name="2">
  48. 25
  49. <Icon type="ios-keypad"></Icon>
  50. 26
  51. Item 2
  52. 27
  53. </MenuItem>
  54. 28
  55. <MenuItem name="3">
  56. 29
  57. <Icon type="ios-analytics"></Icon>
  58. 30
  59. Item 3
  60. 31
  61. </MenuItem>
  62. 32
  63. <MenuItem name="4">
  64. 33
  65. <Icon type="ios-paper"></Icon>
  66. 34
  67. Item 4
  68. 35
  69. </MenuItem>
  70. 36
  71. </div>
  72. 37
  73. </Menu>
  74. 38
  75. </Header>
  76. 39
  77. <Layout :style="{padding: '0 50px'}">
  78. 40
  79. <Breadcrumb :style="{margin: '16px 0'}">
  80. 41
  81. <BreadcrumbItem>后台管理</BreadcrumbItem>
  82. 42
  83. <BreadcrumbItem>{{$route.meta.title}}</BreadcrumbItem>
  84. 43
  85. </Breadcrumb>
  86. 44
  87. <Content :style="{padding: '24px 0', minHeight: '280px', background: '#fff'}">
  88. 45
  89. <Layout>
  90. 46
  91. <Sider hide-trigger :style="{background: '#fff'}">
  92. 47
  93. <Menu active-name="1-2" theme="light" width="auto" :open-names="['1']">
  94. 48
  95. <Submenu name="1">
  96. 49
  97. <template slot="title">
  98. 50
  99. <Icon type="ios-navigate"></Icon>
  100. 51
  101. 商品信息管理
  102. 52
  103. </template>
  104. 53
  105. <MenuItem name="1-1">
  106. 54
  107. <router-link to="/categories">分类管理</router-link>
  108. 55
  109. </MenuItem>
  110. 56
  111. <MenuItem name="1-2">
  112. 57
  113. <router-link to="/products">商品管理</router-link>
  114. 58
  115. </MenuItem>
  116. 59
  117. </Submenu>
  118. 60
  119. <Submenu name="2">
  120. 61
  121. <template slot="title">
  122. 62
  123. <Icon type="ios-analytics"></Icon>
  124. 63
  125. 客户订单管理
  126. 64
  127. </template>
  128. 65
  129. </Submenu>
  130. 66
  131. </Menu>
  132. 67
  133. </Sider>
  134. 68
  135. <Content :style="{padding: '24px', minHeight: '280px', background: '#fff'}">
  136. 69
  137. <router-view></router-view>
  138. 70
  139. </Content>
  140. 71
  141. </Layout>
  142. 72
  143. </Content>
  144. 73
  145. </Layout>
  146. 74
  147. <Footer class="layout-footer-center">2011-2016 &copy; TalkingData</Footer>
  148. 75
  149. </Layout>
  150. 76
  151. </div>
  152. 77
  153. </template>
  154. 78
  155. <script>
  156. 79
  157. export default {}
  158. 80
  159. </script>

3 常见组件的使用:

3.1 Table - 数据表格

表格组件通过columns属性绑定列,通过data属性绑定行数据。以分类管理组件(Categories.vue)为例:

  1. <Table border :columns="columns" :data="categories">
  2. 2
  3. <template slot="operation" slot-scope="{row}">
  4. 3
  5. <Button type="primary" @click="showEdit(row)">修改</Button>&nbsp;
  6. 4
  7. <Button type="error" @click="deleteCategory(row)">删除</Button>
  8. 5
  9. </template>
  10. 6
  11. </Table>

背后绑定的数据:

  1. export default {
  2. 2
  3. name:'categories',
  4. 3
  5. data(){
  6. 4
  7. return {
  8. 5
  9. columns:[
  10. 6
  11. {title:'分类ID', key:'id'},
  12. 7
  13. {title:'分类名称', key:'name'},
  14. 8
  15. {title:'操作', slot:'operation', align: 'center'}
  16. 9
  17. ],
  18. 10
  19. categories:[]
  20. 11
  21. }
  22. 12
  23. },

其中 coloumns中每一个对象代表一个列,title是列标题,key是该列绑定的对象属性名。

如果列中由其它组件组成,则可以定义为插槽(slot),让后再通过Table组件中的模板(template)去定制slot中的结构。

template中的slot属性需要和columns中对用列的slot属性向对应,template中的slot-scope则用于定义Table向slot中传入的上下文数据。

3.2  Form - 表单组件

表单组件可以绑定数据和数据校验。以登录组件(Login.vue)为例:

  1. <template>
  2. 2
  3. <div>
  4. 3
  5. <h1 class="title">趣物网-登录</h1>
  6. 4
  7. <Row>
  8. 5
  9. <Col span="8" offset="8">
  10. 6
  11. <Card>
  12. 7
  13. <p slot="title">
  14. 8
  15. <Icon type="ios-film-outline"></Icon>用户登录
  16. 9
  17. </p>
  18. 10
  19. <Form ref="loginForm" :model="user" :rules="ruleValidate" :label-width="80">
  20. 11
  21. <FormItem label="用户名" prop="username">
  22. 12
  23. <Input v-model="user.username" placeholder="用户名..." />
  24. 13
  25. </FormItem>
  26. 14
  27. <FormItem label="密码" prop="password">
  28. 15
  29. <Input type="password" v-model="user.password" placeholder="密码..." />
  30. 16
  31. </FormItem>
  32. 17
  33. <FormItem>
  34. 18
  35. <Button @click="login" type="primary">登录</Button>
  36. 19
  37. </FormItem>
  38. 20
  39. </Form>
  40. 21
  41. </Card>
  42. 22
  43. </Col>
  44. 23
  45. </Row>
  46. 24
  47. </div>
  48. 25
  49. </template>

(1)Form的 :model="user" 用于设置绑定对象,:rules="ruleValidate" 用于设置绑定验证;

(2)其中FormItem的 prop="username" 用于指定当前项需要验证的属性名,即ruleValidate中的属性名;

(3)为了方便调用验证,我们使用 ref="loginForm" 为表单对象设置了引用名,于是下面的代码可以通过 “this.$refs['loginForm'].validate( (valid)=>{...} )” 来显式调用表单验证。

  1. <script>
  2. 2
  3. import UserInfo from '@/js/UserInfo.js'
  4. 3
  5. const userInfo = new UserInfo();
  6. 4
  7. 5
  8. export default {
  9. 6
  10. name:'login',
  11. 7
  12. data(){
  13. 8
  14. return{
  15. 9
  16. user:{
  17. 10
  18. username:'',
  19. 11
  20. password:''
  21. 12
  22. },
  23. 13
  24. ruleValidate:{
  25. 14
  26. username:[{required:true, message:'请填写用户名', trigger:'blur'}],
  27. 15
  28. password:[{required:true, message:'请填写密码', trigger:'blur'}],
  29. 16
  30. }
  31. 17
  32. }
  33. 18
  34. },
  35. 19
  36. methods:{
  37. 20
  38. login(){
  39. 21
  40. this.$refs['loginForm'].validate((valid)=>{
  41. 22
  42. if(valid){
  43. 23
  44. this.axios.post('/api/auth/login', this.user).then(res=>{
  45. 24
  46. if(res.data.roleName!='管理员'){
  47. 25
  48. alert('您不是管理员,无法进入后台');
  49. 26
  50. }else{
  51. 27
  52. userInfo.saveLoginUser(res.data);
  53. 28
  54. let redirect ='/'
  55. 29
  56. if(this.$route.query.redirect){
  57. 30
  58. redirect = this.$route.query.redirect;
  59. 31
  60. }
  61. 32
  62. this.$router.push({path: redirect});
  63. 33
  64. }
  65. 34
  66. 35
  67. }).catch(()=>alert('用户名或密码有误'));
  68. 36
  69. }
  70. 37
  71. });
  72. 38
  73. }
  74. 39
  75. }
  76. 40
  77. }
  78. 41
  79. </script>

3.3 Modal - 模态框

模态框可以通过简单的布尔属性绑定实现显示和隐藏。继续实现分类管理(Categories.vue)中的分类信息编辑功能:

  1. <Modal
  2. 2
  3. v-model="showModal"
  4. 3
  5. title="商品分类编辑">
  6. 4
  7. <Form ref="categoryForm" :model="editCategory" :rules="editValidate" :label-width="100">
  8. 5
  9. <input type="hidden" v-model="editCategory.id" />
  10. 6
  11. <FormItem label="分类名称" prop="name">
  12. 7
  13. <Input v-model="editCategory.name" placeholder="分类名称..." />
  14. 8
  15. </FormItem>
  16. 9
  17. </Form>
  18. 10
  19. <div slot="footer">
  20. 11
  21. <Button @click="showModal=false">取消</Button>
  22. 12
  23. <Button type="primary" @click="saveCategory">保存</Button>
  24. 13
  25. </div>
  26. 14
  27. </Modal>

以下是分类编辑的代码,值得注意的是:

vue是支持双向绑定的,如果编辑对象是既显示在Table中,又可以被Form元素修改,则会产生联动问题,即使最终放弃了Form中的变更,也会导致Table中的数据发生变化,因此需要克隆一份数据副本进行修改。

  1. export default {
  2. 2
  3. name:'categories',
  4. 3
  5. data(){
  6. 4
  7. return {
  8. 5
  9. ......
  10. 6
  11. showModal: false,
  12. 7
  13. editCategory:{},
  14. 8
  15. editValidate:{
  16. 9
  17. name:[{required:true, message:'请填写分类名称', trigger:'blur'}],
  18. 10
  19. }
  20. 11
  21. }
  22. 12
  23. },
  24. 13
  25. methods:{
  26. 14
  27. ......
  28. 15
  29. saveCategory(){ //保存编辑信息
  30. 16
  31. this.$refs['categoryForm'].validate( valid=>{
  32. 17
  33. if(valid){
  34. 18
  35. this.axios.post('/api/admin/categories', this.editCategory).then(()=>{
  36. 19
  37. this.editCategory = {};
  38. 20
  39. this.showModal = false;
  40. 21
  41. this.loadCategories();
  42. 22
  43. });
  44. 23
  45. }
  46. 24
  47. });
  48. 25
  49. },
  50. 26
  51. showEdit(item){ //弹出编辑框并传入编辑数据
  52. 27
  53. if(item){
  54. 28
  55. this.editCategory = JSON.parse(JSON.stringify(item)); //克隆副本再做绑定
  56. 29
  57. }else{
  58. 30
  59. this.editCategory = {};
  60. 31
  61. }
  62. 32
  63. this.showModal = true;
  64. 33
  65. }
  66. 34
  67. }
  68. 35
  69. }

4 客户端权限:为路由设置拦截器

为了避免未登陆用户能访问后台页面,我们需要为后台路由设置守卫(拦截器)。利用router中的beforeEach事件钩子,我们可以添加守卫。

(1)在程序入口router/index.js中添加路由钩子

  1. import UserInfo from '@/js/UserInfo.js'
  2. 2
  3. const userInfo = new UserInfo()
  4. 3
  5. ......
  6. 4
  7. //设置路由拦截
  8. 5
  9. router.beforeEach((to, from, next) => {
  10. 6
  11. if (to.meta.anonymous) { // 判断该路由是否允许匿名访问
  12. 7
  13. next();
  14. 8
  15. }
  16. 9
  17. else {
  18. 10
  19. if (userInfo.isLogin()) { // 检查是否已登录,已登录继续
  20. 11
  21. next();
  22. 12
  23. }
  24. 13
  25. else {
  26. 14
  27. next({
  28. 15
  29. path: '/login',
  30. 16
  31. query: {redirect: to.fullPath} // 将跳转的路由path作为参数,保留被拦截路径URL
  32. 17
  33. })
  34. 18
  35. }
  36. 19
  37. }
  38. 20
  39. });

(2)在路由设置router.js中,允许匿名访问的路由项(比如 "/login"),添加meta自定义属性标识(比如"anonymous:true")

  1. const routes = [
  2. 2
  3. ......
  4. 3
  5. {
  6. 4
  7. path:'/login',
  8. 5
  9. name:'login',
  10. 6
  11. component:Login,
  12. 7
  13. meta:{
  14. 8
  15. anonymous:true //添加标识符,允许匿名
  16. 9
  17. }
  18. 10
  19. }
  20. 11
  21. ]

附:解决eslint语法报错“Parsing error: x-invalid-end-tag”问题。

问题原因:vue将标签渲染为原生html标签时,由于这些标签是自闭合的,所以有end标签会报错。

解决办法:在“.eslintrc.js” 配置文件的rules配置节中添加“'vue/no-parsing-error': [2, { 'x-invalid-end-tag': false }]”

  1. module.exports = {
  2. 2
  3. ......
  4. 3
  5. rules: {
  6. 4
  7. ......
  8. 5
  9. 'vue/no-parsing-error': [2, { 'x-invalid-end-tag': false }]
  10. 6
  11. },
  12. 7
  13. }
  14. 8

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/113381?site
推荐阅读
相关标签
  

闽ICP备14008679号