Skip to content

Commit

Permalink
[CE-144] Support user management in vue
Browse files Browse the repository at this point in the history
In vue theme can manage users
Add user search api for check new user

Change-Id: I8d15dbdf3eea881fe25d222de215ca7003fac04b
Signed-off-by: Haitao Yue <hightall@me.com>
  • Loading branch information
hightall committed Sep 24, 2017
1 parent 081641f commit 9ea866c
Show file tree
Hide file tree
Showing 15 changed files with 631 additions and 22 deletions.
2 changes: 1 addition & 1 deletion src/modules/user/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# SPDX-License-Identifier: Apache-2.0
#
from .management import ListUser, CreateUser, UpdateUser, \
DeleteUser, UserInfo
DeleteUser, UserInfo, UserSearch
from .auth import Register, Login
from .user import User
from .profile import UserProfile
1 change: 1 addition & 0 deletions src/modules/user/management/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
from .update import UpdateUser
from .delete import DeleteUser
from .info import UserInfo
from .search import UserSearch
59 changes: 59 additions & 0 deletions src/modules/user/management/search.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@

# Copyright IBM Corp, All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#
from flask_restful import Resource, fields, marshal_with, reqparse
import logging
import sys
import os
from flask_login import login_required

sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
from common import log_handler, LOG_LEVEL
from modules.user.user import User

logger = logging.getLogger(__name__)
logger.setLevel(LOG_LEVEL)
logger.addHandler(log_handler)

user_search_fields = {
"username": fields.String,
"apikey": fields.String,
"isActivated": fields.Boolean,
"balance": fields.Integer,
"user_exists": fields.Boolean
}

user_search_parser = reqparse.RequestParser()
user_search_parser.add_argument('username',
location='args',
help='Username for create')


class UserSearch(Resource):
@login_required
@marshal_with(user_search_fields)
def get(self):
"""
search user with username
If user is existed return user info
else return user_exists False
:return:
"""
args = user_search_parser.parse_args()
username = args["username"]
user_obj = User()
user = user_obj.get_by_username(username)
if not user:
return {"user_exists": False}, 200

data = {
"username": user.username,
"apikey": str(user.id),
"isActivated": user.active,
"balance": user.balance,
"user_exists": True
}

return data, 200
3 changes: 2 additions & 1 deletion src/resources/user_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
from modules.user import ListUser, CreateUser, UpdateUser, \
DeleteUser, Register, Login, UserInfo, UserProfile
DeleteUser, Register, Login, UserInfo, UserProfile, UserSearch


