mirror of https://github.com/openspug/spug
A 个人信息 新增个人信息设置 ,更新项目描述。
parent
18873bb6ad
commit
c3d0f3941a
10
README.md
10
README.md
|
@ -6,9 +6,9 @@
|
|||
[](https://nodejs.org/)
|
||||
[](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 贡献者
|
||||
----------------------------
|
||||
|
|
|
@ -75,10 +75,6 @@
|
|||
│ │ ├── model.py // 系统公用类
|
||||
│ │ ├── tool.py // 系统公用工具文件
|
||||
│ │ ├── utils.py //
|
||||
│ └── storage // 系统公用目录
|
||||
│ │ ├── exec_tmp // 执行目录
|
||||
│ │ ├── images // 镜像目录
|
||||
│ │ ├── publish_tmp // 发布目录
|
||||
│ └── config.py.example // 后端配置文件模板
|
||||
│ └── main.py // 后端入口文件,加载所有模块
|
||||
│ └── manage.py // 系统管理文件
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
# Ignore everything in this directory
|
||||
*
|
||||
!.gitignore
|
|
@ -1,3 +0,0 @@
|
|||
# Ignore everything in this directory
|
||||
*
|
||||
!.gitignore
|
|
@ -1,3 +0,0 @@
|
|||
# Ignore everything in this directory
|
||||
*
|
||||
!.gitignore
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<template>
|
||||
<div>
|
||||
Welcome login
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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,
|
||||
}
|
||||
]
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue