Skip to content

Commit 9ea866c

Browse files
committed
[CE-144] Support user management in vue
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>
1 parent 081641f commit 9ea866c

File tree

15 files changed

+631
-22
lines changed

15 files changed

+631
-22
lines changed

src/modules/user/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# SPDX-License-Identifier: Apache-2.0
55
#
66
from .management import ListUser, CreateUser, UpdateUser, \
7-
DeleteUser, UserInfo
7+
DeleteUser, UserInfo, UserSearch
88
from .auth import Register, Login
99
from .user import User
1010
from .profile import UserProfile

src/modules/user/management/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@
88
from .update import UpdateUser
99
from .delete import DeleteUser
1010
from .info import UserInfo
11+
from .search import UserSearch

src/modules/user/management/search.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
2+
# Copyright IBM Corp, All Rights Reserved.
3+
#
4+
# SPDX-License-Identifier: Apache-2.0
5+
#
6+
from flask_restful import Resource, fields, marshal_with, reqparse
7+
import logging
8+
import sys
9+
import os
10+
from flask_login import login_required
11+
12+
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
13+
from common import log_handler, LOG_LEVEL
14+
from modules.user.user import User
15+
16+
logger = logging.getLogger(__name__)
17+
logger.setLevel(LOG_LEVEL)
18+
logger.addHandler(log_handler)
19+
20+
user_search_fields = {
21+
"username": fields.String,
22+
"apikey": fields.String,
23+
"isActivated": fields.Boolean,
24+
"balance": fields.Integer,
25+
"user_exists": fields.Boolean
26+
}
27+
28+
user_search_parser = reqparse.RequestParser()
29+
user_search_parser.add_argument('username',
30+
location='args',
31+
help='Username for create')
32+
33+
34+
class UserSearch(Resource):
35+
@login_required
36+
@marshal_with(user_search_fields)
37+
def get(self):
38+
"""
39+
search user with username
40+
If user is existed return user info
41+
else return user_exists False
42+
:return:
43+
"""
44+
args = user_search_parser.parse_args()
45+
username = args["username"]
46+
user_obj = User()
47+
user = user_obj.get_by_username(username)
48+
if not user:
49+
return {"user_exists": False}, 200
50+
51+
data = {
52+
"username": user.username,
53+
"apikey": str(user.id),
54+
"isActivated": user.active,
55+
"balance": user.balance,
56+
"user_exists": True
57+
}
58+
59+
return data, 200

src/resources/user_api.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

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

1616