bp_user_api = Blueprint('bp_user_api', __name__,
Expand Down Expand Up @@ -39,6 +39,7 @@ def logout():
user_api.add_resource(UpdateUser, '/update/<string:user_id>')
user_api.add_resource(DeleteUser, '/delete/<string:user_id>')
user_api.add_resource(UserInfo, '/account/<string:user_id>')
user_api.add_resource(UserSearch, '/search', endpoint="search")

front_user_api = Api(front_rest_user_v2)
front_user_api.add_resource(UserProfile, '/profile/<string:user_id>')
1 change: 1 addition & 0 deletions src/themes/vue/static/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"iview": "^2.3.2",
"axios": "^0.16.2",
"vue-echarts": "*",
"moment": "^2.18.1",
"vuex-router-sync": "^4.1.2"
},
"devDependencies": {
Expand Down
3 changes: 3 additions & 0 deletions src/themes/vue/static/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,7 @@ export default {
display:inline-block;
border-bottom: 2px solid #5b6270;
}
.main-content {
min-height: 100vh;
}
</style>
37 changes: 37 additions & 0 deletions src/themes/vue/static/src/api/user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@

/* Copyright IBM Corp, All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
import axios from 'axios'

export default {
getUsers (params, cb) {
axios.get('/api/user/list', {params: params}).then(res => {
cb(res.data.users)
})
},
searchUser (params, cb) {
axios.get('/api/user/search', {params: params}).then(res => {
cb(res.data)
})
},
createUser (formData, cb) {
axios.post('/api/user/create', formData).then(res => {
cb(res.data)
})
},
updateUser (params, cb) {
const updateUrl = '/api/user/update/' + params.id
const formData = params.formData
axios.put(updateUrl, formData).then(res => {
cb(res.data)
})
},
deleteUser (params, cb) {
const deleteUrl = '/api/user/delete/' + params.id
axios.delete(deleteUrl).then(res => {
cb(res.data)
})
}
}
40 changes: 40 additions & 0 deletions src/themes/vue/static/src/pages/UserManagement/Operation.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@

<!-- Copyright IBM Corp, All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
-->
<template>
<Dropdown @on-click="clickMenu">
<a href="javascript:void(0)">
Operations
<Icon type="arrow-down-b"></Icon>
</a>
<DropdownMenu slot="list">
<DropdownItem :disabled="user.name && user.name === 'admin'" name="edit">Edit</DropdownItem>
<DropdownItem :disabled="user.name && user.name === 'admin'" divided name="delete">Delete</DropdownItem>
</DropdownMenu>
</Dropdown>
</template>

<script>
export default {
props: ['user', 'showDeleteModal', 'showUserModal'],
methods: {
clickMenu (name) {
switch (name) {
case 'edit':
this.showUserModal('update', this.user)
break
case 'delete':
this.showDeleteModal(this.user)
break
default:
break
}
}
}
}
</script>

<style>
</style>
174 changes: 174 additions & 0 deletions src/themes/vue/static/src/pages/UserManagement/UserModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@

<!-- Copyright IBM Corp, All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
-->
<template>
<Modal
v-model="visible"
:title="title"
ok-text="OK"
cancel-text="Cancel">
<Form ref="userForm" :model="formItem" :rules="ruleValidate" :label-width="80">
<FormItem label="Name" prop="username">
<Input type="text" :readonly="type === 'update'" v-model="formItem.username" placeholder="Input username" />
</FormItem>
<FormItem label="Password" prop="password">
<Input type="text" v-model="formItem.password" placeholder="Input password" />
</FormItem>
<FormItem label="Role">
<Select v-model="formItem.role" placeholder="Select a role">
<Option v-for="item in roles" :value="item.value" :key="item.value">{{ item.label }}</Option>
</Select>
</FormItem>
<FormItem label="Balance">
<InputNumber :max="9999" :min="0" v-model="formItem.balance"></InputNumber>
</FormItem>
<FormItem label="Active">
<i-switch v-model="formItem.active" size="large">
<span v-for="item in activeChoices" :slot="item.value">{{ item.label }}</span>
</i-switch>
</FormItem>
</Form>
<div slot="footer">
<Button type="text" @click="hideModal">Cancel</Button>
<Button type="primary" :loading="submitting" @click="submitForm">Ok</Button>
</div>
</Modal>
</template>

<script>
import { mapGetters, mapActions } from 'vuex'
import user from '@/api/user'
export default {
props: ['type', 'visible', 'onCancel', 'formItem', 'onOk', 'user'],
data () {
const validateName = (rule, value, callback) => {
if (!value) {
callback(new Error('Please input username'))
}
if (this.type === 'create') {
user.searchUser({
username: value
}, result => {
if (result.user_exists) {
callback(new Error('User already exists'))
} else {
callback()
}
})
} else {
callback()
}
}
const validatePassword = (rule, value, callback) => {
if (this.type === 'create' && !value) {
callback(new Error('Please input password for new user'))
} else {
callback()
}
}
return {
roles: [
{
value: 0,
label: 'Admin'
},
{
value: 1,
label: 'Operator'
},
{
value: 2,
label: 'User'
}
],
activeChoices: [
{
value: true,
label: 'Yes'
},
{
value: false,
label: 'No'
}
],
ruleValidate: {
username: [
{ validator: validateName, trigger: 'blur' }
],
password: [
{ validator: validatePassword, trigger: 'blur' }
]
},
submitting: false
}
},
computed: {
...mapGetters([
'currentUser',
'userModal'
]),
title () {
if (this.type === 'create') {
return 'Create User'
} else {
return 'Edit User'
}
}
},
methods: {
...mapActions([
'getUsers'
]),
submitForm () {
this.$refs['userForm'].validate((valid) => {
this.submitting = true
let formData = new FormData()
if (valid) {
for (const key in this.formItem) {
formData.append(key, this.formItem[key])
}
if (this.type === 'create') {
user.createUser(formData, result => {
if (result.status === 'OK') {
this.$Message.success('Create user success')
this.hideModal()
this.submitting = false
this.getUsers({})
} else {
this.$Message.error('Create user failed')
this.submitting = false
}
})
} else {
user.updateUser({
id: this.user.id,
formData
}, result => {
if (result.status === 'OK') {
this.$Message.success('Update user success')
this.hideModal()
this.submitting = false
this.getUsers({})
} else {
this.$Message.error('Update user failed')
this.submitting = false
}
})
}
} else {
this.submitting = false
}
})
},
hideModal () {
this.$refs['userForm'].resetFields()
this.onCancel()
}
}
}
</script>

<style>
</style>
Loading

0 comments on commit 9ea866c

Please sign in to comment.