feat: 用户管理完善

This commit is contained in:
fit2cloud-chenyw 2021-03-03 17:38:41 +08:00
parent ccecdc8c68
commit 60d0146518
24 changed files with 335 additions and 279 deletions

View File

@ -6,6 +6,7 @@ import com.github.pagehelper.PageHelper;
import io.dataease.commons.utils.PageUtils;
import io.dataease.commons.utils.Pager;
import io.dataease.controller.sys.request.SysUserCreateRequest;
import io.dataease.controller.sys.request.SysUserStateRequest;
import io.dataease.controller.sys.request.UserGridRequest;
import io.dataease.controller.sys.response.SysUserGridResponse;
import io.dataease.service.sys.SysUserService;
@ -47,4 +48,11 @@ public class SysUserController {
public void delete(@PathVariable("userId") Long userId){
sysUserService.delete(userId);
}
@ApiOperation("更新用户状态")
@PostMapping("/updateStatus")
public void updateStatus(@RequestBody SysUserStateRequest request){
sysUserService.updateStatus(request);
}
}

View File

@ -0,0 +1,13 @@
package io.dataease.controller.sys.request;
import lombok.Data;
import java.io.Serializable;
@Data
public class SysUserStateRequest implements Serializable {
private Long userId;
private Long enabled;
}

View File

