前端源码:
https://github.com/elunez/eladmin-web
https://gitee.com/elunez/eladmin-web
个简单且易上手的 Spring boot 后台管理框架。我们可以通过这个框架快速的构建出一个前端搭载element-ui组件库,带有权限控制的后台管理系统。
前台页面千变万化,需求不同展现出的效果也不同,我们很难去找出一套模板供大家去参考使用,但后台管理系统则与之不同,他们的需求有很多都是存在共性的。
就像最常见的几种管理模式
admin
test
123456
# 配置镜像加速
https://www.ydyno.com/archives/1219.html
# 安装依赖
npm install
# 启动服务 localhost:8013
npm run dev
# 构建生产环境
npm run build:prod
Vue
vue-router
axios
element ui
|-- public 存放静态资源,存放在该文件夹的东西不会被打包影响,而是会原封不动的输出到dist文件夹中
|-- favicon.ico 网站图标
|-- index.html 主页,项目入口
|-- src
|-- api 后端请求接口文件
|-- assets 静态资源
|-- components 公用组件
|-- layout 系统布局:头部、侧边栏、设置、中间内容页面
|-- mixins 混入文件
|-- router 路由配置
|-- store vuex存放数据
|-- utils 工具包
|-- auth.js 授权管理
|-- clipboard.js 简单的剪切板
|-- datetime.js Date对象补充函数
|-- index.js 获取页面标题
|-- permission.js 权限管理工具方法
|-- request.js 拦截器
|-- rsaEncrypt.js 加密、解密
|-- shortcuts.js 日期工具补充
|-- upload.js 上传函数
|-- validate.js 一些正则验证
|-- views 页面
|-- components 组件管理模块
|-- dashboard 首页
|-- features 401、404等页面
|-- generator 代码生成页面
|-- mnt 运维管理模块
|-- monitor 系统监控模块
|-- nested 多级菜单模块
|-- system 系统管理模块
|-- tools 系统工具模块
|-- home.vue 基础页面(导航栏、左侧菜单栏)
|-- login.vue 登录页面
|-- app.vue 根组件
|-- main.js 入口文件
|-- settings.js 全局配置文件,存储一些键和值,供全局调用
|-- .gitignore git忽略上传的文件格式
|-- .env.development 开发环境下的接口地址配置
|-- .env.production 生产环境下的接口地址配置
|-- .eslintignore 不用校验的文件
|-- .eslintrc.js ES-lint校验(编码规范、校验规则)
|-- package.json 项目描述文件
|-- vue.config.js cli配置文件
新建一个一级菜单(以新建一个数据分析页面为例)
第一步:先在views
目录下,新建文件目录如下:
第二步:在菜单管理新增数据分析菜单
第三步:点击数据分析菜单,即可显示
新建一个二级菜单(以新建一个任务管理,包含二级菜单内容管理与类别管理 为例)
权限管理是需要菜单管理与代码配合实现的
以用户管理为例,用户管理有四种操作:用户创建、用户编辑、用户删除、同步,我们可以利用此框架实现给不同的角色分配不同的权限,有的用户只可以增,有的用户可以增删改等,满足不同的需求
用户管理菜单下有三个按钮并对应相应的权限标识
我们可以在角色管理页面为角色分配权限
给哪个角色分配相应的权限操作,则角色对应的用户则具有相应的操作权限
第三步:代码里为相应的按钮添加权限标识
先看user
下的index.vue
crudOperation
,CRUD.operation
是组件,头部增删改查按钮的封装
crudOperation
接收父组件传来的permisssion
参数,并对相应的按钮进行绑定。则可以实现,拥有admin
或 user:add
权限标识的用户可以创建用户,拥有admin
或 user:edit
权限标识的用户可以编辑用户…
经过上述分析,可以知道,使用权限的步骤一般为:
v-permission="XXX"
头部增删改查按钮以及右侧的刷新等,被封装在CRUD.operation
组件里
不显示某个按钮
因为CRUD.operation按钮绑定了显示值可以控制是否显示,以新增按钮为例:v-if=“crud.optShow.add”,所以我们只需要在页面的created生命周期里定义this.crud.optShow.add = false,则新增按钮就不会在页面显示了,其它按钮也一样
如图所示,CRUD.operation
组件里分别在左右设置了两个插槽
与
导入
导出模板
因为使用的是混入模式,所以页面会先执行混入模式里的代码,在执行本页面的代码,所以会产生一种情况是本页面需要查询出数据之后,把其中的数据作为参数传递给查询接口。
这时候我们只需要把混入模式里的自动请求查询接口的布尔值(queryOnPresenterCreated)设置为false,即
cruds() {
return CRUD({
url: 'api/fellow',
crudMethod: { ...crudFellowShip },
queryOnPresenterCreated: false
})
},
这样就不会自动查询数据了,我们可以获取到相应的数据之后,把数据作为参数,然后再手动调用toQuery
方法进行查询
views/xxx/index.vue
cruds() {
return CRUD({
title: "部署",
url: "api/deploy",
crudMethod: { ...crudDeploy },
});
},
components/Crud/crud.js
created() {
for (const k in this.$crud) {
if (this.$crud[k].queryOnPresenterCreated) {
// 默认查询
this.$crud[k].toQuery()
}
}
},
// 搜索
toQuery() {
crud.page.page = 1
crud.refresh()
},
// 刷新
refresh() {
if (!callVmHook(crud,CRUD.HOOK.beforeRefresh)) {
return
}
return new Promise((resolve, reject) => {
crud.loading = true
// 请求数据
initData(crud.url, crud.getQueryParams()).then(res => {
// console.log(123456)
// console.log(res)
const table = crud.getTable()
if (table && table.lazy) { // 懒加载子节点数据,清掉已加载的数据
table.store.states.treeData = {}
table.store.states.lazyTreeNodeMap = {}
}
crud.page.total = res.data.pages === undefined ? 0 : res.data.total
crud.data = res.data.pages === undefined ? res.data : res.data.records
crud.resetDataStatus()
// time 毫秒后显示表格
setTimeout(() => {
crud.loading = false
callVmHook(crud, CRUD.HOOK.afterRefresh)
}, crud.time)
resolve(data)
}).catch(err => {
crud.loading = false
reject(err)
})
})
},
api/data.js
export function initData(url, params) {
return request({
url: url + '?' + qs.stringify(params, { indices: false }),
method: 'get'
})
}
components/Crud/CRUD.operation.vue
<el-button
v-if="crud.optShow.add"
v-permission="permission.add"
class="filter-item"
size="mini"
type="primary"
icon="el-icon-plus"
@click="crud.toAdd"
>
新增
</el-button>
import CRUD, { crud } from '@crud/crud'
component/Crud/crud.js
// 点击新增按钮后,调用toAdd方法
toAdd() {
crud.resetForm() //调用重置表单方法,渲染表单
if (!(callVmHook(crud, CRUD.HOOK.beforeToAdd, crud.form) && callVmHook(crud, CRUD.HOOK.beforeToCU, crud.form))) {
return
}
crud.status.add = CRUD.STATUS.PREPARED
callVmHook(crud, CRUD.HOOK.afterToAdd, crud.form)
callVmHook(crud, CRUD.HOOK.afterToCU, crud.form)
},
/**
* 重置表单方法
* @param {Array} data 数据
*/
resetForm(data) {
const form = data || (typeof crud.defaultForm === 'object' ? JSON.parse(JSON.stringify(crud.defaultForm)) : crud.defaultForm.apply(crud.findVM('form')))
const crudFrom = crud.form
for (const key in form) {
if (crudFrom.hasOwnProperty(key)) {
crudFrom[key] = form[key]
} else {
Vue.set(crudFrom, key, form[key])
}
}
},
// hook VM
function callVmHook(crud, hook) {
if (crud.debug) {
console.log('callVmHook: ' + hook)
}
const tagHook = crud.tag ? hook + '$' + crud.tag : null
let ret = true
const nargs = [crud]
for (let i = 2; i < arguments.length; ++i) {
nargs.push(arguments[i])
}
// 有些组件扮演了多个角色,调用钩子时,需要去重
const vmSet = new Set()
crud.vms.forEach(vm => vm && vmSet.add(vm.vm))
vmSet.forEach(vm => {
if (vm[hook]) {
ret = vm[hook].apply(vm, nargs) !== false && ret
}
if (tagHook && vm[tagHook]) {
ret = vm[tagHook].apply(vm, nargs) !== false && ret
}
})
return ret
}
// 当表单新增/编辑完毕后,点击提交
/**
* 提交新增/编辑
*/
submitCU() {
if (!callVmHook(crud, CRUD.HOOK.beforeValidateCU)) {
return;
}
crud.findVM('form').$refs['form'].validate(valid => {
if (!valid) {
return;
}
if (!callVmHook(crud, CRUD.HOOK.afterValidateCU)) {
return;
}
// 根据crud状态判断是新增还是编辑
if (crud.status.add === CRUD.STATUS.PREPARED) {
crud.doAdd(); //执行添加
} else if (crud.status.edit === CRUD.STATUS.PREPARED) {
crud.doEdit();
}
});
},
/**
* 执行添加
*/
doAdd() {
if (!callVmHook(crud, CRUD.HOOK.beforeSubmit)) {
return;
}
crud.status.add = CRUD.STATUS.PROCESSING;
crud.crudMethod.add(crud.form).then(() => {
// 发送请求回调成功后,改变crud状态,重置表单,响应,刷新页面
crud.status.add = CRUD.STATUS.NORMAL;
crud.resetForm();
crud.addSuccessNotify();
callVmHook(crud, CRUD.HOOK.afterSubmit);
crud.toQuery();
}).catch(() => {
crud.status.add = CRUD.STATUS.PREPARED;
callVmHook(crud, CRUD.HOOK.afterAddError);
});
},
新增编辑前做的操作
import crudDeploy from "@/api/mnt/deploy";
[CRUD.HOOK.beforeToCU](crud, form) {
this.initSelect();
const deploys = [];
form.deploys.forEach(function (deploy, index) {
deploys.push(deploy.id);
});
this.form.deploys = deploys;
},
initSelect() {
crudDeploy.getApps().then((res) => {
this.apps = res.content;
});
crudDeploy.getServers().then((res) => {
this.servers = res.content;
});
},
api/mnt/deploy.js
export function getApps() {
return request({
url: 'api/app',
method: 'get'
})
}
export function getServers() {
return request({
url: 'api/server-deploy',
method: 'get'
})
}
点击提交前
[CRUD.HOOK.beforeSubmit]() {
const deploys = [];
this.form.deploys.forEach(function (data, index) {
const deploy = { id: data };
deploys.push(deploy);
});
this.form.deploys = deploys;
return true;
}
提交
<el-button
:loading="crud.status.cu === 2"
type="primary"
@click="crud.submitCU"
>确认</el-button>
import CRUD, { presenter, header, form, crud } from "@crud/crud";
/**
* 提交新增/编辑
*/
/components/Crud/crud.js
submitCU() {
if (!callVmHook(crud, CRUD.HOOK.beforeValidateCU)) {
return
}
crud.findVM('form').$refs['form'].validate(valid => {
if (!valid) {
return
}
if (!callVmHook(crud, CRUD.HOOK.afterValidateCU)) {
return
}
if (crud.status.add === CRUD.STATUS.PREPARED) {
crud.doAdd()
} else if (crud.status.edit === CRUD.STATUS.PREPARED) {
crud.doEdit()
}
})
},
doAdd() {
if (!callVmHook(crud, CRUD.HOOK.beforeSubmit)) {
return
}
crud.status.add = CRUD.STATUS.PROCESSING
crud.crudMethod.add(crud.form).then(() => {
crud.status.add = CRUD.STATUS.NORMAL
crud.resetForm()
crud.addSuccessNotify()
callVmHook(crud, CRUD.HOOK.afterSubmit)
crud.toQuery()
}).catch(() => {
crud.status.add = CRUD.STATUS.PREPARED
callVmHook(crud, CRUD.HOOK.afterAddError)2332342442we
})
},
crudMethod: {
add: (form) => { },
del: (id) => { },
edit: (form) => { },
get: (id) => { }
},
/views/xxx/index.vue
cruds() {
return CRUD({
title: "部署",
url: "api/deploy",
crudMethod: { ...crudDeploy },
});
},
/api/mnt/deploy.js
export function add(data) {
return request({
url: 'api/deploy',
method: 'post',
data
})
}
发起获取数据的请求,响应到数据中的res.data.records中
crudDeploy.getApps().then((res) => {
this.apps = res.data.records
})
crudDeploy.getServers().then((res) => {
this.servers = res.data.records
})
自动挂载
var app=new Vue({
el:'#app-3',
data:{
seen:true
}
})
手动挂载
//可以实现延迟按需挂载
<p id="app">
{{name}}
</p>
<button οnclick="test()">挂载
</button>
<script>
var obj= {name: '张三'}
var vm = new Vue({
data: obj
})
function test() {
vm.$mount("#app");
}
</script>
// Vue.extend()创建没有挂载的的子类,可以使用该子累创建多个实例
var app= Vue.extend({
template: '{{message}}',
data: function () {
return {
message: 'message'
}
}
})
new app().$mount('#app') // 创建 app实例,并挂载到一个元素上
①看控制台请求是否返回,查看状态码
②查看响应拦截器
③询问后端返回的状态码是否和前端响应处理的一致
1. 不带参数
<router-link :to="{name:'home'}">
<router-link :to="{path:'/home'}"> //name,path都行, 建议用name
// 注意:router-link中链接如果是'/'开始就是从根路由开始,如果开始不带'/',则从当前路由开始。
2.带参数
<router-link :to="{name:'home', params: {id:1}}">
// params传参数 (类似post)
// 路由配置 path: "/home/:id" 或者 path: "/home:id"
// 不配置path ,第一次可请求,刷新页面id会消失
// 配置path,刷新页面id会保留
// html 取参 $route.params.id
// script 取参 this.$route.params.id
<router-link :to="{name:'home', query: {id:1}}">
// query传参数 (类似get,url后面会显示参数)
// 路由可不配置
// html 取参 $route.query.id
// script 取参 this.$route.query.id
1. 不带参数
this.$router.push('/home')
this.$router.push({name:'home'})
this.$router.push({path:'/home'})
2. query传参
this.$router.push({name:'home',query: {id:'1'}})
this.$router.push({path:'/home',query: {id:'1'}})
// html 取参 $route.query.id
// script 取参 this.$route.query.id
3. params传参
this.$router.push({name:'home',params: {id:'1'}}) // 只能用 name
// 路由配置 path: "/home/:id" 或者 path: "/home:id" ,
// 不配置path ,第一次可请求,刷新页面id会消失
// 配置path,刷新页面id会保留
// html 取参 $route.params.id
// script 取参 this.$route.params.id
4. query和params区别
query类似 get, 跳转之后页面 url后面会拼接参数,类似?id=1, 非重要性的可以这样传, 密码之类还是用params刷新页面id还在
params类似 post, 跳转之后页面 url后面不会拼接参数 , 但是刷新页面id 会消失
repalace和push的区别:push跳转后会向history栈中添加一个记录,而replace不会,点击后退会跳转到上上个页面。
n代表向前或向后跳转几个页面,正数为前,负数为后
v-model=“value”
使用:value="item.label+’/’+item.value "
value.split(’/’)[0]
参考:
https://blog.csdn.net/Superman_peng/article/details/110305237
https://blog.csdn.net/qq_43781399/article/details/110286287
https://blog.csdn.net/weixin_44932487/article/details/114501495