vue perm and some bugs
This commit is contained in:
parent
e266347d47
commit
4123fe657b
|
@ -15,6 +15,7 @@
|
|||
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml"
|
||||
},
|
||||
"dependencies": {
|
||||
"@riophae/vue-treeselect": "^0.4.0",
|
||||
"axios": "0.18.1",
|
||||
"element-ui": "2.13.0",
|
||||
"file-saver": "^2.0.2",
|
||||
|
|
|
@ -4,7 +4,7 @@ export function getDictTypeList(query) {
|
|||
return request({
|
||||
url: '/system/dicttype/',
|
||||
method: 'get',
|
||||
params:query
|
||||
params: query
|
||||
})
|
||||
}
|
||||
export function createDictType(data) {
|
||||
|
@ -32,7 +32,7 @@ export function getDictList(query) {
|
|||
return request({
|
||||
url: '/system/dict/',
|
||||
method: 'get',
|
||||
params:query
|
||||
params: query
|
||||
})
|
||||
}
|
||||
export function createDict(data) {
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
export function getPermAll() {
|
||||
return request({
|
||||
url: '/system/permission/',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
export function createPerm(data) {
|
||||
return request({
|
||||
url: '/system/permission/',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
export function updatePerm(id, data) {
|
||||
return request({
|
||||
url: `/system/permission/${id}/`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
export function deletePerm(id) {
|
||||
return request({
|
||||
url: `/system/permission/${id}/`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
|
@ -24,7 +24,7 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
title: 'Vue Admin Template',
|
||||
title: '认证系统',
|
||||
logo: 'https://wpimg.wallstcn.com/69a1c46c-eb1c-4b46-8bd4-e9e686ef5251.png'
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ export const asyncRoutes = [
|
|||
path: 'organization',
|
||||
name: 'Organization',
|
||||
component: () => import('@/views/system/organization'),
|
||||
meta: { title: '部门管理', icon: 'tree', perms: ['organization_manage'] }
|
||||
meta: { title: '部门管理', icon: 'tree', perms: ['org_manage'] }
|
||||
},
|
||||
{
|
||||
path: 'role',
|
||||
|
@ -100,6 +100,41 @@ export const asyncRoutes = [
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/develop',
|
||||
component: Layout,
|
||||
redirect: '/develop/perm',
|
||||
name: 'Develop',
|
||||
meta: { title: '开发配置', icon: 'example', perms: ['dev_set'] },
|
||||
children: [
|
||||
{
|
||||
path: 'perm',
|
||||
name: 'Perm',
|
||||
component: () => import('@/views/system/perm'),
|
||||
meta: { title: '权限菜单', icon: 'example', perms: ['perm_manage'] }
|
||||
},
|
||||
{
|
||||
path: 'external-link',
|
||||
component: Layout,
|
||||
children: [
|
||||
{
|
||||
path: process.env.VUE_APP_BASE_API + '/docs/',
|
||||
meta: { title: '接口文档', icon: 'link', perms: ['dev_docs'] }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'external-link',
|
||||
component: Layout,
|
||||
children: [
|
||||
{
|
||||
path: process.env.VUE_APP_BASE_API + '/admin/',
|
||||
meta: { title: 'Django后台', icon: 'link', perms: ['dev_admin'] }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
// 404 page must be placed at the end !!!
|
||||
{ path: '*', redirect: '/404', hidden: true }
|
||||
]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import defaultSettings from '@/settings'
|
||||
|
||||
const title = defaultSettings.title || 'Vue Admin Template'
|
||||
const title = defaultSettings.title || '认证系统'
|
||||
|
||||
export default function getPageTitle(pageTitle) {
|
||||
if (pageTitle) {
|
||||
|
|
|
@ -2,9 +2,12 @@
|
|||
<div class="app-container">
|
||||
<el-row :gutter="10">
|
||||
<el-col :md="6">
|
||||
<div style="margin-top:10px">
|
||||
<el-button type="primary" icon="el-icon-plus" @click="handleAddDictType">新增</el-button>
|
||||
<el-card>
|
||||
<div slot="header" class="clearfix">
|
||||
<span>字典类型</span>
|
||||
<el-button v-if="checkPermission(['dicttype_add'])" style="float: right; padding: 3px 0" type="text" icon="el-icon-plus" @click="handleAddDictType" >新增</el-button>
|
||||
</div>
|
||||
|
||||
<el-tree
|
||||
ref="tree"
|
||||
v-loading="treeLoding"
|
||||
|
@ -16,13 +19,36 @@
|
|||
:filter-node-method="filterNode"
|
||||
style="margin-top:10px;"
|
||||
@node-click="handleDictTypeClick"
|
||||
/>
|
||||
>
|
||||
<span class="custom-tree-node" slot-scope="{ node, data }">
|
||||
<span>{{ node.label }}</span>
|
||||
<span>
|
||||
<el-button
|
||||
type="text"
|
||||
size="mini"
|
||||
v-if="checkPermission(['dicttype_delete'])"
|
||||
@click="handleEditDictType(data)">
|
||||
编辑
|
||||
</el-button>
|
||||
<!-- <el-button
|
||||
type="text"
|
||||
size="mini"
|
||||
@click="handleDeleteDictType">
|
||||
删除
|
||||
</el-button> -->
|
||||
</span>
|
||||
</span>
|
||||
</el-tree>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :md="18">
|
||||
<div style="margin-top:10px">
|
||||
<el-button type="primary" icon="el-icon-plus" @click="handleAddDict">新增</el-button>
|
||||
<el-card>
|
||||
<div slot="header" class="clearfix">
|
||||
<span>字典详情</span>
|
||||
</div>
|
||||
<el-button v-show="listQuery.type" type="primary" icon="el-icon-plus" @click="handleAddDict" >新增</el-button>
|
||||
<el-table
|
||||
v-show="listQuery.type"
|
||||
v-loading="listLoading"
|
||||
:data="dictList.results"
|
||||
style="width: 100%;margin-top:10px;"
|
||||
|
@ -65,7 +91,7 @@
|
|||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<p v-show="!listQuery.type">点击左侧类型查看字典</p>
|
||||
<pagination
|
||||
v-show="dictList.count>0"
|
||||
:total="dictList.count"
|
||||
|
@ -73,6 +99,7 @@
|
|||
:limit.sync="listQuery.page_size"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
|
@ -84,6 +111,9 @@
|
|||
<el-form-item label="代号" prop="code">
|
||||
<el-input v-model="dicttype.code" placeholder="代号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="父级" prop="pid">
|
||||
<treeselect v-model="dicttype.pid" :multiple="false" :options="dictTypeData" placeholder="父级"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div style="text-align:right;">
|
||||
<el-button type="danger" @click="dgV1=false">取消</el-button>
|
||||
|
@ -106,11 +136,23 @@
|
|||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<style>
|
||||
.custom-tree-node {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import { getDictTypeList, getDictList, createDictType, createDict, updateDictType, updateDict, deleteDictType, deleteDict } from '@/api/dict'
|
||||
import { genTree } from '@/utils'
|
||||
import checkPermission from '@/utils/permission'
|
||||
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
||||
|
||||
const defaultDictType = {
|
||||
id: '',
|
||||
|
@ -126,16 +168,16 @@ const defaultDict = {
|
|||
type: null
|
||||
}
|
||||
export default {
|
||||
components: { Pagination },
|
||||
components: { Pagination, Treeselect },
|
||||
data() {
|
||||
return {
|
||||
dicttype: defaultDictType,
|
||||
dict: defaultDict,
|
||||
dictList: {count:0},
|
||||
dictList: { count: 0 },
|
||||
listLoading: true,
|
||||
listQuery: {
|
||||
page: 1,
|
||||
page_size: 20,
|
||||
page_size: 20
|
||||
},
|
||||
dgV1: false,
|
||||
dgT1: 'new',
|
||||
|
@ -177,8 +219,8 @@ export default {
|
|||
getList() {
|
||||
this.listLoading = true
|
||||
getDictList(this.listQuery).then(response => {
|
||||
if (response.code>=200){
|
||||
if (response.data.results){
|
||||
if (response.code >= 200) {
|
||||
if (response.data.results) {
|
||||
this.dictList = response.data
|
||||
}
|
||||
this.listLoading = false
|
||||
|
@ -202,42 +244,54 @@ export default {
|
|||
},
|
||||
handleAddDict() {
|
||||
this.dict = Object.assign({}, defaultDict)
|
||||
this.dict.type = this.listQuery.type
|
||||
this.dgT2 = 'new'
|
||||
this.dgV2 = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs['Form2'].clearValidate()
|
||||
})
|
||||
},
|
||||
// handleEdit(scope) {
|
||||
// this.user = Object.assign({}, scope.row) // copy obj
|
||||
// this.dialogType = 'edit'
|
||||
// this.dialogVisible = true
|
||||
// this.$nextTick(() => {
|
||||
// this.$refs['Form'].clearValidate()
|
||||
// })
|
||||
// },
|
||||
// handleDelete(scope) {
|
||||
// this.$confirm('确认删除?', '警告', {
|
||||
// confirmButtonText: '确认',
|
||||
// cancelButtonText: '取消',
|
||||
// type: 'error'
|
||||
// })
|
||||
// .then(async() => {
|
||||
// await deleteUser(scope.row.id)
|
||||
// this.dictList.splice(scope.row.index, 1)
|
||||
// this.$message({
|
||||
// type: 'success',
|
||||
// message: '成功删除!'
|
||||
// })
|
||||
// })
|
||||
// .catch(err => {
|
||||
// console.error(err)
|
||||
// })
|
||||
// },
|
||||
handleEdit(scope) {
|
||||
this.dict = Object.assign({}, scope.row) // copy obj
|
||||
this.dgT2 = 'edit'
|
||||
this.dgV2 = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs['Form2'].clearValidate()
|
||||
})
|
||||
},
|
||||
handleEditDictType(val) {
|
||||
this.dicttype = val // copy obj
|
||||
this.dgT1 = 'edit'
|
||||
this.dgV1 = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs['Form1'].clearValidate()
|
||||
})
|
||||
|
||||
},
|
||||
handleDelete(scope) {
|
||||
this.$confirm('确认删除?', '警告', {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'error'
|
||||
})
|
||||
.then(async() => {
|
||||
const { code } = await deleteDict(scope.row.id)
|
||||
if (code>=200){
|
||||
this.dictList.results.splice(scope.row.index, 1)
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '成功删除!'
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
})
|
||||
},
|
||||
async confirm1(form) {
|
||||
this.$refs[form].validate(valid => {
|
||||
if (valid) {
|
||||
const isEdit = this.dialogType === 'edit'
|
||||
const isEdit = this.dgT1 === 'edit'
|
||||
if (isEdit) {
|
||||
updateDictType(this.dicttype.id, this.dicttype).then(res => {
|
||||
if (res.code >= 200) {
|
||||
|
@ -269,6 +323,42 @@ export default {
|
|||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
async confirm2(form) {
|
||||
this.$refs[form].validate(valid => {
|
||||
if (valid) {
|
||||
const isEdit = this.dgT2 === 'edit'
|
||||
if (isEdit) {
|
||||
updateDict(this.dict.id, this.dict).then(res => {
|
||||
if (res.code >= 200) {
|
||||
this.getList()
|
||||
this.dgV2 = false
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '编辑成功',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
createDict(this.dict).then(res => {
|
||||
if (res.code >= 200) {
|
||||
this.getList()
|
||||
this.dgV2 = false
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '新增成功',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,13 +60,7 @@
|
|||
<el-input v-model="org.name" placeholder="名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="上级部门" prop="pid">
|
||||
<el-cascader
|
||||
v-model="org.pid"
|
||||
:options="pidList"
|
||||
:props="{ checkStrictly: true }"
|
||||
clearable
|
||||
style="width:100%"
|
||||
/>
|
||||
<treeselect v-model="org.pid" :multiple="false" :options="tableData" placeholder="父级"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div style="text-align:right;">
|
||||
|
@ -81,13 +75,16 @@
|
|||
import { getOrgAll, createOrg, deleteOrg, updateOrg } from '@/api/org'
|
||||
import { genTree } from '@/utils'
|
||||
import checkPermission from '@/utils/permission'
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
||||
|
||||
const defaultOrg = {
|
||||
id: '',
|
||||
id: null,
|
||||
name: '',
|
||||
pid: ''
|
||||
pid: null
|
||||
}
|
||||
export default {
|
||||
components: { Treeselect },
|
||||
data() {
|
||||
return {
|
||||
org: {
|
||||
|
@ -98,7 +95,6 @@ export default {
|
|||
search: '',
|
||||
tableData: [],
|
||||
orgList: [],
|
||||
pidList: [],
|
||||
listLoading: true,
|
||||
dialogVisible: false,
|
||||
dialogType: 'new',
|
||||
|
@ -120,7 +116,6 @@ export default {
|
|||
this.orgList = response.data
|
||||
const data = genTree(response.data)
|
||||
this.tableData = data
|
||||
this.pidList = data
|
||||
this.listLoading = false
|
||||
})
|
||||
},
|
||||
|
@ -174,7 +169,6 @@ export default {
|
|||
if (valid) {
|
||||
const isEdit = this.dialogType === 'edit'
|
||||
if (isEdit) {
|
||||
this.org.pid = this.org.pid.pop()
|
||||
updateOrg(this.org.id, this.org).then(() => {
|
||||
this.getList()
|
||||
this.dialogVisible = false
|
||||
|
@ -186,7 +180,6 @@ export default {
|
|||
})
|
||||
})
|
||||
} else {
|
||||
this.org.pid = this.org.pid.pop()
|
||||
createOrg(this.org).then(res => {
|
||||
// this.org = res.data
|
||||
// this.tableData.unshift(this.org)
|
||||
|
|
|
@ -0,0 +1,215 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<div style="margin-top:10px">
|
||||
<el-input
|
||||
v-model="search"
|
||||
placeholder="输入权限名称进行搜索"
|
||||
style="width: 200px;"
|
||||
class="filter-item"
|
||||
@keyup.native="handleFilter"
|
||||
/>
|
||||
<el-button type="primary" icon="el-icon-plus" @click="handleAdd">新增</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
v-loading="listLoading"
|
||||
:data="tableData.filter(data => !search || data.name.toLowerCase().includes(search.toLowerCase()))"
|
||||
style="width: 100%;margin-top:10px;"
|
||||
border
|
||||
fit
|
||||
stripe
|
||||
highlight-current-row
|
||||
max-height="600"
|
||||
row-key="id"
|
||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
|
||||
>
|
||||
<el-table-column label="菜单名称">
|
||||
<template slot-scope="scope">{{ scope.row.name }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="类型">
|
||||
<template slot-scope="scope">{{ scope.row.type }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="代号">
|
||||
<template slot-scope="scope">{{ scope.row.method }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序">
|
||||
<template slot-scope="scope">{{ scope.row.sort }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="操作">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="el-icon-edit"
|
||||
:disabled="!checkPermission(['perm_update'])"
|
||||
@click="handleEdit(scope)"
|
||||
/>
|
||||
<el-button
|
||||
type="danger"
|
||||
size="small"
|
||||
icon="el-icon-delete"
|
||||
:disabled="!checkPermission(['perm_delete'])"
|
||||
@click="handleDelete(scope)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog :visible.sync="dialogVisible" :title="dialogType==='edit'?'编辑':'新增'">
|
||||
<el-form ref="Form" :model="perm" label-width="80px" label-position="right" :rules="rule1">
|
||||
<el-form-item label="类型">
|
||||
<el-radio-group v-model="perm.type">
|
||||
<el-radio label="目录"></el-radio>
|
||||
<el-radio label="菜单"></el-radio>
|
||||
<el-radio label="接口"></el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input v-model="perm.name" placeholder="名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="代号" prop="method">
|
||||
<el-input v-model="perm.method" placeholder="代号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="父级" prop="pid">
|
||||
<treeselect v-model="perm.pid" :multiple="false" :options="tableData" placeholder="父级"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<el-input-number v-model="perm.sort" :min="1" label="排序"></el-input-number>
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
<div style="text-align:right;">
|
||||
<el-button type="danger" @click="dialogVisible=false">取消</el-button>
|
||||
<el-button type="primary" @click="confirmPerm('Form')">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getPermAll, createPerm, deletePerm, updatePerm } from '@/api/perm'
|
||||
import { genTree } from '@/utils'
|
||||
import checkPermission from '@/utils/permission'
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
||||
const defaultPerm = {
|
||||
id: null,
|
||||
name: '',
|
||||
type: '目录',
|
||||
method: '',
|
||||
sort:1,
|
||||
pid: null
|
||||
}
|
||||
export default {
|
||||
components: { Treeselect },
|
||||
data() {
|
||||
return {
|
||||
perm: defaultPerm,
|
||||
search: '',
|
||||
tableData: [],
|
||||
permList: [],
|
||||
listLoading: true,
|
||||
dialogVisible: false,
|
||||
dialogType: 'new',
|
||||
rule1: {
|
||||
name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
|
||||
method: [{ required: true, message: '请输入代号', trigger: 'blur' }]
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
created() {
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
checkPermission,
|
||||
getList() {
|
||||
this.listLoading = true
|
||||
getPermAll().then(response => {
|
||||
this.permList = response.data
|
||||
const data = genTree(response.data)
|
||||
this.tableData = data
|
||||
this.listLoading = false
|
||||
})
|
||||
},
|
||||
resetFilter() {
|
||||
this.getList()
|
||||
},
|
||||
handleFilter() {
|
||||
const newData = this.permList.filter(
|
||||
data =>
|
||||
!this.search ||
|
||||
data.name.toLowerCase().includes(this.search.toLowerCase())
|
||||
)
|
||||
this.tableData = genTree(newData)
|
||||
},
|
||||
handleAdd() {
|
||||
this.perm = Object.assign({}, defaultPerm)
|
||||
this.dialogType = 'new'
|
||||
this.dialogVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs['Form'].clearValidate()
|
||||
})
|
||||
},
|
||||
handleEdit(scope) {
|
||||
this.perm = Object.assign({}, scope.row) // copy obj
|
||||
this.dialogType = 'edit'
|
||||
this.dialogVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs['Form'].clearValidate()
|
||||
})
|
||||
},
|
||||
handleDelete(scope) {
|
||||
this.$confirm('确认删除?', '警告', {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'error'
|
||||
})
|
||||
.then(async() => {
|
||||
await deletePerm(scope.row.id)
|
||||
this.getList()
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '成功删除!'
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
})
|
||||
},
|
||||
async confirmPerm(form) {
|
||||
this.$refs[form].validate(valid => {
|
||||
if (valid) {
|
||||
const isEdit = this.dialogType === 'edit'
|
||||
if (isEdit) {
|
||||
updatePerm(this.perm.id, this.perm).then(() => {
|
||||
this.getList()
|
||||
this.dialogVisible = false
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '编辑成功',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
})
|
||||
} else {
|
||||
createPerm(this.perm).then(res => {
|
||||
// this.perm = res.data
|
||||
// this.tableData.unshift(this.perm)
|
||||
this.getList()
|
||||
this.dialogVisible = false
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '新增成功',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
})
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,8 +1,7 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-row :gutter="10">
|
||||
|
||||
<el-col :md="6">
|
||||
<el-col :span="6">
|
||||
<el-input v-model="filterOrgText" placeholder="输入部门名进行过滤" />
|
||||
|
||||
<el-tree
|
||||
|
@ -18,7 +17,7 @@
|
|||
@node-click="handleOrgClick"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :md="18">
|
||||
<el-col :span="18">
|
||||
<div>
|
||||
<el-select
|
||||
v-model="listQuery.is_active"
|
||||
|
@ -138,13 +137,7 @@
|
|||
<el-input v-model="user.username" placeholder="账户" />
|
||||
</el-form-item>
|
||||
<el-form-item label="所属部门" prop="dept">
|
||||
<el-cascader
|
||||
v-model="user.dept"
|
||||
:options="orgData"
|
||||
:props="{ checkStrictly: true }"
|
||||
clearable
|
||||
style="width:100%"
|
||||
/>
|
||||
<treeselect v-model="user.dept" :multiple="false" :options="orgData" placeholder="所属部门"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="角色" prop="roles">
|
||||
<el-select v-model="user.roles" multiple placeholder="请选择" style="width:100%">
|
||||
|
@ -166,7 +159,7 @@
|
|||
:before-upload="beforeAvatarUpload"
|
||||
:headers="myHeaders"
|
||||
>
|
||||
<img v-if="user.avatar" :src="user.avatar" class="avatar">
|
||||
<img v-if="user.avatar" :src="user.avatar" class="avatar" />
|
||||
<i v-else class="el-icon-plus avatar-uploader-icon" />
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
|
@ -204,36 +197,37 @@
|
|||
}
|
||||
</style>
|
||||
<script>
|
||||
import { getUserList, createUser, deleteUser, updateUser } from '@/api/user'
|
||||
import { getOrgAll } from '@/api/org'
|
||||
import { getRoleAll } from '@/api/role'
|
||||
import { genTree } from '@/utils'
|
||||
import checkPermission from '@/utils/permission'
|
||||
import { uploadUrl } from '@/api/file'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
|
||||
|
||||
import { getUserList, createUser, deleteUser, updateUser } from "@/api/user";
|
||||
import { getOrgAll } from "@/api/org";
|
||||
import { getRoleAll } from "@/api/role";
|
||||
import { genTree } from "@/utils";
|
||||
import checkPermission from "@/utils/permission";
|
||||
import { uploadUrl } from "@/api/file";
|
||||
import { getToken } from "@/utils/auth";
|
||||
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
||||
const defaultUser = {
|
||||
id: '',
|
||||
name: '',
|
||||
username: '',
|
||||
id: "",
|
||||
name: "",
|
||||
username: "",
|
||||
dept: null,
|
||||
avatar: ''
|
||||
}
|
||||
avatar: "/media/default/avatar.png"
|
||||
};
|
||||
export default {
|
||||
components: { Pagination },
|
||||
components: { Pagination, Treeselect },
|
||||
data() {
|
||||
return {
|
||||
user: {
|
||||
id: '',
|
||||
name: '',
|
||||
username: '',
|
||||
id: "",
|
||||
name: "",
|
||||
username: "",
|
||||
dept: null,
|
||||
avatar: ''
|
||||
avatar: ""
|
||||
},
|
||||
myHeaders: { Authorization: 'JWT ' + getToken() },
|
||||
myHeaders: { Authorization: "JWT " + getToken() },
|
||||
uploadUrl: uploadUrl(),
|
||||
userList: [],
|
||||
userList: {'count':0},
|
||||
roles: [],
|
||||
listLoading: true,
|
||||
listQuery: {
|
||||
|
@ -241,163 +235,161 @@ export default {
|
|||
page_size: 20
|
||||
},
|
||||
enabledOptions: [
|
||||
{ key: 'true', display_name: '激活' },
|
||||
{ key: 'false', display_name: '禁用' }
|
||||
{ key: "true", display_name: "激活" },
|
||||
{ key: "false", display_name: "禁用" }
|
||||
],
|
||||
dialogVisible: false,
|
||||
dialogType: 'new',
|
||||
dialogType: "new",
|
||||
rule1: {
|
||||
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
|
||||
username: [{ required: true, message: '请输入账号', trigger: 'change' }]
|
||||
name: [{ required: true, message: "请输入姓名", trigger: "blur" }],
|
||||
username: [{ required: true, message: "请输入账号", trigger: "change" }]
|
||||
// password: [
|
||||
// { required: true, message: '请输入密码', trigger: 'change' }
|
||||
// ],
|
||||
},
|
||||
filterOrgText: '',
|
||||
filterOrgText: "",
|
||||
treeLoding: false,
|
||||
orgData: []
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {},
|
||||
watch: {
|
||||
filterOrgText(val) {
|
||||
this.$refs.tree.filter(val)
|
||||
this.$refs.tree.filter(val);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
this.getOrgAll()
|
||||
this.getRoleAll()
|
||||
this.getList();
|
||||
this.getOrgAll();
|
||||
this.getRoleAll();
|
||||
},
|
||||
methods: {
|
||||
checkPermission,
|
||||
handleAvatarSuccess(res, file) {
|
||||
if (res.code === 200) {
|
||||
this.user.avatar = res.data.path
|
||||
this.user.avatar = res.data.path;
|
||||
} else {
|
||||
this.$message.error('头像上传失败!')
|
||||
this.$message.error("头像上传失败!");
|
||||
}
|
||||
},
|
||||
beforeAvatarUpload(file) {
|
||||
const isLt2M = file.size / 1024 / 1024 < 2
|
||||
const isLt2M = file.size / 1024 / 1024 < 2;
|
||||
if (!isLt2M) {
|
||||
this.$message.error('上传头像图片大小不能超过 2MB!')
|
||||
this.$message.error("上传头像图片大小不能超过 2MB!");
|
||||
}
|
||||
return isLt2M
|
||||
return isLt2M;
|
||||
},
|
||||
filterNode(value, data) {
|
||||
if (!value) return true
|
||||
return data.label.indexOf(value) !== -1
|
||||
if (!value) return true;
|
||||
return data.label.indexOf(value) !== -1;
|
||||
},
|
||||
handleOrgClick(obj, node, vue) {
|
||||
this.listQuery.page = 1
|
||||
this.listQuery.dept = obj.id
|
||||
this.getList()
|
||||
this.listQuery.page = 1;
|
||||
this.listQuery.dept = obj.id;
|
||||
this.getList();
|
||||
},
|
||||
getList() {
|
||||
this.listLoading = true
|
||||
this.listLoading = true;
|
||||
getUserList(this.listQuery).then(response => {
|
||||
if (response.data) {
|
||||
this.userList = response.data
|
||||
this.listLoading = false
|
||||
})
|
||||
}
|
||||
this.listLoading = false;
|
||||
});
|
||||
},
|
||||
getOrgAll() {
|
||||
this.treeLoding = true
|
||||
this.treeLoding = true;
|
||||
getOrgAll().then(response => {
|
||||
this.orgData = genTree(response.data)
|
||||
this.treeLoding = false
|
||||
})
|
||||
this.orgData = genTree(response.data);
|
||||
this.treeLoding = false;
|
||||
});
|
||||
},
|
||||
getRoleAll() {
|
||||
getRoleAll().then(response => {
|
||||
this.roles = genTree(response.data)
|
||||
})
|
||||
this.roles = genTree(response.data);
|
||||
});
|
||||
},
|
||||
resetFilter() {
|
||||
this.listQuery = {
|
||||
page: 1,
|
||||
page_size: 20
|
||||
}
|
||||
this.getList()
|
||||
};
|
||||
this.getList();
|
||||
},
|
||||
handleFilter() {
|
||||
this.listQuery.page = 1
|
||||
this.getList()
|
||||
this.listQuery.page = 1;
|
||||
this.getList();
|
||||
},
|
||||
handleAddUser() {
|
||||
this.user = Object.assign({}, defaultUser)
|
||||
this.dialogType = 'new'
|
||||
this.dialogVisible = true
|
||||
this.user = Object.assign({}, defaultUser);
|
||||
this.dialogType = "new";
|
||||
this.dialogVisible = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs['Form'].clearValidate()
|
||||
})
|
||||
this.$refs["Form"].clearValidate();
|
||||
});
|
||||
},
|
||||
handleEdit(scope) {
|
||||
this.user = Object.assign({}, scope.row) // copy obj
|
||||
this.dialogType = 'edit'
|
||||
this.dialogVisible = true
|
||||
this.user = Object.assign({}, scope.row); // copy obj
|
||||
this.dialogType = "edit";
|
||||
this.dialogVisible = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs['Form'].clearValidate()
|
||||
})
|
||||
this.$refs["Form"].clearValidate();
|
||||
});
|
||||
},
|
||||
handleDelete(scope) {
|
||||
this.$confirm('确认删除?', '警告', {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'error'
|
||||
this.$confirm("确认删除?", "警告", {
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "取消",
|
||||
type: "error"
|
||||
})
|
||||
.then(async() => {
|
||||
await deleteUser(scope.row.id)
|
||||
this.userList.splice(scope.row.index, 1)
|
||||
.then(async () => {
|
||||
await deleteUser(scope.row.id);
|
||||
this.userList.splice(scope.row.index, 1);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '成功删除!'
|
||||
})
|
||||
type: "success",
|
||||
message: "成功删除!"
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
})
|
||||
console.error(err);
|
||||
});
|
||||
},
|
||||
async confirm(form) {
|
||||
this.$refs[form].validate(valid => {
|
||||
if (valid) {
|
||||
const isEdit = this.dialogType === 'edit'
|
||||
const isEdit = this.dialogType === "edit";
|
||||
if (isEdit) {
|
||||
if (this.user.dept instanceof Array) {
|
||||
this.user.dept = this.user.dept.pop()
|
||||
}
|
||||
updateUser(this.user.id, this.user).then(res => {
|
||||
if (res.code >= 200) {
|
||||
this.getList()
|
||||
this.dialogVisible = false
|
||||
this.getList();
|
||||
this.dialogVisible = false;
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '编辑成功',
|
||||
type: 'success',
|
||||
title: "成功",
|
||||
message: "编辑成功",
|
||||
type: "success",
|
||||
duration: 2000
|
||||
})
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
} else {
|
||||
this.user.dept = this.user.dept.pop()
|
||||
createUser(this.user).then(res => {
|
||||
if (res.code >= 200) {
|
||||
this.getList()
|
||||
this.dialogVisible = false
|
||||
this.getList();
|
||||
this.dialogVisible = false;
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '新增成功',
|
||||
type: 'success',
|
||||
title: "成功",
|
||||
message: "新增成功",
|
||||
type: "success",
|
||||
duration: 2000
|
||||
})
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# Generated by Django 3.0.5 on 2020-05-17 06:00
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('system', '0002_dict_dicttype'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='dict',
|
||||
options={'verbose_name': '字典', 'verbose_name_plural': '字典'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='dicttype',
|
||||
options={'verbose_name': '字典类型', 'verbose_name_plural': '字典类型'},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='permission',
|
||||
name='method',
|
||||
field=models.CharField(blank=True, max_length=50, null=True, unique=True, verbose_name='方法/代号'),
|
||||
),
|
||||
]
|
|
@ -14,7 +14,12 @@ class DictType(SoftModel):
|
|||
code = models.CharField('代号', unique=True, max_length=30)
|
||||
pid = models.ForeignKey('self', null=True, blank=True,
|
||||
on_delete=models.SET_NULL, verbose_name='父')
|
||||
class Meta:
|
||||
verbose_name = '字典类型'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Dict(SoftModel):
|
||||
"""
|
||||
|
@ -28,6 +33,12 @@ class Dict(SoftModel):
|
|||
pid = models.ForeignKey('self', null=True, blank=True,
|
||||
on_delete=models.SET_NULL, verbose_name='父')
|
||||
|
||||
class Meta:
|
||||
verbose_name = '字典'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Position(BaseModel):
|
||||
"""
|
||||
|
@ -60,7 +71,7 @@ class Permission(SoftModel):
|
|||
sort = models.IntegerField('排序标记', default=1)
|
||||
pid = models.ForeignKey('self', null=True, blank=True,
|
||||
on_delete=models.SET_NULL, verbose_name='父')
|
||||
method = models.CharField('方法/代号', max_length=50, null=True, blank=True)
|
||||
method = models.CharField('方法/代号', max_length=50, unique=True, null=True, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
|
|
@ -47,8 +47,9 @@ class RbacPermission(BasePermission):
|
|||
perms_map = view.perms_map
|
||||
_method = request._request.method.lower()
|
||||
if perms_map:
|
||||
for i in perms_map:
|
||||
if (i[_method] or i['*']) in perms:
|
||||
for key in perms_map:
|
||||
if key == _method or key == '*':
|
||||
if perms_map[key] in perms or perms_map[key] == '*':
|
||||
return True
|
||||
return False
|
||||
else:
|
||||
|
|
|
@ -16,7 +16,7 @@ from rest_framework.viewsets import GenericViewSet, ModelViewSet
|
|||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
from utils.queryset import get_child_queryset
|
||||
|
||||
from .models import Organization, Permission, Position, Role, User, Dict, DictType
|
||||
from .models import Organization, Permission, Position, Role, User, DictType, Dict
|
||||
from .permission import RbacPermission, get_permission_list
|
||||
from .serializers import (OrganizationSerializer, PermissionSerializer,
|
||||
PositionSerializer, RoleSerializer,
|
||||
|
@ -36,16 +36,11 @@ class LogoutView(APIView):
|
|||
def get(self, request, *args, **kwargs): # 可将token加入黑名单
|
||||
return Response(status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class DictTypeViewSet(ModelViewSet):
|
||||
"""
|
||||
数据字典类型:增删改查
|
||||
"""
|
||||
perms_map = [{'post': 'dicttype_create'},
|
||||
{'put': 'dicttype_update'},
|
||||
{'delete': 'dicttype_delete'},
|
||||
{'get': 'dicttype_view'},
|
||||
]
|
||||
perms_map = {'get': '*', 'post': 'dicttype_create', 'put': 'dicttype_update','delete': 'dicttype_delete'}
|
||||
queryset = DictType.objects.all()
|
||||
serializer_class = DictTypeSerializer
|
||||
pagination_class = None
|
||||
|
@ -58,28 +53,18 @@ class DictViewSet(ModelViewSet):
|
|||
"""
|
||||
数据字典:增删改查
|
||||
"""
|
||||
perms_map = [{'post': 'dict_create'},
|
||||
{'put': 'dict_update'},
|
||||
{'delete': 'dict_delete'},
|
||||
{'get': 'dict_view'},
|
||||
]
|
||||
perms_map = {'get': '*', 'post': 'dict_create', 'put': 'dict_update','delete': 'dict_delete'}
|
||||
queryset = Dict.objects.all()
|
||||
serializer_class = DictSerializer
|
||||
pagination_class = None
|
||||
search_fields = ['^name']
|
||||
ordering_fields = ['id']
|
||||
ordering = 'id'
|
||||
|
||||
|
||||
class PositionViewSet(ModelViewSet):
|
||||
"""
|
||||
岗位:增删改查
|
||||
"""
|
||||
perms_map = [{'post': 'position_create'},
|
||||
{'put': 'position_update'},
|
||||
{'delete': 'position_delete'},
|
||||
{'get': 'position_view'},
|
||||
]
|
||||
perms_map = {'get': '*', 'post': 'position_create', 'put': 'position_update','delete': 'position_delete'}
|
||||
queryset = Position.objects.all()
|
||||
serializer_class = PositionSerializer
|
||||
pagination_class = None
|
||||
|
@ -89,7 +74,7 @@ class PositionViewSet(ModelViewSet):
|
|||
|
||||
|
||||
class TestView(APIView):
|
||||
perms_map = [{'get': 'test_view'}] # 单个API控权
|
||||
perms_map = {'get':'test_view'} # 单个API控权
|
||||
pass
|
||||
|
||||
|
||||
|
@ -97,11 +82,8 @@ class PermissionViewSet(ModelViewSet):
|
|||
"""
|
||||
权限:增删改查
|
||||
"""
|
||||
perms_map = [{'post': 'perm_create'}, # 视图类控权
|
||||
{'put': 'perm_update'},
|
||||
{'delete': 'perm_delete'},
|
||||
{'get': 'perm_view'},
|
||||
]
|
||||
perms_map = {'get': '*', 'post': 'perm_create', 'put': 'perm_update','delete': 'perm_delete'}
|
||||
queryset = Position.objects.all()
|
||||
queryset = Permission.objects.all()
|
||||
serializer_class = PermissionSerializer
|
||||
pagination_class = None
|
||||
|
@ -114,15 +96,11 @@ class OrganizationViewSet(ModelViewSet):
|
|||
"""
|
||||
组织机构:增删改查
|
||||
"""
|
||||
perms_map = [{'post': 'org_create'},
|
||||
{'put': 'org_update'},
|
||||
{'delete': 'org_delete'},
|
||||
{'get': 'org_view'},
|
||||
]
|
||||
perms_map = {'get': '*', 'post': 'org_create', 'put': 'org_update','delete': 'org_delete'}
|
||||
queryset = Organization.objects.all()
|
||||
serializer_class = OrganizationSerializer
|
||||
pagination_class = None
|
||||
search_fields = ['^name']
|
||||
search_fields = ['^name','^method']
|
||||
ordering_fields = ['id']
|
||||
ordering = 'id'
|
||||
|
||||
|
@ -131,11 +109,7 @@ class RoleViewSet(ModelViewSet):
|
|||
"""
|
||||
角色:增删改查
|
||||
"""
|
||||
perms_map = [{'post': 'role_create'},
|
||||
{'put': 'role_update'},
|
||||
{'delete': 'role_delete'},
|
||||
{'get': 'role_view'},
|
||||
]
|
||||
perms_map = {'get': '*', 'post': 'role_create', 'put': 'role_update','delete': 'role_delete'}
|
||||
queryset = Role.objects.all()
|
||||
serializer_class = RoleSerializer
|
||||
pagination_class = None
|
||||
|
@ -148,11 +122,7 @@ class UserViewSet(ModelViewSet):
|
|||
"""
|
||||
用户管理:增删改查
|
||||
"""
|
||||
perms_map = [{'post': 'user_create'},
|
||||
{'put': 'user_update'},
|
||||
{'delete': 'user_delete'},
|
||||
{'get': 'user_view'},
|
||||
]
|
||||
perms_map = {'get': '*', 'post': 'user_create', 'put': 'user_update','delete': 'user_delete'}
|
||||
queryset = User.objects.all().order_by('-id')
|
||||
serializer_class = UserListSerializer
|
||||
filterset_class = UserFilter
|
||||
|
@ -180,17 +150,17 @@ class UserViewSet(ModelViewSet):
|
|||
# 创建用户默认添加密码
|
||||
password = request.data['password'] if 'password' in request.data else None
|
||||
if password:
|
||||
request.data['password'] = make_password(password)
|
||||
password = make_password(password)
|
||||
else:
|
||||
request.data['password'] = make_password('0000')
|
||||
password = make_password('0000')
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
self.perform_create(serializer)
|
||||
serializer.save(password = password)
|
||||
return Response(serializer.data)
|
||||
|
||||
@action(methods=['post'], detail=True, permission_classes=[IsAuthenticated],
|
||||
url_name='change_passwd')
|
||||
def change_passwd(self, request, pk=None):
|
||||
@action(methods=['put'], detail=True, permission_classes=[IsAuthenticated],
|
||||
url_name='change_password')
|
||||
def password(self, request, pk=None):
|
||||
"""
|
||||
修改密码
|
||||
"""
|
||||
|
@ -208,7 +178,7 @@ class UserViewSet(ModelViewSet):
|
|||
else:
|
||||
return Response('旧密码错误!', status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# perms_map=[{'get':'my_info'}], 自定义action控权
|
||||
# perms_map={'get':'*'}, 自定义action控权
|
||||
@action(methods=['get'], detail=False, url_name='my_info', permission_classes=[IsAuthenticated])
|
||||
def info(self, request, pk=None):
|
||||
"""
|
||||
|
|
|
@ -17,7 +17,7 @@ class SoftDeletableQuerySetMixin(object):
|
|||
field to True)
|
||||
'''
|
||||
if soft:
|
||||
self.update(is_delete=True)
|
||||
self.update(is_deleted=True)
|
||||
else:
|
||||
return super(SoftDeletableQuerySetMixin, self).delete()
|
||||
|
||||
|
@ -47,6 +47,7 @@ class SoftDeletableManagerMixin(object):
|
|||
class SoftDeletableManager(SoftDeletableManagerMixin, models.Manager):
|
||||
pass
|
||||
|
||||
|
||||
class BaseModel(models.Model):
|
||||
create_time = models.DateTimeField(
|
||||
default=timezone.now, verbose_name='创建时间', help_text='创建时间')
|
||||
|
@ -59,11 +60,11 @@ class BaseModel(models.Model):
|
|||
abstract = True
|
||||
|
||||
class SoftModel(BaseModel):
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
objects = SoftDeletableManager()
|
||||
|
||||
def delete(self, using=None, soft=True, *args, **kwargs):
|
||||
'''
|
||||
这里需要真删除的话soft=False即可
|
||||
|
|
Loading…
Reference in New Issue