@ -10,6 +10,7 @@ import io.dataease.base.mapper.ext.ExtSysUserMapper;
import io.dataease.commons.utils.BeanUtils;
import io.dataease.commons.utils.CodingUtil;
import io.dataease.controller.sys.request.SysUserCreateRequest;
import io.dataease.controller.sys.request.SysUserStateRequest;
import io.dataease.controller.sys.request.UserGridRequest;
import io.dataease.controller.sys.response.SysUserGridResponse;
import io.dataease.controller.sys.response.SysUserRole;
@ -74,6 +75,14 @@ public class SysUserService {
return sysUserMapper.updateByPrimaryKey(user);
}
public int updateStatus(SysUserStateRequest request){
SysUser sysUser = new SysUser();
sysUser.setUserId(request.getUserId());
sysUser.setEnabled(request.getEnabled());
return sysUserMapper.updateByPrimaryKeySelective(sysUser);
}
/**
* 删除用户角色关联
* @param userId

View File

@ -1,9 +1,57 @@
import request from '@/utils/request'
const pathMap = {
queryPath: '/api/user/userGrid/',
deletePath: '/api/user/delete/',
createPath: '/api/user/create',
updatePath: '/api/user/update',
editPasswordPath: '/api/user/password',
editStatusPath: '/api/user/updateStatus'
}
export function userLists(page, size, data) {
return request({
url: 'api/user/userGrid/' + page + '/' + size,
url: pathMap.queryPath + page + '/' + size,
method: 'post',
data
})
}
export const addUser = (data) => {
return request({
url: pathMap.createPath,
method: 'post',
data
})
}
export const editUser = (data) => {
return request({
url: pathMap.updatePath,
method: 'post',
data
})
}
export const delUser = (userId) => {
return request({
url: pathMap.deletePath + userId,
method: 'post'
})
}
export const editPassword = (data) => {
return request({
url: pathMap.editPasswordPath,
method: 'post',
data
})
}
export const editStatus = (data) => {
return request({
url: pathMap.editStatusPath,
method: 'post',
data
})
}
export default { editPassword, delUser, editUser, addUser, userLists, editStatus }

View File

@ -1,9 +1,29 @@
import request from '@/utils/request'
import Mock from 'mockjs'
const data = Mock.mock({
'items|30': [{
id: '@id',
title: '@sentence(10, 20)',
'status|1': ['published', 'draft', 'deleted'],
author: 'name',
display_time: '@datetime',
pageviews: '@integer(300, 5000)'
}]
})
export function getList(params) {
return request({
url: '/vue-admin-template/table/list',
method: 'get',
params
// return request({
// url: '/vue-admin-template/table/list',
// method: 'get',
// params
// })
return new Promise((resolve, reject) => {
const items = data.items
const result = {
code: 20000,
data: {
total: items.length,
items: items
}
}
resolve(result)
})
}

View File

@ -1,36 +1,39 @@
<template>
<el-aside :width="width" class="ms-aside-container"
:style="{'margin-left': !asideHidden ? 0 : '-' + width}">
<el-aside
:width="width"
class="ms-aside-container"
:style="{'margin-left': !asideHidden ? 0 : '-' + width}"
>
<!--<div v-if="enableAsideHidden" class="hiddenBottom" @click="asideHidden = !asideHidden">-->
<!--<i v-if="!asideHidden" class="el-icon-arrow-left"/>-->
<!--<i v-if="asideHidden" class="el-icon-arrow-right"/>-->
<!--<i v-if="!asideHidden" class="el-icon-arrow-left"/>-->
<!--<i v-if="asideHidden" class="el-icon-arrow-right"/>-->
<!--</div>-->
<slot></slot>
<ms-horizontal-drag-bar/>
<slot />
<ms-horizontal-drag-bar />
</el-aside>
</template>
<script>
import MsHorizontalDragBar from "./dragbar/MsLeft2RightDragBar";
export default {
name: "MsAsideContainer",
components: {MsHorizontalDragBar},
props: {
width: {
type: String,
default: '300px'
},
enableAsideHidden: {
type: Boolean,
default: true
},
},
data() {
return {
asideHidden: false
}
}
import MsHorizontalDragBar from './dragbar/MsLeft2RightDragBar'
export default {
name: 'MsAsideContainer',
components: { MsHorizontalDragBar },
props: {
width: {
type: String,
default: '300px'
},
enableAsideHidden: {
type: Boolean,
default: true
}
},
data() {
return {
asideHidden: false
}
}
}
</script>
<style scoped>

View File

@ -1,15 +1,15 @@
<template>
<el-container class="ms-container">
<slot></slot>
<slot />
</el-container>
</template>
<script>
export default {
name: "MsContainer"
}
export default {
name: 'MsContainer'
}
</script>
<style scoped>

View File

@ -1,75 +1,77 @@
<template>
<el-dialog :close-on-click-modal="false"
:title="title"
:visible.sync="dialogVisible"
class="delete-confirm" >
<el-dialog
:close-on-click-modal="false"
:title="title"
:visible.sync="dialogVisible"
class="delete-confirm"
>
<el-row>
<el-col>
<span>{{$t('commons.delete_confirm')}}</span>
<span class="delete-tip"> DELETE-{{record.name || record.title}}</span>
<br/>
<span>{{ $t('commons.delete_confirm') }}</span>
<span class="delete-tip"> DELETE-{{ record.name || record.title }}</span>
<br>
</el-col>
</el-row>
<el-row class="tip" v-if="withTip">
<el-row v-if="withTip" class="tip">
<span>
<slot class="tip"></slot>
<slot class="tip" />
</span>
</el-row>
<el-row>
<el-col :span="15">
<el-input v-model="value" :placeholder="$t('commons.input_content')"/>
<el-input v-model="value" :placeholder="$t('commons.input_content')" />
</el-col>
</el-row>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">{{$t('commons.cancel')}}</el-button>
<el-button type="primary" @click="confirm">{{$t('commons.confirm')}}</el-button>
<el-button @click="dialogVisible = false">{{ $t('commons.cancel') }}</el-button>
<el-button type="primary" @click="confirm">{{ $t('commons.confirm') }}</el-button>
</span>
</el-dialog>
</template>
<script>
export default {
name: "MsDeleteConfirm",
data() {
return {
dialogVisible: false,
value: '',
record: {},
}
},
props: {
title: {
type: String,
default() {
return this.$t('commons.title')
}
},
withTip: {
type: Boolean,
default() {
return false
}
}
},
methods: {
open(record) {
this.dialogVisible = true;
this.value = '';
this.record = record;
},
confirm() {
if (this.value.trim() != 'DELETE-' + (this.record.name || this.record.title) ) {
this.$warning(this.$t('commons.incorrect_input'));
return;
}
this.$emit('delete', this.record);
this.dialogVisible = false;
}
export default {
name: 'MsDeleteConfirm',
props: {
title: {
type: String,
default() {
return this.$t('commons.title')
}
},
withTip: {
type: Boolean,
default() {
return false
}
}
},
data() {
return {
dialogVisible: false,
value: '',
record: {}
}
},
methods: {
open(record) {
this.dialogVisible = true
this.value = ''
this.record = record
},
confirm() {
if (this.value.trim() !== 'DELETE-' + (this.record.name || this.record.title)) {
this.$warning(this.$t('commons.incorrect_input'))
return
}
this.$emit('delete', this.record)
this.dialogVisible = false
}
}
}
</script>
<style scoped>

View File

@ -1,47 +1,47 @@
<template>
<div class="dialog-footer">
<el-button @click="cancel">{{$t('commons.cancel')}}</el-button>
<el-button type="warning" v-if="isShowValidate" @click="validate" @keydown.enter.native.prevent>{{$t('commons.validate')}}</el-button>
<el-button type="primary" :disabled="disabled" @click="confirm" @keydown.enter.native.prevent>{{$t('commons.confirm')}}</el-button>
<el-button type="primary" v-if="isShow" @click="saveAsEdit" @keydown.enter.native.prevent>{{title}}</el-button>
<el-button @click="cancel">{{ $t('commons.cancel') }}</el-button>
<el-button v-if="isShowValidate" type="warning" @click="validate" @keydown.enter.native.prevent>{{ $t('commons.validate') }}</el-button>
<el-button type="primary" :disabled="disabled" @click="confirm" @keydown.enter.native.prevent>{{ $t('commons.confirm') }}</el-button>
<el-button v-if="isShow" type="primary" @click="saveAsEdit" @keydown.enter.native.prevent>{{ title }}</el-button>
</div>
</template>
<script>
export default {
name: "MsDialogFooter",
props: {
isShow: {
type: Boolean,
default: false,
},
isShowValidate: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
title:String,
export default {
name: 'MsDialogFooter',
props: {
isShow: {
type: Boolean,
default: false
},
methods: {
cancel() {
this.$emit("cancel");
},
validate() {
this.$emit("validate");
},
confirm() {
this.$emit("confirm");
},
saveAsEdit() {
this.$emit("saveAsEdit");
}
isShowValidate: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
title: String
},
methods: {
cancel() {
this.$emit('cancel')
},
validate() {
this.$emit('validate')
},
confirm() {
this.$emit('confirm')
},
saveAsEdit() {
this.$emit('saveAsEdit')
}
}
}
</script>
<style scoped>

View File

@ -1,13 +1,13 @@
<template>
<el-main class="ms-main-container">
<slot></slot>
<slot />
</el-main>
</template>
<script>
export default {
name: "MsMainContainer"
}
export default {
name: 'MsMainContainer'
}
</script>
<style scoped>

View File

@ -12,13 +12,11 @@
</template>
<script>
import MsTableButton from './MsTableButton'
import MsTipButton from './MsTipButton'
import { checkoutTestManagerOrTestUser, hasRoles } from '@/metersphere/common/js/utils'
import { ROLE_TEST_MANAGER, ROLE_TEST_USER } from '@/metersphere/common/js/constants'
import { checkoutTestManagerOrTestUser } from '@/metersphere/common/js/utils'
export default {
name: 'MsTableOperatorButton',
components: { MsTipButton, MsTableButton },
components: { MsTipButton },
props: {
icon: {
type: String,
@ -29,7 +27,8 @@ export default {
default: 'primary'
},
tip: {
type: String
type: String,
default: ''
},
disabled: {
type: Boolean,

View File

@ -19,10 +19,11 @@ export default {
name: 'MsTableSearchBar',
props: {
condition: {
type: Object
type: Object,
default: null
},
tip: {
String,
type: String,
default() {
return this.$t('commons.search_by_name')
}

View File

@ -1,3 +1,3 @@
@import './main.css';
@import './menu-header.css';
@import '../theme/index.css';
/* @import './menu-header.css';
@import '../theme/index.css'; */

