Prechádzať zdrojové kódy

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

张玉坡 7 rokov pred
rodič
commit
c3d0f3941a

+ 7 - 3
README.md

@@ -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 贡献者
 ----------------------------

+ 0 - 4
docs/project_structure.md

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

+ 25 - 2
spug_api/apps/account/user.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:

+ 0 - 3
spug_api/storage/exec_tmp/.gitignore

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

+ 0 - 3
spug_api/storage/images/.gitignore

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

+ 0 - 3
spug_api/storage/publish_tmp/.gitignore

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

+ 30 - 7
spug_web/src/components/Layout.vue

@@ -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>

+ 3 - 1
spug_web/src/components/Login.vue

@@ -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)

+ 13 - 0
spug_web/src/components/Welcome.vue

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

+ 62 - 0
spug_web/src/components/account/Person.vue

@@ -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>

+ 170 - 0
spug_web/src/components/account/PersonSet.vue

@@ -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>

+ 12 - 2
spug_web/src/components/account/routes.js

@@ -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,
     }
 ]

+ 1 - 1
spug_web/src/plugins/globalTools.js

@@ -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 - 0
spug_web/src/router.js

@@ -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,