zoukankan      html  css  js  c++  java
  • vue 使用element-ui基础布局

    首先创建项目,在此之前,我们需要全局安装npm

    接着执行如下指令:

    npm install -g vue-cli
    vue init webpack vueDemo
    cd vueDemo
    npm install
    npm run dev

    访问http://localhost:8080就可以看到我们新创建的vue项目了

    然后安装element-ui

    npm i element-ui -S

    接着在main.js文件中集成element-ui

    // The Vue build version to load with the `import` command
    // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
    import Vue from 'vue'
    import ElementUI from 'element-ui'
    import 'element-ui/lib/theme-chalk/index.css'
    import App from './App'
    
    Vue.use(ElementUI)
    Vue.config.productionTip = false
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      components: { App },
      template: '<App/>'
    })

    接下来我们在App.vue中增加一个顶级的router-view

    <template>
      <div id="app">
        <router-view></router-view>
      </div>
    </template>
    
    <script>
    import router from '@/js/router'
    export default {
      name: 'App',
      router,
      components: {
      },
      created () {
      },
      data () {
        return {
        }
      },
      methods: {
    
      }
    }
    
    </script>
    
    <style>
    
    </style>

    写router.js

    import Vue from 'vue'
    import VueRouter from 'vue-router'
    import Layout from '@/layout/Layout'
    
    Vue.use(VueRouter)
    
    const routes = [
      {
        path: '/',
        component: Layout,
        redirect: '/student',
        children: [
          {
            path: 'student',
            name: 'student',
            component: () => import('@/components/student/student')
          }
        ]
      }
    ]
    
    // 路由配置
    const RouterConfig = {
      mode: 'history', // require service support
      scrollBehavior: () => ({
        y: 0
      }),
      routes
    }
    // export const router = new Router(RouterConfig)
    const createRouter = () => new VueRouter(RouterConfig)
    
    // 创建路由实例
    const router = createRouter()
    // 添加动态路由
    // addAsyncRouter()
    
    export default router

    我们可以看到定义的路由组件有子组件,这样我们就可以直接在App.vue中定义一个router-view,然后其它什么也不用写,将路由全部路由到Layout组件中,这样就可以所有页面统一样式了,每一个菜单的特定路由定义在children路由中

    当然也可以在App.vue中引入Layout组件,如果这样,所有的菜单路由就不需要统一路由到Layout组件了。

    然后我们写一个布局文件Layout.vue

    <template>
      <div>
        <el-row>
          <el-col :span="3">
            <side-bar></side-bar></el-col>
          <el-col :span="21">
            <router-view></router-view></el-col>
        </el-row>
      </div>
    </template>
    
    <script>
    import sideBar from '../components/SideBar/sideBar'
    export default {
      name: '',
      components: {
        sideBar
      }
    }
    </script>
    
    <style scoped>
    
    </style>

    其中sideBar是我们自定义的左侧导航

    by the way 封装以下http请求

    http.js

    import axios from 'axios' // 引入axios
    
    // 响应拦截器
    // 响应拦截器
    axios.interceptors.response.use(
      response => {
        // 如果返回的状态码为200,说明接口请求成功,可以正常拿到数据
        // 否则的话抛出错误
        if (response.status === 200) {
          return Promise.resolve(response)
        } else {
          return Promise.reject(response)
        }
      },
      // 服务器状态码不是2开头的的情况
      // 这里可以跟你们的后台开发人员协商好统一的错误状态码
      // 然后根据返回的状态码进行一些操作,例如登录过期提示,错误提示等等
      // 下面列举几个常见的操作,其他需求可自行扩展
      error => {
        if (error.response.status) {
          return Promise.reject(error.response)
        }
      })
    
    /**
     * get方法,对应get请求
     * @param {String} url [请求的url地址]
     * @param {Object} params [请求时携带的参数]
     */
    export function get (url, params) {
      return new Promise((resolve, reject) => {
        axios.get(url, {
          params: params
        }).then(res => {
          resolve(res.data)
        }).catch(err => {
          reject(err.data)
        })
      })
    }
    
    /**
     * post方法,对应post请求
     * @param {String} url [请求的url地址]
     * @param {Object} params [请求时携带的参数]
     */
    export function post (url, params) {
      return new Promise((resolve, reject) => {
        axios.post(url, params)
          .then(res => {
            resolve(res.data)
          })
          .catch(err => {
            reject(err.data)
          })
      })
    }

    api.js

    /**
     * api接口统一管理
     */
    import { get, post } from './http'
    const preUrl = 'http://localhost:8081/'
    export const apiAddress = p => get(preUrl + '/stuUser/list', p)
    
    export const apiAddressa = p => post(preUrl + '/stuUser/list', p)

    然后写一个router.js中定义的student组件

    <template>
      <div>
      <el-table
    
        v-loading="listLoading"
        :data="tableData"
        style=" 100%">
        <el-table-column
          label="照片"
          width="180">
          <template slot-scope="scope">
            <i class="el-icon-time"></i>
            <span style="margin-left: 10px">
              <el-image
              style=" 100px; height: 100px"
              :src="scope.row.picture"
              fit="fit"></el-image></span>
          </template>
        </el-table-column>
        <el-table-column
          label="学生姓名"
          width="180">
          <template slot-scope="scope">
            <el-popover trigger="hover" placement="top">
              <p>学生姓名: {{ scope.row.studentName }}</p>
              <p>学生学号: {{ scope.row.studentNumber }}</p>
              <div slot="reference" class="name-wrapper">
                <el-tag size="medium">{{ scope.row.studentName }}</el-tag>
              </div>
            </el-popover>
          </template>
        </el-table-column>
        <el-table-column
          label="手机号"
          width="180">
          <template slot-scope="scope">
            <span style="margin-left: 10px">{{ scope.row.phoneNumber }}</span>
          </template>
        </el-table-column>
        <el-table-column
          label="家庭地址"
          width="180">
          <template slot-scope="scope">
            <span style="margin-left: 10px">{{ scope.row.address }}</span>
          </template>
        </el-table-column>
        <el-table-column label="操作">
          <template slot-scope="scope">
            <el-button
              size="mini"
              @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
            <el-button
              size="mini"
              type="danger"
              @click="handleDelete(scope.$index, scope.row)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
        <pagination v-show="total>0" :total="total" :page.sync="pageNum" :rows.sync="pageSize" @pagination="getList" />
        <el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
          <el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="80px" style=" 400px; margin-left:50px;">
           <el-form-item label="照片" prop="studentName">
             <uploader @setImage="setImage" :url.sync="temp.picture"/>
    
             <el-input v-model="temp.picture" />
            </el-form-item>
           <el-form-item label="学生姓名" prop="studentName">
             <el-input v-model="temp.studentName" />
            </el-form-item>
            <el-form-item label="学生学号" prop="studentNumber">
              <el-input v-model="temp.studentNumber" />
            </el-form-item>
            <el-form-item label="手机号" prop="phoneNumber">
              <el-input v-model="temp.phoneNumber" />
            </el-form-item>
            <el-form-item label="家庭地址" prop="address">
              <el-input v-model="temp.address" />
            </el-form-item>
          </el-form>
          <div slot="footer" class="dialog-footer">
            <el-button @click="dialogFormVisible = false">
              Cancel
            </el-button>
            <el-button type="primary" @click="dialogStatus==='create'?createData():updateData()">
              Confirm
            </el-button>
          </div>
        </el-dialog>
        </div>
    </template>
    
    <script>
    import { apiAddress } from '@/js/api'
    import Pagination from '@/components/Pagination' // secondary package based on el-pagination
    import uploader from '../uploader/uploader'
    export default {
      components: {Pagination, uploader},
      data () {
        return {
          picture: '',
          tableData: [],
          listLoading: true,
          total: 0,
          pageNum: 1,
          pageSize: 20,
          temp: {studentNumber: '',
            studentName: '',
            phoneNumber: '',
            address: '',
            id: ''},
          dialogFormVisible: false,
          dialogStatus: '',
          textMap: {
            update: '编辑',
            create: '新增'
          },
          rules: {
            address: [{ required: true, message: 'address is required', trigger: 'change' }],
            phoneNumber: [{ required: true, message: 'phoneNumber is required', trigger: 'change' }],
            studentName: [{ required: true, message: 'studentName is required', trigger: 'blur' }],
            picture: [{ required: true, message: 'picture is required', trigger: 'blur' }],
            studentNumber: [{ required: true, message: 'title is required', trigger: 'blur' }]
          }
        }
      },
      mounted () {
        this.getList()
      },
      methods: {
        setImage (val) {
          console.log(val)
        },
        getList () {
          this.listLoading = true
          apiAddress({
            page: this.pageNum,
            rows: this.pageSize
          }).then(res => {
            if (res.list.length > 0) {
              res.list.forEach(item => {
                item.picture = 'http://localhost:8081/' + item.picture
              })
              this.tableData = res.list
              this.total = res.total
              this.pageNum = res.pageNum
              this.pageSize = res.pageSize
            }
            this.listLoading = false
          })
        },
        handleEdit (index, row) {
          this.temp = Object.assign({}, row) // copy obj
          this.dialogStatus = 'update'
          this.dialogFormVisible = true
          /* this.$nextTick(() => {
            this.$refs['dataForm'].clearValidate()
          }) */
        },
        handleDelete (index, row) {
          this.$notify({
            title: 'Success',
            message: 'Delete Successfully',
            type: 'success',
            duration: 2000
          })
          this.list.splice(index, 1)
        },
        updateData () {
          this.$refs['dataForm'].validate((valid) => {
            if (valid) {
              const tempData = Object.assign({}, this.temp)
              tempData.timestamp = +new Date(tempData.timestamp) // change Thu Nov 30 2017 16:41:05 GMT+0800 (CST) to 1512031311464
            }
          })
        }
      }
    }
    </script>

    其中涉及Pagenation分页组件和uploader图片上传组件

    Pagenation index.vue

    <template>
      <div :class="{'hidden':hidden}" class="pagination-container">
        <el-pagination
          :background="background"
          :current-page.sync="currentPage"
          :page-size.sync="pageSize"
          :layout="layout"
          :page-sizes="pageSizes"
          :total="total"
          v-bind="$attrs"
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
        />
      </div>
    </template>
    
    <script>
    import { scrollTo } from './scroll-to'
    
    export default {
      name: 'Pagination',
      props: {
        total: {
          required: true,
          type: Number
        },
        page: {
          type: Number,
          default: 1
        },
        rows: {
          type: Number,
          default: 20
        },
        pageSizes: {
          type: Array,
          default () {
            return [10, 20, 30, 50]
          }
        },
        layout: {
          type: String,
          default: 'total, sizes, prev, pager, next, jumper'
        },
        background: {
          type: Boolean,
          default: true
        },
        autoScroll: {
          type: Boolean,
          default: true
        },
        hidden: {
          type: Boolean,
          default: false
        }
      },
      computed: {
        currentPage: {
          get () {
            return this.page
          },
          set (val) {
            this.$emit('update:page', val)
          }
        },
        pageSize: {
          get () {
            return this.rows
          },
          set (val) {
            this.$emit('update:rows', val)
          }
        }
      },
      methods: {
        handleSizeChange (val) {
          this.$emit('pagination', { page: this.currentPage, rows: val })
          if (this.autoScroll) {
            scrollTo(0, 800)
          }
        },
        handleCurrentChange (val) {
          this.$emit('pagination', { page: val, rows: this.pageSize })
          if (this.autoScroll) {
            scrollTo(0, 800)
          }
        }
      }
    }
    </script>
    
    <style scoped>
    .pagination-container {
      background: #fff;
      padding: 32px 16px;
    }
    .pagination-container.hidden {
      display: none;
    }
    </style>

    scroll-to.js

    Math.easeInOutQuad = function (t, b, c, d) {
      t /= d / 2
      if (t < 1) {
        return c / 2 * t * t + b
      }
      t--
      return -c / 2 * (t * (t - 2) - 1) + b
    }
    
    // requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
    var requestAnimFrame = (function () {
      return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) { window.setTimeout(callback, 1000 / 60) }
    })()
    
    /**
     * Because it's so fucking difficult to detect the scrolling element, just move them all
     * @param {number} amount
     */
    function move (amount) {
      document.documentElement.scrollTop = amount
      document.body.parentNode.scrollTop = amount
      document.body.scrollTop = amount
    }
    
    function position () {
      return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop
    }
    
    /**
     * @param {number} to
     * @param {number} duration
     * @param {Function} callback
     */
    export function scrollTo (to, duration, callback) {
      const start = position()
      const change = to - start
      const increment = 20
      let currentTime = 0
      duration = (typeof (duration) === 'undefined') ? 500 : duration
      var animateScroll = function () {
        // increment the time
        currentTime += increment
        // find the value with the quadratic in-out easing function
        var val = Math.easeInOutQuad(currentTime, start, change, duration)
        // move the document.body
        move(val)
        // do the animation unless its over
        if (currentTime < duration) {
          requestAnimFrame(animateScroll)
        } else {
          if (callback && typeof (callback) === 'function') {
            // the animation is done so lets callback
            callback()
          }
        }
      }
      animateScroll()
    }

    uploader.vue

    <template>
      <el-upload
        class="avatar-uploader"
        action="http://localhost:8081/uploadFileToFast"
        :show-file-list="false"
        :on-success="handleAvatarSuccess"
        :before-upload="beforeAvatarUpload">
        <img v-if="url" :src="url" class="avatar">
        <i v-else class="el-icon-plus avatar-uploader-icon"></i>
      </el-upload>
    </template>
    
    <style>
      .avatar-uploader .el-upload {
        border: 1px dashed #d9d9d9;
        border-radius: 6px;
        cursor: pointer;
        position: relative;
        overflow: hidden;
      }
      .avatar-uploader .el-upload:hover {
        border-color: #409EFF;
      }
      .avatar-uploader-icon {
        font-size: 28px;
        color: #8c939d;
         178px;
        height: 178px;
        line-height: 178px;
        text-align: center;
      }
      .avatar {
         178px;
        height: 178px;
        display: block;
      }
    </style>
    
    <script>
    export default {
      props: {
        url: {
          required: true,
          type: String
        }
      },
      /* data () {
        return {
          imageUrl: ''
        }
      }, */
      /*  computed: {
        imageUrl: {
          get () {
            return this.url
          },
          set (val) {
            this.$emit('update:url', val)
          }
        }
      }, */
      methods: {
        handleAvatarSuccess (res, file) {
          this.$emit('update:url', 'http://localhost:8081/' + res)
          this.$emit('setImage', { url: res })
        },
        beforeAvatarUpload (file) {
          const isJPG = file.type === 'image/jpeg'
          const isLt2M = file.size / 1024 / 1024 < 2
    
          if (!isJPG) {
            this.$message.error('上传头像图片只能是 JPG 格式!')
          }
          if (!isLt2M) {
            this.$message.error('上传头像图片大小不能超过 2MB!')
          }
          return isJPG && isLt2M
        }
      }
    }
    </script>

    组件的prop不可以直接修改,如果要响应式双向绑定,正如代码中的样子,子组件定义prop,父组件使用子组件时,绑定自身相对应的data,如下代码:

    <uploader @setImage="setImage" :url.sync="temp.picture"/>

    注意到需要加上.sync,然后子组件中不可以直接修改,需要触发$emit方法

     this.$emit('update:url', 'http://localhost:8081/' + res) //修改url 直接this.url=xxx会报错 不允许在子组件中直接修改prop 执行此步骤父组件相对应的temp.picture就会响应式被修改
          this.$emit('setImage', { url: res }) //调用父组件方法,传参为url(此处传参是以数组形式,父组件接收参数使用一个参数即可,如果是执行了上一步代码,实现了双向绑定,这一步就可以省略啦)
    //还有一种方式是定义计算属性
     computed: {
    imageUrl: {
    get () {
    return this.url
    },
    set (val) {
    this.$emit('update:url', val)
    }
    }
    }
    给imageUrl赋值的时候,自动就更新了子组件的prop url,父组件与之对应的temp.picture
  • 相关阅读:
    bodybuilding
    DBLINK的session无法关闭,报异常!
    失控
    eclipse
    Linux下查看用户列表
    org.apache.commons.httpclient
    java map 遍历
    java String split
    胸上肌到底要怎么练啊!
    POI操作Excel常用方法总结
  • 原文地址:https://www.cnblogs.com/fengwenzhee/p/15407606.html
Copyright © 2011-2022 走看看