View File

@ -1,48 +1,46 @@
import {Message} from 'element-ui';
import { Message } from 'element-ui'
export default {
install(Vue) {
if (!Message) {
window.console.error('You have to install Message of ElementUI');
window.console.error('You have to install Message of ElementUI')
return
}
Vue.prototype.$success = function (message) {
Vue.prototype.$success = function(message) {
Message.success({
message: message,
type: "success",
type: 'success',
showClose: true,
duration: 1500
})
};
}
Vue.prototype.$info = function (message, duration) {
Vue.prototype.$info = function(message, duration) {
Message.info({
message: message,
type: "info",
type: 'info',
showClose: true,
duration: duration || 3000
})
};
}
Vue.prototype.$warning = function (message) {
Vue.prototype.$warning = function(message) {
Message.warning({
message: message,
type: "warning",
type: 'warning',
showClose: true,
duration: 5000
})
};
}
Vue.prototype.$error = function (message, duration) {
Vue.prototype.$error = function(message, duration) {
Message.error({
message: message,
type: "error",
type: 'error',
showClose: true,
duration: duration || 10000
})
};
}
}
}

View File

@ -64,62 +64,30 @@ export const constantRoutes = [
component: () => import('@/views/dashboard/index'),
meta: { title: '首页', icon: 'dashboard' }
}]
},
{
path: '/example',
component: Layout,
redirect: '/example/table',
name: 'Example',
meta: { title: 'Example', icon: 'example' },
children: [
{
path: 'table',
name: 'Table',
component: () => import('@/views/table/index'),
meta: { title: 'Table', icon: 'table' }
},
{
path: 'tree',
name: 'Tree',
component: () => import('@/views/tree/index'),
meta: { title: 'Tree', icon: 'tree' }
}
]
}
// {
// path: '/system',
// component: Layout,
// redirect: '/system/user',
// name: '系统管理',
// meta: {
// title: '系统管理',
// icon: 'system'
// },
// children: [
// {
// path: 'user',
// name: '用户管理',
// meta: {
// title: ' 用户管理',
// icon: 'peoples'
// }
// },
// {
// path: 'menu',
// name: '菜单管理',
// meta: {
// title: ' 菜单管理',
// icon: 'menu'
// }
// }
// ]
// },
// {
// path: '/example',
// component: Layout,
// redirect: '/example/table',
// name: 'Example',
// meta: { title: 'Example', icon: 'example' },
// children: [
// {
// path: 'table',
// name: 'Table',
// component: () => import('@/views/table/index'),
// meta: { title: 'Table', icon: 'table' }
// },
// {
// path: 'tree',
// name: 'Tree',
// component: () => import('@/views/tree/index'),
// meta: { title: 'Tree', icon: 'tree' }
// }
// ]
// },
// {
// path: '/form',
// component: Layout,

