A 个人信息 新增个人信息设置 ,更新项目描述。

pull/11/head
张玉坡 2018-03-05 16:16:56 +08:00
parent 18873bb6ad
commit c3d0f3941a
14 changed files with 327 additions and 29 deletions

View File

@ -6,9 +6,9 @@
[![Node](https://img.shields.io/badge/node-6.x-green.svg?style=plastic)](https://nodejs.org/)
[![Element](https://img.shields.io/badge/Element-2.x-green.svg?style=plastic)](http://element-cn.eleme.io/#/zh-CN/)
Spug is an open source O & M management system developed with Python + Flask + Element. The system is separated from the front and the back of the system to help small and medium-sized enterprises manage the hosts, tasks, deployment, configuration files, monitoring and alarming
Spug is an open source O & M management system developed with Python + Flask + Vue + Element. The system is separated from the front and the back of the system to help small and medium-sized enterprises manage the hosts, tasks, deployment, configuration files, monitoring and alarming
Spug是一款使用Python+Flask+Element组件开发的开源运维管理系统,系统前后端分离,帮助中小型企业完成主机、任务、发布部署、配置文件、监控、报警等管理。
Spug是一款使用Python+Flask+Vue+Element组件开发的开源运维管理系统,系统前后端分离,帮助中小型企业完成主机、任务、发布部署、配置文件、监控、报警等管理。
#### Demo演示地址<https://spug.qbangmang.com/login>
@ -50,7 +50,7 @@ $ -e MYSQL_DATABASE="spug" //指定数据库名称
-e REGISTRY_PASSWORD="hubpwd" //指定私有镜像仓库密码
```
更多Dockerfile [Dockerfile](https://github.com/openspug/spug/docs/Dockerfile)
更多Dockerfile [Dockerfile](https://github.com/openspug/spug/tree/master/docs/Dockerfile)
### 详细安装步骤
@ -68,6 +68,7 @@ $ -e MYSQL_DATABASE="spug" //指定数据库名称
  2. Start server 启动服务端:
$ cd spug/spug_api
$ pip install -r requirements.txt //安装依赖包
$ mv config.py.example config.py //编辑配置文件
$ python manage.py init_db //初始化数据库
$ python manage.py create_admin //创建管理员
  $ python main.py //启动服务
@ -91,6 +92,9 @@ $ -e MYSQL_DATABASE="spug" //指定数据库名称
----------------------------
* [Project structure 项目结构](https://github.com/openspug/spug/blob/master/docs/project_structure.md)
* [前端UI组件](http://element-cn.eleme.io/2.1/#/zh-CN/component/installation)
* [后端Flask文档](http://flask.pocoo.org/)
### Contributor 贡献者
----------------------------

View File

@ -75,10 +75,6 @@
│ │ ├── model.py // 系统公用类
│ │ ├── tool.py // 系统公用工具文件
│ │ ├── utils.py //
│ └── storage // 系统公用目录
│ │ ├── exec_tmp // 执行目录
│ │ ├── images // 镜像目录
│ │ ├── publish_tmp // 发布目录
│ └── config.py.example // 后端配置文件模板
│ └── main.py // 后端入口文件,加载所有模块
│ └── manage.py // 系统管理文件

View File

@ -64,7 +64,7 @@ def delete(u_id):
@require_permission('account_user_edit | account_user_disable')
def put(u_id):
form, error = JsonParser('nickname', 'is_active',
Argument('role_id', type=int, help='请选择角色'),
Argument('role_id', type=int, required=False, help='请选择角色'),
Argument('email', nullable=True),
Argument('password', nullable=False, required=False),
Argument('mobile', nullable=True)).parse()
@ -79,6 +79,27 @@ def put(u_id):
return json_response(message=error)
@blueprint.route('/modifypwd', methods=['POST'])
def modify_pwd():
form, error = JsonParser('password', 'newpassword').parse()
if error is None:
if g.user.verify_password(form.password):
g.user.password = form.newpassword
g.user.save()
return json_response()
else:
return json_response(message='原密码错误')
return json_response(message=error)
@blueprint.route('/<int:u_id>', methods=['GET'])
def get_person(u_id):
if u_id:
u_info = User.query.get_or_404(u_id)
return json_response(u_info)
return json_response(message='user_id不能为空')
@blueprint.route('/login/', methods=['POST'])
def login():
form, error = JsonParser('username', 'password').parse()
@ -91,7 +112,9 @@ def login():
user.access_token = token
user.token_expired = time.time() + 8 * 60 * 60
user.save()
return json_response({'token': token, 'is_supper': user.is_supper, 'permissions': list(user.permissions)})
user_data = user.to_json()
user_data.update({'token': token, 'permissions': list(user.permissions)})
return json_response(user_data)
else:
login_limit[form.username] += 1
if login_limit[form.username] >= 3:

View File

@ -1,3 +0,0 @@
# Ignore everything in this directory
*
!.gitignore

View File

@ -1,3 +0,0 @@
# Ignore everything in this directory
*
!.gitignore

View File

@ -1,3 +0,0 @@
# Ignore everything in this directory
*
!.gitignore

View File

@ -7,7 +7,7 @@
<div class="layout-logo" v-if="!isCollapse">Spug</div>
<div class="layout-logo" v-else>S</div>
<el-menu :collapse="isCollapse" :default-active="current_index" @select="handleSelect" unique-opened router>
<template v-for="item in menus">
<template v-for="item in menus" v-if="has_permission(item.permission)">
<el-submenu v-if="item.subs" :index="item.key">
<template slot="title">
<i :class="item.icon"></i>
@ -28,7 +28,13 @@
<i class="fa fa-sign-out" @click="logout"></i>
</div>
<div class="i-button">
<i class="fa fa-user"></i>
<el-dropdown trigger="click" @command="handleCommand">
<span class="el-dropdown-link "> <i class="fa fa-user"></i> {{ login_user }}<i class="el-icon-arrow-down el-icon--right"></i></span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="person">个人信息</el-dropdown-item>
<el-dropdown-item command="set_person">设置</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<div class="i-button">
<i class="fa fa-envelope"></i>
@ -54,7 +60,8 @@
return {
current_index: undefined,
isCollapse: false,
menus: menu.menus
menus: menu.menus,
login_user: undefined,
}
},
methods: {
@ -64,18 +71,34 @@
this.$router.push({name: 'login'})
})
},
handleCommand(command) {
if (command === 'person') {
this.$router.push({path: '/account/person'})
}
if (command === 'set_person'){
this.$router.push({path: '/account/personset'})
}
},
routerChange() {
this.current_index = this.$route.path;
},
handleSelect(path) {
this.current_index = path
},
back_login(){
let token = localStorage.getItem('token');
if (!token || token.length !== 32) {
this.$router.push({name: 'login'})
}
},
personInfo(){
this.login_user = localStorage.getItem('nickname');
}
},
created() {
let token = localStorage.getItem('token');
if (!token || token.length !== 32) {
this.$router.push({name: 'login'})
}
this.back_login();
this.personInfo();
}
}
</script>

View File

@ -87,7 +87,9 @@
localStorage.setItem('token', res.result['token']);
localStorage.setItem('is_supper', res.result['is_supper']);
localStorage.setItem('permissions', res.result['permissions']);
this.$router.push('/')
localStorage.setItem('user_id', res.result['id']);
localStorage.setItem('nickname', res.result['nickname']);
this.$router.push('/welcome');
}, response => {
this.error = response.result
}).finally(() => this.loading = false)

View File

@ -0,0 +1,13 @@
<template>
<div>
Welcome login
</div>
</template>
<script>
export default {
data () {
return {}
}
}
</script>

View File

@ -0,0 +1,62 @@
<template>
<div class="info_container">
<el-row class="info_row row" :gutter="10">
<el-col :span="8">
<div class="area">
<p class="title-container"><h5>个人信息</h5></p>
<el-form class="form" :model="personInfo" ref="personInfo" label-width="80px">
<el-form-item label="登录名">
<div class="info-msg">{{personInfo.username}}</div>
</el-form-item>
<el-form-item label="姓名" prop="nickname">
<div class="info-msg">{{personInfo.nickname}}</div>
</el-form-item>
<el-form-item label="邮箱" prop="email">
<div class="info-msg">{{personInfo.email}}</div>
</el-form-item>
<el-form-item label="电话" prop="mobile">
<div class="info-msg">{{personInfo.mobile}}</div>
</el-form-item>
</el-form>
</div>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
data() {
return {
personInfo: {
username: '',
nickname: '',
email:'',
mobile: '',
},
}
},
methods: {
//
getPersonInfo(user_id) {
this.$http.get(`/api/account/users/${user_id}`).then((response) => {
this.personInfo = response.result;
}, (response) => this.$layer_message(response.result)).finally()
},
getPerson(){
let user_id = localStorage.getItem('user_id');
this.getPersonInfo(user_id);
}
},
mounted() {
this.getPerson();
}
}
</script>
<style >
.info-msg{
padding: 0 10px;
}
</style>

View File

@ -0,0 +1,170 @@
<template>
<div class="info_container">
<el-row class="info_row row" :gutter="10">
<el-tabs v-model="activeName" type="card">
<el-tab-pane label="个人信息" name="person_setup">
<el-col :span="8">
<div class="area">
<p class="title"><h5>个人信息</h5></p>
<el-form class="form" :model="personInfo" :rules="personRules" ref="personInfo" label-width="80px">
<el-form-item label="登录名">
<el-input v-model="personInfo.username" size="small" disabled></el-input>
</el-form-item>
<el-form-item label="姓名" prop="nickname">
<el-input v-model="personInfo.nickname" size="small" placeholder="请输入姓名"></el-input>
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="personInfo.email" size="small" placeholder="请输入邮箱"></el-input>
</el-form-item>
<el-form-item label="电话" prop="mobile">
<el-input v-model="personInfo.mobile" size="small" placeholder="请输入电话"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="modifyPerson('personInfo')"></el-button>
<el-button @click="cancelPerson()"></el-button>
</el-form-item>
</el-form>
</div>
</el-col>
</el-tab-pane>
<el-tab-pane label="密码" name="pwd_setup">
<el-col :span="8">
<div class="area">
<p class="title"><h5>修改密码</h5></p>
<el-form class="form" :model="pwdInfo" :rules="pwdRules" ref="pwdInfo" label-width="80px">
<el-form-item label="原密码" prop="password">
<el-input type="password" v-model="pwdInfo.password" size="small" placeholder="原密码"></el-input>
</el-form-item>
<el-form-item label="新密码" prop="newpassword">
<el-input type="password" v-model="pwdInfo.newpassword" size="small" placeholder="新密码"></el-input>
</el-form-item>
<el-form-item label="确认密码" prop="surepassword">
<el-input type="password" v-model="pwdInfo.surepassword" size="small" placeholder="确认新密码"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="modifyPwd('pwdInfo')"></el-button>
<el-button @click="resetPwd('pwdInfo')"></el-button>
</el-form-item>
</el-form>
</div>
</el-col>
</el-tab-pane>
</el-tabs>
</el-row>
</div>
</template>
<script>
export default {
data() {
let validatePass = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入新密码'));
} else {
if (this.pwdInfo.surepassword !== '') {
this.$refs.pwdInfo.validateField('surepassword');
}
callback();
}
};
let validateSurepwd = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入确认密码'));
} else if (value !== this.pwdInfo.newpassword) {
callback(new Error('两次输入密码不一致!'));
} else {
callback();
}
};
let checkMobile = (rule, value, callback) => {
if (value === '') {
return callback(new Error('手机号不能为空'));
}
setTimeout(() => {
let mobile = /^1[34578]\d{9}$/;
if (!mobile.test(value)) {
callback(new Error('手机号不正确'));
} else {
callback();
}
}, 1000);
};
return {
personInfo: {
username: '',
nickname: '',
email:'',
mobile: '',
},
pwdInfo:{
password:'',
newpassword:'',
surepassword:''
},
activeName: 'person_setup',
personRules: {
nickname: [
{required: true, message: '请输入姓名', trigger: 'blur'}
],
mobile: [
{required: true, validator: checkMobile, trigger: 'blur'}
],
email:[
{ required: true, message: '请输入邮箱地址', trigger: 'blur' },
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur,change' }
],
},
pwdRules: {
password: [
{ required: true, message: '请输入原密码', trigger: 'blur' }
],
newpassword: [
{required: true, validator: validatePass, trigger: 'blur'}
],
surepassword:[
{ required: true, validator:validateSurepwd, trigger: 'blur' },
],
},
}
},
methods: {
//
getPersonInfo(user_id) {
this.$http.get(`/api/account/users/${user_id}`).then((response) => {
this.personInfo = response.result;
}, (response) => this.$layer_message(response.result)).finally()
},
//
cancelPerson: function () {
this.$router.push('/welcome');
},
resetPwd: function () {
this.pwdInfo = {};
},
modifyPwd: function () {
this.$http.post(`/api/account/users/modifypwd`, this.pwdInfo).then(response => {
this.$layer_message('修改成功', 'success');
}, response => this.$layer_message(response.result)).finally();
},
modifyPerson: function () {
console.log(this.personInfo);
this.$http.put(`/api/account/users/${this.personInfo.id}`, this.personInfo).then(response => {
this.$layer_message('保存成功', 'success');
localStorage.setItem('nickname', response.result['nickname']);
}, response => this.$layer_message(response.result)).finally();
},
getPerson(){
let user_id = localStorage.getItem('user_id');
this.getPersonInfo(user_id);
}
},
mounted() {
this.getPerson();
}
}
</script>

View File

@ -2,8 +2,10 @@
* Created by aka on 2017/5/22.
*/
import User from './User.vue'
import Role from './Role.vue'
import User from './User.vue';
import Role from './Role.vue';
import PersonSet from './PersonSet.vue';
import Person from './Person.vue';
export default [
{
@ -19,5 +21,13 @@ export default [
meta: {
permission: 'account_role_view'
}
},
{
path: 'person',
component: Person,
},
{
path: 'personset',
component: PersonSet,
}
]

View File

@ -122,7 +122,7 @@ tools.install = function (Vue, router) {
};
// 路由导航钩子
router.beforeEach((to, from, next) => {
if (['/', '/login', '/deny'].includes(to.path)) {
if (['/', '/login', '/deny','/account/person','/account/personset','/home','/welcome'].includes(to.path)) {
next()
} else if (to.meta.hasOwnProperty('permission') && Vue.prototype.has_permission(to.meta.permission)) {
next()

View File

@ -4,6 +4,7 @@
import Home from './components/Home.vue'
import Deny from './components/Deny.vue'
import Welcome from './components/Welcome.vue'
import Login from './components/Login.vue'
import Layout from './components/Layout.vue'
import account_routes from './components/account/routes'
@ -67,6 +68,9 @@ export default [
}, {
path: '/deny',
component: Deny
}, {
path: '/welcome',
component: Welcome
}, {
path: '/',
component: Layout,