1717
bp_user_api = Blueprint('bp_user_api', __name__,
@@ -39,6 +39,7 @@ def logout():
3939
user_api.add_resource(UpdateUser, '/update/<string:user_id>')
4040
user_api.add_resource(DeleteUser, '/delete/<string:user_id>')
4141
user_api.add_resource(UserInfo, '/account/<string:user_id>')
42+
user_api.add_resource(UserSearch, '/search', endpoint="search")
4243

4344
front_user_api = Api(front_rest_user_v2)
4445
front_user_api.add_resource(UserProfile, '/profile/<string:user_id>')

src/themes/vue/static/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"iview": "^2.3.2",
2121
"axios": "^0.16.2",
2222
"vue-echarts": "*",
23+
"moment": "^2.18.1",
2324
"vuex-router-sync": "^4.1.2"
2425
},
2526
"devDependencies": {

src/themes/vue/static/src/App.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,7 @@ export default {
130130
display:inline-block;
131131
border-bottom: 2px solid #5b6270;
132132
}
133+
.main-content {
134+
min-height: 100vh;
135+
}
133136
</style>

src/themes/vue/static/src/api/user.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
2+
/* Copyright IBM Corp, All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
import axios from 'axios'
7+
8+
export default {
9+
getUsers (params, cb) {
10+
axios.get('/api/user/list', {params: params}).then(res => {
11+
cb(res.data.users)
12+
})
13+
},
14+
searchUser (params, cb) {
15+
axios.get('/api/user/search', {params: params}).then(res => {
16+
cb(res.data)
17+
})
18+
},
19+
createUser (formData, cb) {
20+
axios.post('/api/user/create', formData).then(res => {
21+
cb(res.data)
22+
})
23+
},
24+
updateUser (params, cb) {
25+
const updateUrl = '/api/user/update/' + params.id
26+
const formData = params.formData
27+
axios.put(updateUrl, formData).then(res => {
28+
cb(res.data)
29+
})
30+
},
31+
deleteUser (params, cb) {
32+
const deleteUrl = '/api/user/delete/' + params.id
33+
axios.delete(deleteUrl).then(res => {
34+
cb(res.data)
35+
})
36+
}
37+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
2+
<!-- Copyright IBM Corp, All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
-->
6+
<template>
7+
<Dropdown @on-click="clickMenu">
8+
<a href="javascript:void(0)">
9+
Operations
10+
<Icon type="arrow-down-b"></Icon>
11+
</a>
12+
<DropdownMenu slot="list">
13+
<DropdownItem :disabled="user.name && user.name === 'admin'" name="edit">Edit</DropdownItem>
14+
<DropdownItem :disabled="user.name && user.name === 'admin'" divided name="delete">Delete</DropdownItem>
15+
</DropdownMenu>
16+
</Dropdown>
17+
</template>
18+
19+
<script>
20+
export default {
21+
props: ['user', 'showDeleteModal', 'showUserModal'],
22+
methods: {
23+
clickMenu (name) {
24+
switch (name) {
25+
case 'edit':
26+
this.showUserModal('update', this.user)
27+
break
28+
case 'delete':
29+
this.showDeleteModal(this.user)
30+
break
31+
default:
32+
break
33+
}
34+
}
35+
}
36+
}
37+
</script>
38+
39+
<style>
40+
</style>
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
2+
<!-- Copyright IBM Corp, All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
-->
6+
<template>
7+
<Modal
8+
v-model="visible"
9+
:title="title"
10+
ok-text="OK"
11+
cancel-text="Cancel">
12+
<Form ref="userForm" :model="formItem" :rules="ruleValidate" :label-width="80">
13+
<FormItem label="Name" prop="username">
14+
<Input type="text" :readonly="type === 'update'" v-model="formItem.username" placeholder="Input username" />
15+
</FormItem>
16+
<FormItem label="Password" prop="password">
17+
<Input type="text" v-model="formItem.password" placeholder="Input password" />
18+
</FormItem>
19+
<FormItem label="Role">
20+
<Select v-model="formItem.role" placeholder="Select a role">
21+
<Option v-for="item in roles" :value="item.value" :key="item.value">{{ item.label }}</Option>
22+
</Select>
23+
</FormItem>
24+
<FormItem label="Balance">
25+
<InputNumber :max="9999" :min="0" v-model="formItem.balance"></InputNumber>
26+
</FormItem>
27+
<FormItem label="Active">
28+
<i-switch v-model="formItem.active" size="large">
29+
<span v-for="item in activeChoices" :slot="item.value">{{ item.label }}</span>
30+
</i-switch>
31+
</FormItem>
32+
</Form>
33+
<div slot="footer">
34+
<Button type="text" @click="hideModal">Cancel</Button>
35+
<Button type="primary" :loading="submitting" @click="submitForm">Ok</Button>
36+
</div>
37+
</Modal>
38+
</template>
39+
40+
<script>
41+
import { mapGetters, mapActions } from 'vuex'
42+
import user from '@/api/user'
43+
export default {
44+
props: ['type', 'visible', 'onCancel', 'formItem', 'onOk', 'user'],
45+
data () {
46+
const validateName = (rule, value, callback) => {
47+
if (!value) {
48+
callback(new Error('Please input username'))
49+
}
50+
if (this.type === 'create') {
51+
user.searchUser({
52+
username: value
53+
}, result => {
54+
if (result.user_exists) {
55+
callback(new Error('User already exists'))
56+
} else {
57+
callback()
58+
}
59+
})
60+
} else {
61+
callback()
62+
}
63+
}
64+
const validatePassword = (rule, value, callback) => {
65+
if (this.type === 'create' && !value) {
66+
callback(new Error('Please input password for new user'))
67+
} else {
68+
callback()
69+
}
70+
}
71+
return {
72+
roles: [
73+
{
74+
value: 0,
75+
label: 'Admin'
76+
},
77+
{
78+
value: 1,
79+
label: 'Operator'
80+
},
81+
{
82+
value: 2,
83+
label: 'User'
84+
}
85+
],
86+
activeChoices: [
87+
{
88+
value: true,
89+
label: 'Yes'
90+
},
91+
{
92+
value: false,
93+
label: 'No'
94+
}
95+
],
96+
ruleValidate: {
97+
username: [
98+
{ validator: validateName, trigger: 'blur' }
99+
],
100+
password: [
101+
{ validator: validatePassword, trigger: 'blur' }
102+
]
103+
},
104+
submitting: false
105+
}
106+
},
107+
computed: {
108+
...mapGetters([
109+
'currentUser',
110+
'userModal'
111+
]),
112+
title () {
113+
if (this.type === 'create') {
114+
return 'Create User'
115+
} else {
116+
return 'Edit User'
117+
}
118+
}
119+
},
120+
methods: {
121+
...mapActions([
122+
'getUsers'
123+
]),
124+
submitForm () {
125+
this.$refs['userForm'].validate((valid) => {
126+
this.submitting = true
127+
let formData = new FormData()
128+
if (valid) {
129+
for (const key in this.formItem) {
130+
formData.append(key, this.formItem[key])
131+
}
132+
if (this.type === 'create') {
133+
user.createUser(formData, result => {
134+
if (result.status === 'OK') {
135+
this.$Message.success('Create user success')
136+
this.hideModal()
137+
this.submitting = false
138+
this.getUsers({})
139+
} else {
140+
this.$Message.error('Create user failed')
141+
this.submitting = false
142+
}
143+
})
144+
} else {
145+
user.updateUser({
146+
id: this.user.id,
147+
formData
148+
}, result => {
149+
if (result.status === 'OK') {
150+
this.$Message.success('Update user success')
151+
this.hideModal()
152+
this.submitting = false
153+
this.getUsers({})
154+
} else {
155+
this.$Message.error('Update user failed')
156+
this.submitting = false
157+
}
158+
})
159+
}
160+
} else {
161+
this.submitting = false
162+
}
163+
})
164+
},
165+
hideModal () {
166+
this.$refs['userForm'].resetFields()
167+
this.onCancel()
168+
}
169+
}
170+
}
171+
</script>
172+
173+
<style>
174+
</style>

0 commit comments

Comments
 (0)