View File

@ -5,7 +5,7 @@
@import './sidebar.scss';
@import './topbar.scss';
@import '../metersphere/common/css/index.css';
// @import '../metersphere/common/css/index.css';
body {

View File

@ -1,59 +1,60 @@
<template>
<el-col>
<span>{{table.name}}</span>
<el-table
size="mini"
:data="data"
height="40vh"
border
style="width: 100%;margin-top: 6px;">
<el-table-column
min-width="200px"
v-for="field in fields"
:key="field.originName"
:prop="field.originName"
:label="field.name">
</el-table-column>
</el-table>
</el-col>
<el-col>
<span>{{ table.name }}</span>
<el-table
size="mini"
:data="data"
height="40vh"
border
style="width: 100%;margin-top: 6px;"
>
<el-table-column
v-for="field in fields"
:key="field.originName"
min-width="200px"
:prop="field.originName"
:label="field.name"
/>
</el-table>
</el-col>
</template>
<script>
export default {
name: 'DatasetTableData',
props: {
table: Object
},
name: "DatasetTableData",
data() {
return {
fields: [],
data: []
}
},
watch: {
table() {
this.initData()
}
},
created() {
this.initData();
this.initData()
},
mounted() {
},
methods: {
initData() {
this.resetData();
this.resetData()
if (this.table.id) {
this.$post('/dataset/table/getPreviewData', this.table, response => {
this.fields = response.data.fields;
this.data = response.data.data;
});
this.fields = response.data.fields
this.data = response.data.data
})
}
},
resetData() {
this.fields = [];
this.data = [];
}
},
watch: {
table() {
this.initData();
this.fields = []
this.data = []
}
}
}

