前面我们已经实现了前后两端项目的基础搭建以及前后数据交互,目前的UI页面十分的简陋,所以今天咱们来完善前端项目,让UI更加美观。
如果你的html、js、css的基础很好,那么前端项目研发起来相信不会很困难,不管是传统技术JSTL、JQuery与easyUI、bootstrap等UI框架,还是vue、react、angular与elmentui、layui等新技术,前端底层实现都是大同小异的,无非每项技术的特点各有不同。后期可根据团队人员和项目的基本情况来进行配套组合的技术选型。我们继续以Vue.js和elmentui为例研发本项目。
基础组件整合
为了后续不断完善前端项目,需要一些VUE组件来帮助我们更好的实现前端功能。
页面模块化
require和import的区别
vue编程中最重要的思想就是模块化,import和require都是被模块化所使用。
遵循规范
require 是 AMD规范引入方式。
import是ES6的一个语法标准,如果要兼容浏览器的话必须转化成es5的语法。
调用时间
require是运行时调用,所以require理论上可以运用在代码的任何地方。
import是编译时调用,所以必须放在文件开头。
原来得样例中页面引入咱们使用 import login from '@/views/login'的方式。
后续如果页面过多,开发环境如果使用懒加载, 页面太多的话会造成webpack热更新太慢。所以生产环境使用懒加载,开发环境不使用懒加载。
添加模块化工具类
router目录下添加工具类根据不同环境采用不同的模块化方式
import-development.js
module.exports = file => require('@/views/' + file + '.vue').default
import-production.js
module.exports = file => () => import('@/views/' + file + '.vue')
嵌套路由
嵌套路由又称为子路由,在实际应用中,通常由多层嵌套的组件组合而成。同样地,URL中各段动态路景观也按某种结构对应嵌套的各层组件,例如我们看Vue的官网教程。
当我们点击左边的项目的时候,我们知道,他左边和上面两个导航栏是不会改变的。改变的只是右边那一块区域。将鼠标放在某个标签上面,我们可以看到,他就是一个路由。或者我们应该说,他就是一个子路由。
index.js 添加路由
const _import = require('./import-' + process.env.NODE_ENV)
//全局路由
const routes = [
{ path: '/login', name:'login',component: _import('login') },
]
// 嵌套路由入口
const mainRoutes = {
path: '/',
component: _import('layout/main'),
name: 'main',
redirect: { name: 'home' },
meta: { title: '主入口整体布局' },
children: [
{ path: '/home', component: _import('demo/list'), name: 'home', meta: { title: '首页' } },
{ path: '/test1', component: _import('demo/test1'), name: 'test1', meta: { title: '选项一', isTab: true } },
{ path: '/test2', component: _import('demo/test2'), name: 'test2', meta: { title: '选项二', isTab: true } },
{ path: '/test', component: _import('demo/add'), name: 'test', meta: { title: '测试功能', isTab: true } },
{ path: '/theme', component: _import('common/theme'), name: 'theme', meta: { title: '主题', isTab: true } },
],
}
export default new Router({
routes: routes.concat(mainRoutes)
})
状态管理
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
vuex 包含有五个基本的对象:
state:存储状态。也就是变量;
getters:派生状态。也就是set、get中的get,有两个可选参数:state、getters分别可以获取state中的变量和其他的getters。外部调用方式:store.getters.personInfo()。就和vue的computed差不多;
mutations:提交状态修改。也就是set、get中的set,这是vuex中唯一修改state的方式,但不支持异步操作。第一个参数默认是state。外部调用方式:store.commit(‘SET_AGE’, 18)。和vue中的methods类似。
actions:和mutations类似。不过actions支持异步操作。第一个参数默认是和store具有相同参数属性的对象。外部调用方式:store.dispatch(‘nameAsyn’)。this.$store.dispatch(‘user/login’, this.loginForm)
modules:store的子模块,内容就相当于是store的一个实例。调用方式和前面介绍的相似,只是要加上当前子模块名,如:store.a.getters.xxx()。
有了Vuex可以实现组件全局状态(数据)的管理,方便的实现组件之间的数据共享。
Sass整合
Sass 是一个 CSS 的扩展,它在 CSS 语法的基础上,允许使用变量 (variables),嵌套规则 (nested rules), 混合 (mixins), 导入 (inline imports) 等功能,令 CSS 更加强大与优雅。使用 Sass 以及 Compass 样式库 有助于更好地组织管理样式文件,以及更高效地开发项目。
npm install node-sass --save-dev //安装node-sass
npm install sass-loader --save-dev //安装sass-loader
npm install style-loader --save-dev //安装style-loader
此处安装后可能会因sass-loader版本过高报错,可手动指定版本
npm install sass-loader@7.3.1 --save-dev //版本不能太高
也可在package.json中更改版本,然后npm install
基本UI设计
我们先研后台管理系统,管理系统一般有一下几类UI页面。
1、登录页面
这个不多说了用户密码登录页面。
2、主页面
登录验证成功后跳转到本页面,左侧菜单右侧详情的常规布局。
3、功能列表页
4、功能编辑详情
登录页面
登录页面前边我们已经实现了简单表单,样子比较丑陋,登录页比较简单,样式大家可以自行找网上提供的布局样式也可,基础较好的同学可以自己调整布局。这里举一个简单的登录样式的样例。
<template>
<div class="login">
<el-form ref="dataForm" :model="dataForm" :rules="loginRules" size="mini" class="login-form" status-icon>
<h2 class="title">欢迎登录后台管理系统</h2>
<el-form-item prop="username">
<el-input
v-model="dataForm.username"
type="text"
auto-complete="off"
placeholder="账号"
>
<i slot="prefix" class="el-input__icon el-icon-s-custom svg-external-icon"></i>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="dataForm.password"
type="password"
auto-complete="off"
placeholder="密码"
@keyup.enter.native="dataFormSubmit"
>
<i slot="prefix" class="el-input__icon el-icon-s-goods svg-external-icon"></i>
</el-input>
</el-form-item>
<!-- <el-form-item prop="captcha" v-if="captchaOnOff">
<el-input
v-model="dataForm.captcha"
auto-complete="off"
placeholder="验证码"
style="width: 63%"
@keyup.enter.native="dataFormSubmit"
>
<i slot="prefix" class="el-input__icon el-icon-menu svg-external-icon"></i>
</el-input>
<div class="login-code">
<img :src="captchaPath" @click="getCaptcha()" style="width: 100%" class="login-code-img"/>
</div>
</el-form-item>-->
<!-- <el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox>-->
<el-form-item style="width:100%;">
<el-button
:loading="loading"
size="medium"
type="primary"
style="width:100%;"
@click.native.prevent="dataFormSubmit"
>
<span v-if="!loading">登 录</span>
<span v-else>登 录 中...</span>
</el-button>
<div style="float: right;" v-if="register">
<router-link class="link-type" :to="'/register'">立即注册</router-link>
</div>
</el-form-item>
</el-form>
<!-- 底部 -->
<div class="el-login-footer">
<span>Copyright © 2021-2022 All Rights Reserved.</span>
</div>
</div>
</template>
<script>
export default {
name: "Login",
data() {
return {
codeUrl: "",
dataForm: {
username: '',
password: '',
uid: '',
captcha: '',
client_id:'webApp',
client_secret:'webApp',
grant_type:'password'
},
loginRules: {
username: [
{ required: true, trigger: "blur", message: "请输入您的账号" }
],
password: [
{ required: true, trigger: "blur", message: "请输入您的密码" }
],
captcha: [{ required: true, trigger: "change", message: "请输入验证码" }]
},
captchaPath: '',
loading: false,
// 验证码开关
captchaOnOff: true,
// 注册开关
register: false,
};
},
created() {
// this.getCaptcha()
var info=this.$route.params.data
if(info!=null)
this.$message({
showClose: true,
message: info,
type: 'error',
duration:1000
});
},
methods: {
// 提交表单
dataFormSubmit () {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
this.loading = true;
this.$router.replace({ name: 'home'})
}
})
},
// 获取验证码
getCaptcha () {
this.dataForm.uid = getUUID()
this.captchaPath = this.$http.adornUrl(`/api-auth/captcha/captcha.jpg?uid=${this.dataForm.uid}`)
},
}
};
</script>
<style rel="stylesheet/scss" lang="scss">
.login {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
/*background-image: url("../assets/images/login-background.jpg");*/
background: #3a8ee6;
background-size: cover;
}
.title {
margin: 0px auto 20px auto;
text-align: center;
color: #707070;
}
.login-form {
border-radius: 6px;
background: #ffffff;
width: 450px;
padding: 25px 25px 5px 25px;
.el-input {
height: 38px;
input {
height: 38px;
}
}
.input-icon {
height: 39px;
width: 14px;
margin-left: 2px;
}
}
.login-tip {
font-size: 13px;
text-align: center;
color: #bfbfbf;
}
.login-code {
width: 33%;
height: 36px;
float: right;
img {
cursor: pointer;
vertical-align: middle;
}
}
.el-login-footer {
height: 40px;
line-height: 40px;
position: fixed;
bottom: 0;
width: 100%;
text-align: center;
color: #fff;
font-family: Arial;
font-size: 12px;
letter-spacing: 1px;
}
.login-code-img {
height: 38px;
}
.svg-external-icon{
font-size: 12px;
//margin-right: 18px;
margin-left: 3px;
display: flex;
align-items: center;
}
body {
height: 100%;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
}
html {
height: 100%;
box-sizing: border-box;
}
#app {
height: 100%;
}
.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
font-family: inherit;
font-weight: 500;
line-height: 1.1;
color: inherit;
}
</style>
访问 :http://localhost:9081/#/login 显示如下
这里先不做后台校验,随便输入用户名、密码后跳转主页面。
主页面实现
主页面是整个后台管理系统的门面,它的重要程度不言而喻。
先实现常规的左侧是菜单、右侧是详情页面,头部有登录人的基础信息提示。更炫酷的要求后面逐步实现。
整体布局我们参考element官网布局容器来实现,我门以这个样例作为参考样例。
新增主页面main.vue
<template>
<div class="site-wrapper" :class="classObj">
<template >
<main-sidebar />
<main-navbar />
<div class="site-content__wrapper" :style="styleObj">
<main-content />
</div>
</template>
</div>
</template>
<script>
import MainNavbar from './main-navbar'
import MainSidebar from './main-sidebar'
import MainContent from './main-content'
export default {
data () {
return {
loading: true,
styleObj: {'min-height': this.documentClientHeight + 'px'}
}
},
components: {
MainNavbar,
MainSidebar,
MainContent
},
computed: {
documentClientHeight: {
get () { return this.$store.state.common.documentClientHeight },
set (val) { this.$store.commit('common/updateDocumentClientHeight', val) }
},
userId: {
get () { return this.$store.state.user.id },
set (val) { this.$store.commit('user/updateId', val) }
},
userName: {
get () {
return this.$store.state.user.name
},
set (val) {
this.$store.commit('user/updateName', val)
}
},
sidebarFold: {
get () { return this.$store.state.common.sidebarFold }
},
clientType() {
let type = this.$store.state.common.clientType;
if (type === 'phone') {
this.styleObj['margin-left'] = '0px';
this.styleObj['padding-top'] = '100px';
} else {
if (this.styleObj['margin-left']) this.$delete(this.styleObj, 'margin-left');
if (this.styleObj['padding-top']) this.$delete(this.styleObj, 'padding-top');
}
return type
},
showScreen() {
if (this.clientType == 'phone' && !this.sidebarFold) return true;
return false;
},
classObj() {
return {
'site-sidebar--hide': this.sidebarFold && this.clientType == 'phone',
'site-sidebar--fold': this.sidebarFold && this.clientType != 'phone',
'tab-phone-top': this.clientType == 'phone'
};
}
},
created () {
},
mounted () {
this.resetDocumentClientHeight()
},
methods: {
}
}
}
</script>
封装Aside、Header、Main-content三个组件,在Main.vue主页面中使用。
Aside组件存放菜单
参照element官网 导航菜单组件
main-sidebar.vue
<template>
<aside class="site-sidebar" :class="'site-sidebar--' + sidebarLayoutSkin">
<div class="site-sidebar__inner">
<el-menu
:collapse="sidebarFold"
default-active="home"
:collapseTransition="false"
class="site-sidebar__menu"
>
<el-menu-item index="home" @click="$router.push({ name: 'home' })" v-show="showSidebar">
<i class="el-icon-s-home"></i>
<span slot="title">首页</span>
</el-menu-item>
<el-submenu index="1">
<template slot="title">
<i class="el-icon-location"></i>
<span>导航一</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="1-1" @click="$router.push({ name: 'test1' })">选项1</el-menu-item>
<el-menu-item index="1-2" @click="$router.push({ name: 'test2' })">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="1-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="1-4">
<template slot="title">选项4</template>
<el-menu-item index="1-4-1">选项1</el-menu-item>
</el-submenu>
</el-submenu>
<el-menu-item index="2">
<i class="el-icon-menu"></i>
<span slot="title">导航二</span>
</el-menu-item>
<el-menu-item index="3" disabled>
<i class="el-icon-document"></i>
<span slot="title">导航三</span>
</el-menu-item>
<el-menu-item index="4">
<i class="el-icon-setting"></i>
<span slot="title">导航四</span>
</el-menu-item>
</el-menu>
</div>
</aside>
</template>
<script>
import { isURL } from '@/utils/validate'
export default {
data () {
return {
dynamicMenuRoutes: [],
menuValue: '',
leafMenu: []
}
},
components: {
},
computed: {
sidebarLayoutSkin: {
get () { return this.$store.state.common.sidebarLayoutSkin }
},
sidebarFold: {
get () { return this.$store.state.common.sidebarFold }
},
menuList: {
get () { return this.$store.state.common.menuList },
set (val) { this.$store.commit('common/updateMenuList', val) }
},
menuActiveName: {
get () { return this.$store.state.common.menuActiveName },
set (val) { this.$store.commit('common/updateMenuActiveName', val) }
},
mainTabs: {
get () { return this.$store.state.common.mainTabs },
set (val) { this.$store.commit('common/updateMainTabs', val) }
},
mainTabsActiveName: {
get () { return this.$store.state.common.mainTabsActiveName },
set (val) { this.$store.commit('common/updateMainTabsActiveName', val) }
},
showSidebar() {//在phone模式并且关闭菜单状态下不展示菜单
if (this.$store.state.common.clientType == 'phone' && this.sidebarFold) return false;
return true;
}
},
watch: {
$route: 'routeHandle'
},
created () {
this.menuList = JSON.parse(sessionStorage.getItem('menuList') || '[]')
this.dynamicMenuRoutes = JSON.parse(sessionStorage.getItem('dynamicMenuRoutes') || '[]')
this.routeHandle(this.$route)
this.getLeafMenu(this.menuList);
},
methods: {
getLeafMenu(menuList) {
menuList.forEach((item)=>{
if (item.list) {
this.getLeafMenu(item.list)
} else {
this.leafMenu.push(item)
}
})
},
chose() {
let route = this.dynamicMenuRoutes.filter(item => item.meta.menuId === this.menuValue)
if (route.length >= 1) {
this.$router.push({name: route[0].name})
}
this.menuValue = ''
},
// 路由操作
routeHandle (route) {
if (route.meta.isTab&&this.$store.state.common.navbarTab) {
// tab选中, 不存在先添加
var tab = this.mainTabs.filter(item => item.name === route.name)[0]
if (!tab) {
if (route.meta.isDynamic) {
var globalRoutes=this.$router.options.routes
var mainRoutes
for (var i = 0; i < globalRoutes.length; i++) {
if (globalRoutes[i].children && globalRoutes[i].children.length >= 1 && globalRoutes[i].name == 'main-dynamic') {
mainRoutes = globalRoutes[i].children
}
}
route = mainRoutes.filter(item => item.name === route.name)[0]
if (!route) {
return console.error('未能找到可用标签页!')
}
}
tab = {
menuId: route.meta.menuId || route.name,
name: route.name,
title: route.meta.title,
type: isURL(route.meta.iframeUrl) ? 'iframe' : 'module',
iframeUrl: route.meta.iframeUrl || ''
}
this.mainTabs = this.mainTabs.concat(tab)
}
this.menuActiveName = tab.menuId + ''
this.mainTabsActiveName = tab.name
}
}
}
}
</script>
Header
<template>
<nav class="site-navbar" :class="'site-navbar--' + navbarLayoutType">
<div class="site-navbar__header" :style="brandStyle">
<h1 class="site-navbar__brand" :style="brandStyle" @click="$router.push({ name: 'home' })">
<a class="site-navbar__brand-lg" href="javascript:;">后台管理系统</a>
<a class="site-navbar__brand-mini" href="javascript:;"></a>
</h1>
</div>
<div class="site-navbar__body clearfix">
<el-menu
class="site-navbar__menu"
mode="horizontal">
<el-menu-item class="site-navbar__switch" index="0" @click="sidebarFold = !sidebarFold">
<i class="el-icon-s-fold"></i>
</el-menu-item>
</el-menu>
<el-menu
class="site-navbar__menu site-navbar__menu--right"
mode="horizontal">
<el-menu-item class="site-navbar__avatar" index="3">
<el-dropdown :show-timeout="0" placement="bottom">
<span class="el-dropdown-link">
欢迎,登录!
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="updatePasswordHandle()">修改密码</el-dropdown-item>
<el-dropdown-item @click.native="logoutHandle()">退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-menu-item>
</el-menu>
</div>
</nav>
</template>
<script>
export default {
name: 'screenfull',
data () {
return {
updatePassowrdVisible: false,
isFullscreen: false,
}
},
components: {
},
computed: {
navbarLayoutType: {
get () { return this.$store.state.common.navbarLayoutType }
},
sidebarFold: {
get () { return this.$store.state.common.sidebarFold },
set (val) { this.$store.commit('common/updateSidebarFold', val) }
},
mainTabs: {
get () { return this.$store.state.common.mainTabs },
set (val) { this.$store.commit('common/updateMainTabs', val) }
},
userName: {
get () { return this.$store.state.user.name }
},
brandStyle: {
get() {
let type = this.$store.state.common.clientType;
if (type === 'phone') {
return this.brandStyle = {
float: 'none',
width: '100%'
}
} else {
return this.brandStyle = {}
}
},
set() {}
}
},
methods: {
// 修改密码
updatePasswordHandle () {
this.updatePassowrdVisible = true
this.$nextTick(() => {
this.$refs.updatePassowrd.init()
})
},
// 退出
logoutHandle () {
this.$router.push({ name: 'login' })
}
}
}
</script>
右侧主页面
主页面多个tabs标签页组成,
参考官网
main-content.vue
<template>
<main class="site-content" :class="{ 'site-content--tabs': $route.meta.isTab && this.$store.state.common.navbarTab}">
<!-- 主入口标签页 s -->
<el-tabs
v-if="$route.meta.isTab && this.$store.state.common.navbarTab"
v-model="mainTabsActiveName"
:closable="true"
@tab-click="selectedTabHandle"
@tab-remove="removeTabHandle">
<el-dropdown class="site-tabs__tools" :show-timeout="0">
<i class="el-icon-arrow-down el-icon--right"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="tabsCloseCurrentHandle">关闭当前标签页</el-dropdown-item>
<el-dropdown-item @click.native="tabsCloseOtherHandle">关闭其它标签页</el-dropdown-item>
<el-dropdown-item @click.native="tabsCloseAllHandle">关闭全部标签页</el-dropdown-item>
<el-dropdown-item @click.native="tabsRefreshCurrentHandle">刷新当前标签页</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-tab-pane
v-for="item in mainTabs"
:key="item.name"
:label="item.title"
:name="item.name">
<el-card :body-style="siteContentViewHeight">
<iframe
v-if="item.type === 'iframe'"
:src="item.iframeUrl"
width="100%" height="100%" frameborder="0" scrolling="yes">
</iframe>
<keep-alive v-else>
<router-view v-if="item.name === mainTabsActiveName" />
</keep-alive>
</el-card>
</el-tab-pane>
</el-tabs>
<!-- 主入口标签页 e -->
<el-card v-else :body-style="siteContentViewHeight">
<keep-alive>
<router-view />
</keep-alive>
</el-card>
</main>
</template>
<script>
import { isURL } from '@/utils/validate'
export default {
data () {
return {
}
},
computed: {
documentClientHeight: {
get () { return this.$store.state.common.documentClientHeight }
},
menuActiveName: {
get () { return this.$store.state.common.menuActiveName },
set (val) { this.$store.commit('common/updateMenuActiveName', val) }
},
mainTabs: {
get () { return this.$store.state.common.mainTabs },
set (val) { this.$store.commit('common/updateMainTabs', val) }
},
mainTabsActiveName: {
get () { return this.$store.state.common.mainTabsActiveName },
set (val) { this.$store.commit('common/updateMainTabsActiveName', val) }
},
siteContentViewHeight () {
var height = this.documentClientHeight - 50 - 30 - 2
if (this.$route.meta.isTab && this.$store.state.common.navbarTab) {
height -= 40
return isURL(this.$route.meta.iframeUrl) ? { height: height + 'px' } : { minHeight: height + 'px' }
}
return { minHeight: height + 'px' }
}
},
methods: {
// tabs, 选中tab
selectedTabHandle (tab) {
tab = this.mainTabs.filter(item => item.name === tab.name)
if (tab.length >= 1) {
this.$router.push({ name: tab[0].name })
}
},
// tabs, 删除tab
removeTabHandle (tabName) {
this.mainTabs = this.mainTabs.filter(item => item.name !== tabName)
if (this.mainTabs.length >= 1) {
// 当前选中tab被删除
if (tabName === this.mainTabsActiveName) {
this.$router.push({ name: this.mainTabs[this.mainTabs.length - 1].name }, () => {
this.mainTabsActiveName = this.$route.name
})
}
} else {
this.menuActiveName = ''
this.$router.push({ name: 'home' })
}
},
// tabs, 关闭当前
tabsCloseCurrentHandle () {
this.removeTabHandle(this.mainTabsActiveName)
},
// tabs, 关闭其它
tabsCloseOtherHandle () {
this.mainTabs = this.mainTabs.filter(item => item.name === this.mainTabsActiveName)
},
// tabs, 关闭全部
tabsCloseAllHandle () {
this.mainTabs = []
this.menuActiveName = ''
this.$router.push({ name: 'home' })
},
// tabs, 刷新当前
tabsRefreshCurrentHandle () {
var tempTabName = this.mainTabsActiveName
this.removeTabHandle(tempTabName)
this.$nextTick(() => {
this.$router.push({ name: tempTabName })
})
}
}
}
</script>
访问测试
列表页
list.vue
<template>
<div>
<el-form :model="dataForm" ref="dataForm">
<el-row :gutter="20" >
<el-col :xs="24" :sm="6" >
<el-form-item prop="classCode">
<el-input v-model="dataForm.name" placeholder="姓名" maxlength="20" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="6" >
<el-form-item prop="className">
<el-input v-model="dataForm.province" placeholder="省份" maxlength="20" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="{span:24,offset:0}" :sm="{span:6,offset:6}">
<el-form-item style="float: right">
<el-button type="primary" icon="el-icon-search">查询</el-button>
<el-button type="default" plain icon="el-icon-refresh-left">重置</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-divider></el-divider>
<el-row style="padding-bottom: 10px;" align="left">
<el-button type="primary" icon="el-icon-plus" @click="addPage()">新增</el-button>
<el-button type="success" icon="el-icon-mouse" @click="add()">测试</el-button>
<el-button type="info" @click="addPage()">弹框按钮</el-button>
</el-row>
<el-table
:data="tableData"
border
style="width: 100%">
<el-table-column
fixed
prop="date"
label="日期"
>
</el-table-column>
<el-table-column
prop="name"
label="姓名"
>
</el-table-column>
<el-table-column
prop="province"
label="省份"
width="120">
</el-table-column>
<el-table-column
prop="city"
label="市区"
width="120">
</el-table-column>
<el-table-column
prop="address"
label="地址"
width="300">
</el-table-column>
<el-table-column
prop="zip"
label="邮编"
width="120">
</el-table-column>
<el-table-column
fixed="right"
label="操作"
width="100">
<template slot-scope="scope">
<el-button @click="handleClick(scope.row)" type="text" size="small">查看</el-button>
<el-button type="text" size="small">编辑</el-button>
</template>
</el-table-column>
</el-table>
<add-page ref="addPage" @refreshDataList="test"></add-page>
</div>
</template>
<script>
import axios from 'axios'
import AddPage from './addPage'
export default {
components: {
AddPage
},
methods: {
handleClick(row) {
console.log(row);
},
test(){
axios
.get('http://127.0.0.1:8888/test', {
params: {
id: 12345
}
})
.then(response => (
// this.info = response
alert(response.data)
// this.info = response
))
.catch(function (error) { // 请求失败处理
console.log(error);
});
},
add(){
this.$router.push({ path:'/add'})
},
addPage(id){
this.$nextTick(() => {
this.$refs.addPage.init(id);
})
}
},
mounted () {
},
data() {
return {
dataForm: {
name: '',
province: ''
},
addPageVisible: false,
tableData: [{
date: '2016-05-02',
name: '王小虎',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-04',
name: '王小虎',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1517 弄',
zip: 200333
}, {
date: '2016-05-01',
name: '王小虎',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1519 弄',
zip: 200333
}, {
date: '2016-05-03',
name: '王小虎',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1516 弄',
zip: 200333
}]
}
}
}
</script>
<style scoped>
.el-divider--horizontal {
display: block;
height: 1px;
width: 100%;
margin: 12px 0;
}
</style>
演示截图
明细页面
addPage.vue
<template>
<el-dialog
class="custom_dialog"
:fullscreen=false
:show-close=false
:close-on-click-modal=false
:visible.sync="visible">
<div slot="title" class="dialog-title">
<i class="el-icon-edit-outline"></i>
<span class="title-text">数据详情</span>
</div>
<el-form :model="dataForm" :rules="dataRule" ref="dataForm"
label-width="100px">
<el-row :gutter="40">
<el-col :xs="24" :lg="24">
<el-form-item label="姓名:" prop="classCode">
<el-input v-model="dataForm.classCode" maxlength="20" placeholder="类型编码"></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :lg="24">
<el-form-item label="省份:" prop="className">
<el-input v-model="dataForm.className" maxlength="20" placeholder="类型名称"></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :lg="24">
<el-form-item label="邮编:" prop="dataName">
<el-input v-model="dataForm.dataName" maxlength="20" placeholder="名称"></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :lg="24">
<el-form-item label="邮编:" prop="dataName">
<el-input v-model="dataForm.dataName" maxlength="20" placeholder="名称"></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :lg="24">
<el-form-item label="市区:" prop="dataCode">
<el-input v-model="dataForm.dataCode" maxlength="20" placeholder="键值"></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :lg="24">
<el-form-item label="邮编:" prop="dataName">
<el-input v-model="dataForm.dataName" maxlength="20" placeholder="名称"></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :lg="24">
<el-form-item label="地址:" prop="dataOrder">
<el-input v-model="dataForm.dataOrder" maxlength="10" placeholder="排序"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button icon="el-icon-close" @click="visible = false">取消</el-button>
<el-button icon="el-icon-check" type="primary">提交</el-button>
</span>
</el-dialog>
</template>
<script>
export default {
data() {
return {
visible: false,
dataForm: {
id: '',
classCode: '',
className: '',
dataCode: '',
dataName: '',
dataOrder: '',
status: ''
},
dataRule: {
classCode: [
{required: true, message: '类型编码不能为空', trigger: 'blur'}
],
className: [
{required: true, message: '类型名称不能为空', trigger: 'blur'}
],
dataCode: [
{required: true, message: '键值不能为空', trigger: 'blur'}
],
dataName: [
{required: true, message: '名称不能为空', trigger: 'blur'}
],
dataOrder: [
{required: true, message: '排序不能为空', trigger: 'blur'}
],
status: [
{required: true, message: '状态不能为空', trigger: 'blur'}
]
}
}
},
methods: {
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
alert('submit!');
} else {
console.log('error submit!!');
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
},
init (id) {
// this.dataForm.id = id
this.visible = true
}
}
}
</script>
<style scoped>
</style>
演示样例
以上就是前端系统主要页面及常用组件,有新的组件可通过elementUI官网,查询具体用法,另外样式的底层逻辑是相同的。
关注公众号”小猿架构“,发送 "前后分离架构" ,下载课程视频+课程源码+课件。