View File

@ -36,7 +36,7 @@
<div class="block">
<el-tree
:default-expanded-keys="expandedArray"
:data="data"
:data="tData"
node-key="id"
:expand-on-click-node="true"
@node-click="nodeClick"
@ -240,7 +240,7 @@ export default {
search: '',
editGroup: false,
editTable: false,
data: [],
tData: [],
tableData: [],
currGroup: {},
expandedArray: [],
@ -462,7 +462,7 @@ export default {
tree(group) {
groupTree(group).then(res => {
this.data = res.data.data
this.tData = res.data.data
})
},

View File

@ -159,7 +159,6 @@
</template>
<script>
import MsCreateBox from '../CreateBox'
import MsTablePagination from '@/metersphere/common/pagination/TablePagination'
import MsTableHeader from '@/metersphere/common/components/MsTableHeader'
import MsTableOperator from '@/metersphere/common/components/MsTableOperator'
@ -176,7 +175,6 @@ export default {
name: 'DEDatasource',
components: {
MsDeleteConfirm,
MsCreateBox,
MsTablePagination,
MsTableHeader,
MsTableOperator,
@ -356,7 +354,7 @@ export default {
},
changeType() {
for (let i = 0; i < this.allTypes.length; i++) {
if (this.allTypes[i].name == this.form.type) {
if (this.allTypes[i].name === this.form.type) {
this.form.configuration.dataSourceType = this.allTypes[i].type
}
}
@ -388,7 +386,7 @@ export default {
</script>
<style scoped>
@import "~@/metersphere/common/css/index.css";
.member-size {
text-decoration: underline;
}

View File

@ -395,7 +395,7 @@ export default {
</script>
<style scoped>
@import "~@/metersphere/common/css/index.css";
.member-size {
text-decoration: underline;
}

View File

@ -262,7 +262,10 @@ export default {
treeByArr(arr) {
if (!Array.isArray(arr) || !arr.length) return
const map = {}
arr.forEach(item => map[item.id] = item)
arr.forEach(item => {
map[item.id] = item
})
const roots = []
arr.forEach(item => {
@ -289,7 +292,7 @@ export default {
return obj
})
if (!row) {
data.some(node => node.children = null)
// data.some(node => node.children = null)
_self.tableData = data
_self.menus = []
_self.menus.push(_self.topMunu)
@ -404,7 +407,7 @@ export default {
</script>
<style scoped>
@import "~@/metersphere/common/css/index.css";
.member-size {
text-decoration: underline;
}

View File

@ -258,5 +258,5 @@ export default {
</script>
<style scoped>
@import "~@/metersphere/common/css/index.css";
</style>

View File

@ -17,9 +17,7 @@
<script>
import EmailSetting from './EmailSetting'
import LdapSetting from './LdapSetting'
import UiSetting from './UiSetting'
import BaseSetting from './BaseSetting'
import { hasLicense } from '@/metersphere/common/js/utils'
// const requireComponent = require.context('@/metersphere/common/components/xpack/', true, /\.vue$/)
@ -29,10 +27,8 @@ import { hasLicense } from '@/metersphere/common/js/utils'
export default {
name: 'SystemParameterSetting',
components: {
BaseSetting,
UiSetting,
EmailSetting,
LdapSetting,
EmailSetting
// 'MsDisplay': display.default,
// 'MsAuth': auth.default
},

View File

@ -26,7 +26,7 @@
</el-table-column>
<el-table-column prop="status" :label="$t('commons.status')" width="120">
<template v-slot:default="scope">
<el-switch v-model="scope.row.enabled" inactive-color="#DCDFE6" @change="changeSwitch(scope.row)" />
<el-switch v-model="scope.row.enabled" :active-value="1" :inactive-value="0" inactive-color="#DCDFE6" @change="changeSwitch(scope.row)" />
</template>
</el-table-column>
<el-table-column prop="createTime" :label="$t('commons.create_time')">
@ -88,8 +88,8 @@
</el-form-item>
<el-form-item label="状态">
<el-radio-group v-model="form.enabled" style="width: 140px">
<el-radio label="1">启用</el-radio>
<el-radio label="0">停用</el-radio>
<el-radio :label="1">启用</el-radio>
<el-radio :label="0">停用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="部门" prop="dept">
@ -176,9 +176,9 @@ import { PHONE_REGEX } from '@/metersphere/common/js/regex'
import { LOAD_CHILDREN_OPTIONS, LOAD_ROOT_OPTIONS } from '@riophae/vue-treeselect'
import Treeselect from '@riophae/vue-treeselect'
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
import { userLists } from '@/api/system/user'
import { userLists, addUser, editUser, delUser, editPassword, editStatus } from '@/api/system/user'
import { allRoles } from '@/api/system/role'
import { getMenusTree } from '@/api/system/menu'
import { getDeptTree } from '@/api/system/dept'
export default {
name: 'MsUser',
components: {
@ -268,7 +268,7 @@ export default {
}
]
},
defaultForm: { id: null, username: null, nickName: null, gender: '男', email: null, enabled: '1', deptId: null, phone: null },
defaultForm: { id: null, username: null, nickName: null, gender: '男', email: null, enabled: 1, deptId: null, phone: null },
depts: null,
roles: [],
roleDatas: [],
@ -307,7 +307,7 @@ export default {
cancelButtonText: this.$t('commons.cancel'),
type: 'warning'
}).then(() => {
this.result = this.$get(this.deletePath + encodeURIComponent(row.userId), () => {
delUser(encodeURIComponent(row.userId)).then(res => {
this.$success(this.$t('commons.delete_success'))
this.search()
})
@ -318,8 +318,8 @@ export default {
createUser(createUserForm) {
this.$refs[createUserForm].validate(valid => {
if (valid) {
const url = this.formType === 'add' ? this.createPat : this.updatePath
this.result = this.$post(url, this.form, () => {
const method = this.formType === 'add' ? addUser : editUser
method(this.form).then(res => {
this.$success(this.$t('commons.save_success'))
this.search()
this.dialogVisible = false
@ -329,23 +329,11 @@ export default {
}
})
},
updateUser(updateUserForm) {
this.$refs[updateUserForm].validate(valid => {
if (valid) {
this.result = this.$post(this.updatePath, this.form, () => {
this.$success(this.$t('commons.modify_success'))
this.dialogVisible = false
this.search()
})
} else {
return false
}
})
},
editUserPassword(editPasswordForm) {
this.$refs[editPasswordForm].validate(valid => {
if (valid) {
this.result = this.$post(this.editPasswordPath, this.ruleForm, () => {
editPassword(this.ruleForm).then(res => {
this.$success(this.$t('commons.modify_success'))
this.editPasswordVisible = false
this.search()
@ -377,13 +365,13 @@ export default {
this.dialogVisible = false
},
changeSwitch(row) {
this.$post('/api/user/update_status', row, () => {
const { userId, enabled } = row
const param = { userId: userId, enabled: enabled }
editStatus(param).then(res => {
this.$success(this.$t('commons.modify_success'))
})
},
buildPagePath(path) {
return path + '/' + this.currentPage + '/' + this.pageSize
},
handleSelectionChange(val) {
this.multipleSelection = val
},
@ -391,7 +379,7 @@ export default {
loadDepts({ action, parentNode, callback }) {
if (action === LOAD_ROOT_OPTIONS) {
const _self = this
!this.depts && getMenusTree('0').then(res => {
!this.depts && getDeptTree('0').then(res => {
_self.depts = res.data.data.map(node => _self.normalizer(node))
callback()
})
@ -399,7 +387,7 @@ export default {
if (action === LOAD_CHILDREN_OPTIONS) {
const _self = this
getMenusTree(parentNode.id).then(res => {
getDeptTree(parentNode.id).then(res => {
parentNode.children = res.data.data.map(function(obj) {
return _self.normalizer(obj)
})
@ -442,4 +430,5 @@ export default {
</script>
<style scoped>
@import "~@/metersphere/common/css/index.css";
</style>