Browse Source

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	src/api/meeting/meeting.js
humingbo 1 year ago
parent
commit
2519b4cf5a

+ 5 - 7
src/api/meeting/meeting.js

@@ -95,14 +95,12 @@ export function confirmMeetings(id){
 }
 
 /**
- * 手动确定会议
- * @param id
- * @returns {*}
+ * 获取待办会议列表
+ * @returns {AxiosPromise}
  */
-export function confirmMeetingByconfirmId(id){
+export function getTodoMeetings(){
   return request({
-    url: '/meeting/confirmMeetingByconfirmId',
-    method:'get'
+    url: '/meeting/getTodoMeetings',
+    method: 'get'
   })
 }
-

+ 13 - 0
src/api/system/user.js

@@ -78,3 +78,16 @@ export function getDeptUserTree(deptId) {
     method: 'get'
   })
 }
+
+// 用户修改密码
+export function updateUserPwd(oldPassword, newPassword) {
+  const data = {
+    oldPassword,
+    newPassword
+  }
+  return request({
+    url: '/system/user/updateUserPwd',
+    method: 'put',
+    params: data
+  })
+}

+ 44 - 0
src/components/Hamburger/index.vue

@@ -0,0 +1,44 @@
+<template>
+  <div style="padding: 0 15px;" @click="toggleClick">
+    <svg
+      :class="{'is-active':isActive}"
+      class="hamburger"
+      viewBox="0 0 1024 1024"
+      xmlns="http://www.w3.org/2000/svg"
+      width="64"
+      height="64"
+    >
+      <path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" />
+    </svg>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'Hamburger',
+  props: {
+    isActive: {
+      type: Boolean,
+      default: false
+    }
+  },
+  methods: {
+    toggleClick() {
+      this.$emit('toggleClick')
+    }
+  }
+}
+</script>
+
+<style scoped>
+.hamburger {
+  display: inline-block;
+  vertical-align: middle;
+  width: 20px;
+  height: 20px;
+}
+
+.hamburger.is-active {
+  transform: rotate(180deg);
+}
+</style>

+ 17 - 10
src/layout/components/Navbar.vue

@@ -1,23 +1,26 @@
 <template>
   <div class="navbar">
-<!--    <hamburger :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />-->
-
-    <breadcrumb class="breadcrumb-container" />
+    <hamburger :is-active="sidebar.opened" id="hamburger-container" class="hamburger-container" @toggleClick="toggleSideBar"/>
 
+    <breadcrumb class="breadcrumb-container"/>
     <div class="right-menu">
+      <div style="margin-right: 10px;margin-bottom: 20px">{{ name }}</div>
       <el-dropdown class="avatar-container" trigger="click">
         <div class="avatar-wrapper">
           <img :src="avatar+'?imageView2/1/w/80/h/80'" class="user-avatar">
-          <i class="el-icon-caret-bottom" />
+          <i class="el-icon-caret-bottom"/>
         </div>
         <el-dropdown-menu slot="dropdown" class="user-dropdown">
           <router-link to="/">
             <el-dropdown-item>
-              Home
+              首页
             </el-dropdown-item>
           </router-link>
+          <router-link to="/user/profile">
+            <el-dropdown-item>个人中心</el-dropdown-item>
+          </router-link>
           <el-dropdown-item divided @click.native="logout">
-            <span style="display:block;">Log Out</span>
+            <span style="display:block;">退出登录</span>
           </el-dropdown-item>
         </el-dropdown-menu>
       </el-dropdown>
@@ -26,17 +29,20 @@
 </template>
 
 <script>
-import { mapGetters } from 'vuex'
+import {mapGetters} from 'vuex'
 import Breadcrumb from '@/components/Breadcrumb'
+import Hamburger from '@/components/Hamburger'
 
 export default {
   components: {
     Breadcrumb,
+    Hamburger
   },
   computed: {
     ...mapGetters([
       'sidebar',
-      'avatar'
+      'avatar',
+      'name'
     ])
   },
   methods: {
@@ -57,7 +63,7 @@ export default {
   overflow: hidden;
   position: relative;
   background: #fff;
-  box-shadow: 0 1px 4px rgba(0,21,41,.08);
+  box-shadow: 0 1px 4px rgba(0, 21, 41, .08);
 
   .hamburger-container {
     line-height: 46px;
@@ -65,7 +71,7 @@ export default {
     float: left;
     cursor: pointer;
     transition: background .3s;
-    -webkit-tap-highlight-color:transparent;
+    -webkit-tap-highlight-color: transparent;
 
     &:hover {
       background: rgba(0, 0, 0, .025)
@@ -80,6 +86,7 @@ export default {
     float: right;
     height: 100%;
     line-height: 50px;
+    display: flex;
 
     &:focus {
       outline: none;

+ 1 - 1
src/layout/components/Sidebar/index.vue

@@ -7,7 +7,7 @@
         :collapse="isCollapse"
         :background-color="variables.menuBg"
         :text-color="variables.menuText"
-        :unique-opened="false"
+        :unique-opened="true"
         :active-text-color="variables.menuActiveText"
         :collapse-transition="false"
         mode="vertical"

+ 1 - 0
src/main.js

@@ -9,6 +9,7 @@ import zh from 'element-ui/lib/locale/lang/zh-CN' // lang i18n
 import directive from './directive'
 import auth from './plugins/auth'
 import '@/styles/index.scss' // global css
+import '@/assets/styles/ruoyi.scss'
 
 import App from './App'
 import store from './store'

+ 14 - 0
src/router/index.js

@@ -52,6 +52,20 @@ export const constantRoutes = [
       component: () => import('@/views/dashboard/index'),
       meta: {title: 'Dashboard', icon: 'dashboard'}
     }]
+  },
+  {
+    path: '/user',
+    component: Layout,
+    hidden: true,
+    redirect: 'noredirect',
+    children: [
+      {
+        path: 'profile',
+        component: () => import('@/views/system/profile/index'),
+        name: 'Profile',
+        meta: { title: '个人中心', icon: 'user' }
+      }
+    ]
   }
 
 ]

+ 3 - 0
src/store/modules/app.js

@@ -10,6 +10,9 @@ const state = {
 
 const mutations = {
   TOGGLE_SIDEBAR: state => {
+    if (state.sidebar.hide) {
+      return false;
+    }
     state.sidebar.opened = !state.sidebar.opened
     state.sidebar.withoutAnimation = false
     if (state.sidebar.opened) {

+ 7 - 1
src/utils/request.js

@@ -49,6 +49,12 @@ service.interceptors.response.use(
 
     // if the custom code is not 20000, it is judged as an error.
     if (res.code !== '2000') {
+      if (res.code === 'S-F-5000') {
+        store.dispatch('Logout').then(() => {
+          location.href = process.env.VUE_APP_CONTEXT_PATH + "dashboard";
+        })
+      }
+
       Message({
         message: res.message || 'Error',
         type: 'error',
@@ -68,7 +74,7 @@ service.interceptors.response.use(
           })
         })
       }
-      return Promise.reject(new Error(res.message || 'Error'))
+      return Promise.reject('无效的会话,或者会话已过期,请重新登录')
     } else {
       return res
     }

+ 446 - 3
src/views/dashboard/index.vue

@@ -1,18 +1,451 @@
 <template>
-  <div class="dashboard-container">
-    <div class="dashboard-text">name: {{ name }}</div>
+  <div class="app-container">
+    <el-row :gutter="10">
+      <el-col :span="6">
+        <el-card class="card">
+          <div style="color: white;">
+            <h2>我的待办事项:{{ todoTaskList }}</h2>
+          </div>
+        </el-card>
+      </el-col>
+      <el-col :span="6">
+        <el-card class="card" style="background-color: indianred">
+          <div style="color: white">
+            <h2>我的项目</h2>
+          </div>
+        </el-card>
+      </el-col>
+      <el-col :span="6">
+        <el-card class="card" style="background-color:steelblue">
+          <div style="color: white;">
+            <h2>出勤状态:上班</h2>
+          </div>
+        </el-card>
+      </el-col>
+      <el-col :span="6">
+        <el-card class="card" style="background-color:goldenrod">
+          <div style="color: white;">
+            <h2>其他事项</h2>
+          </div>
+        </el-card>
+      </el-col>
+    </el-row>
+
+    <el-row :gutter="20" style="margin-top: 50px">
+      <el-col :span="24">
+        <h3>我的待办列表</h3>
+        <el-table
+          :data="tableData"
+          style="width: 100%" border stripe size="small">
+          <el-table-column type="index" label="序号" width="80" align="center">
+          </el-table-column>
+          <el-table-column
+            prop="name"
+            label="任务名称" align="center"
+            width="180">
+          </el-table-column>
+          <el-table-column
+            prop="hname"
+            label="实施医院" align="center"
+            width="180">
+          </el-table-column>
+          <el-table-column
+            prop="hsytem"
+            label="实施系统" align="center"
+            width="160">
+          </el-table-column>
+          <el-table-column
+            prop="content"
+            label="实施内容" align="center"
+            min-width="250">
+          </el-table-column>
+          <el-table-column
+            prop="taskName"
+            label="当前处理步骤" align="center"
+            width="180">
+          </el-table-column>
+          <el-table-column
+            prop="flowType"
+            label="流程类型" align="center"
+            width="180">
+          </el-table-column>
+          <el-table-column align="center"
+                           prop="createTime"
+                           label="创建时间" width="200">
+          </el-table-column>
+          <el-table-column prop="op" width="200" label="操作" align="center">
+            <template scope="scope">
+              <el-button size="mini" type="success" @click="searchWorkFlow(scope.row)">查阅流程</el-button>
+              <el-button size="mini" type="primary" @click="auditFlow(scope.row)">处理</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <br/>
+        <el-table
+          :data="todoMeetings"
+          border
+          stripe
+          size="mini"
+          style="width: 100%">
+          <el-table-column type="index" label="序号" width="60">
+          </el-table-column>
+          <el-table-column label="会议主题" prop="meetingName">
+          </el-table-column>
+          <el-table-column label="会议类型" prop="meetingType" width="120">
+            <template scope="scope">
+              <!--会议类型(1-周例会,2-项目会议,3-实施会议,4-其他会议)-->
+              <div v-if="scope.row.meetingType===1">周例会</div>
+              <div v-else-if="scope.row.meetingType===2">项目会议</div>
+              <div v-else-if="scope.row.meetingType===3">实施会议</div>
+              <div v-else>其他会议</div>
+            </template>
+          </el-table-column>
+          <el-table-column label="会议地点" prop="meetingPlace" width="120">
+          </el-table-column>
+          <el-table-column prop="status" label="会议状态" width="120">
+            <template scope="scope">
+              <!--会议状态,0待开始,1进行中,2已结束-->
+              <div v-if="scope.row.status===0" style="color: green">待开始</div>
+              <div v-else-if="scope.row.status===1" style="color: #3A71A8">进行中</div>
+              <div v-else>已结束</div>
+            </template>
+          </el-table-column>
+          <el-table-column prop="name" label="会议时间" width="300">
+            <template scope="scope">
+              <div>{{ scope.row.beginTime }}——{{ scope.row.endTime }}</div>
+            </template>
+          </el-table-column>
+          <el-table-column prop="address" label="参会人员">
+            <template scope="scope">
+              <span>{{ scope.row.outAttendees }}</span>
+              <span v-if="scope.row.outAttendees!=''">、</span>
+              <span v-for="(item,index) in JSON.parse(scope.row.innerAttendees) ">
+             <template v-if="index > 0">,</template>
+             <span>{{ item.name }}</span>
+           </span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="createTime" label="创建时间" width="150">
+
+          </el-table-column>
+          <el-table-column label="操作" prop="op" width="150">
+            <template slot-scope="scope">
+              <el-button v-if="scope.row.status===2" type="primary" size="mini" @click="confirmMeeting(scope.row)">
+                结果确认
+              </el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+
+      </el-col>
+    </el-row>
+
+    <el-dialog :visible.sync="workFlowVisble" width="75%" title="实施流程明细">
+      <el-steps :active="activeFlow" finish-status="success" align-center>
+        <el-step title="开始"/>
+        <el-step title="项目启动会"/>
+        <el-step title="发货"/>
+        <el-step title="物流签收"/>
+        <el-step title="进入现场"/>
+        <el-step title="设备安装"/>
+        <el-step title="系统调试"/>
+        <el-step title="操作培训"/>
+        <el-step title="现场值守"/>
+        <el-step title="离场"/>
+        <el-step title="实施复盘"/>
+        <el-step title="文件归档"/>
+        <el-step title="结束"/>
+      </el-steps>
+    </el-dialog>
+
+    <!--处理实施工作任务--->
+    <el-dialog title="审批处理" :visible.sync="editEnforceFormVisible" width="45%" @click="closeEnforceDialog">
+      <el-form label-width="120px" :model="enforceForm">
+        <el-descriptions title="" :column="2" border>
+          <el-descriptions-item>
+            <template slot="label">
+              项目名称
+            </template>
+            {{ enforceFlow.name }}
+          </el-descriptions-item>
+          <el-descriptions-item>
+            <template slot="label">
+              项目总负责人
+            </template>
+            {{ enforceFlow.enforcer }}
+          </el-descriptions-item>
+          <el-descriptions-item>
+            <template slot="label">
+              实施医院
+            </template>
+            {{ enforceFlow.hno }}
+          </el-descriptions-item>
+          <el-descriptions-item>
+            <template slot="label">
+              实施系统
+            </template>
+            {{ enforceFlow.hsytem }}
+          </el-descriptions-item>
+          <el-descriptions-item>
+            <template slot="label">
+              实施时间
+            </template>
+            {{ enforceFlow.time }}
+          </el-descriptions-item>
+          <el-descriptions-item>
+            <template slot="label">
+              实施状态
+            </template>
+            <span style="color: darkred" v-if="enforceFlow.status===0">待开始</span>
+            <span style="color: green" v-else-if="enforceFlow.status===1">进行中</span>
+            <span style="color: darkblue" v-else="enforceFlow.status===2">已结束</span>
+          </el-descriptions-item>
+          <el-descriptions-item :span="2">
+            <template slot="label">
+              当前任务
+            </template>
+            {{ enforceFlow.flowType }}
+          </el-descriptions-item>
+          <el-descriptions-item :span="2">
+            <template slot="label">
+              实施内容
+            </template>
+            {{ enforceFlow.content }}
+          </el-descriptions-item>
+          <el-descriptions-item :span="2">
+            <template slot="label">
+              下个步骤负责人
+            </template>
+            <el-input size="small" v-model="enforceForm.director" :disabled="true" @keyup.enter.native="openUsers()">
+              <el-button @click="openUsers" icon="el-icon-search" slot="append"></el-button>
+            </el-input>
+          </el-descriptions-item>
+          <el-descriptions-item :span="2">
+            <template slot="label">
+              备注说明
+            </template>
+            <el-input size="small" v-model="enforceForm.remark" type="textarea" :rows="4"></el-input>
+          </el-descriptions-item>
+
+          <el-descriptions-item :span="2">
+            <template slot="label">
+              文件列表
+            </template>
+            <el-upload
+              ref="upload"
+              :action="fileAction"
+              :on-remove="handleRemove"
+              :on-success="handleSuccess"
+              :on-error="handleError"
+              name="file">
+              <el-button slot="trigger" size="small" type="primary">选取文件</el-button>
+            </el-upload>
+          </el-descriptions-item>
+
+        </el-descriptions>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button size="small" @click="closeEnforceDialog">取消</el-button>
+        <el-button size="small" type="primary" class="title" @click="submitEnforceForm('editForm')">确定处理</el-button>
+      </div>
+    </el-dialog>
+
+
+    <el-dialog title="选择人员" :visible.sync="usersVisble" width="30%" @click="closeUserDialog">
+      <el-tree
+        ref="dept"
+        :data="userList"
+        node-key="id"
+        :props="{label:'name'}" default-expand-all="true">
+      </el-tree>
+      <div slot="footer" class="dialog-footer">
+        <el-button size="small" @click="closeUserDialog">取消</el-button>
+        <el-button size="small" type="primary" class="title" @click="submitUserForm('editForm')">确定</el-button>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
 <script>
-import { mapGetters } from 'vuex'
+import {mapGetters} from 'vuex'
+import Pagination from '@/components/Page/Pagination'
+import {completeTask, getPersonalTaskList} from '@/api/meeting/enforce'
+import {confirmMeetings, getTodoMeetings} from '@/api/meeting/meeting'
+import {getDeptUserTree} from '@/api/system/user'
+import {deleteFile, uploadFileUrl} from '@/api/file/file'
 
 export default {
   name: 'Dashboard',
+  components: {
+    Pagination
+  },
   computed: {
     ...mapGetters([
       'name'
     ])
+  },
+  data() {
+    return {
+      usersVisble: false,
+      userList: [],
+      pageparm: {
+        current: 1,
+        size: 10,
+        total: 0
+      },
+      tableData: [],
+      todoMeetings: [],
+      auditWin: false,
+      formData: {},
+      workFlowVisble: false,
+      activeFlow: 0,
+      todoTaskList: 0,
+      editEnforceFormVisible: false,
+      enforceForm: {
+        id: null,
+        enforceId: null,
+        director: null,
+        directorId: null,
+        remark: null,
+        taskId: null,
+        currentStep: null,
+        filess: []
+      },
+      enforceFlow: {
+        name: '',
+        enforcer: '',
+        time: null,
+        hno: null,
+        hsytem: null,
+        content: '',
+        status: 0,
+        flowType: null
+      },
+      fileAction: ''
+    }
+  },
+  mounted() {
+    this.getTaskList();
+    this.getTodoMeetingList();
+    this.getDeptUserTrees();
+    this.fileAction = uploadFileUrl()
+  },
+  methods: {
+    getDeptUserTrees() {
+      getDeptUserTree('').then(res => {
+        this.userList = res.data
+      });
+    },
+    // 分页插件事件
+    callFather(parm) {
+      this.formInline.current = parm.current
+      this.formInline.size = parm.size
+
+    },
+    getTaskList() {
+      getPersonalTaskList().then(res => {
+        this.tableData = res.data
+        this.todoTaskList = this.tableData.length
+      })
+    },
+    getTodoMeetingList() {
+      getTodoMeetings().then(res => {
+        this.todoMeetings = res.data
+      })
+    },
+    searchWorkFlow(row) {
+      this.workFlowVisble = true
+      this.activeFlow = row.step
+    },
+    closeEnforceDialog() {
+      this.editEnforceFormVisible = false
+    },
+    auditFlow(row) {
+      this.enforceFlow.name = row.name
+      this.enforceFlow.enforcer = row.enforcer
+      this.enforceFlow.hno = row.hname
+      this.enforceFlow.hsytem = row.hsytem
+      this.enforceFlow.content = row.content
+      this.enforceFlow.time = row.beginTime + "至" + row.endTime
+      this.enforceFlow.status = row.status
+      this.enforceFlow.flowType = row.taskName
+      this.enforceForm.taskId = row.taskId
+      this.enforceForm.currentStep = row.step
+      this.enforceForm.enforceId = row.id
+      this.editEnforceFormVisible = true
+    },
+    openUsers() {
+      this.usersVisble = true
+    },
+    submitUserForm() {
+      //获取选中的节点
+      let node = this.$refs.dept.getCurrentNode();
+      if (node == undefined || node == null) {
+        this.$message({
+          message: '请选择数据!',
+          type: 'warning'
+        });
+        return;
+      }
+      if (node.type !== 'u') {
+        this.$message({
+          message: '请选择人员!',
+          type: 'warning'
+        });
+        return;
+      }
+      this.enforceForm.director = node.name
+      this.enforceForm.directorId = node.id
+      this.closeUserDialog();
+    },
+    closeUserDialog() {
+      this.usersVisble = false
+    },
+    handleRemove(file, fileList) {
+      let url = file.response.data.url;
+      deleteFile(url).then(res => {
+        this.$message({
+          message: '操作成功!',
+          type: 'success'
+        });
+      });
+      this.enforceForm.filess.splice(url);
+    },
+    handleSuccess(response, file, fileList) {
+      let fileUrl = response.data.url
+      this.enforceForm.filess.push(fileUrl)
+    },
+    handleError() {
+      this.$message({
+        message: '上传失败,请稍候再试!',
+        type: 'warning'
+      });
+    },
+    submitEnforceForm() {
+      let data = this.enforceForm;
+
+      completeTask(data).then(res => {
+        this.$message({
+          message: '操作成功!',
+          type: 'success'
+        });
+        this.closeEnforceDialog();
+        this.getTaskList();
+      });
+
+    },
+    confirmMeeting(row) {
+      let id = row.id;
+      confirmMeetings(id).then(res => {
+        this.$message({
+          message: '操作成功!',
+          type: 'success'
+        });
+        this.getData(this.formInline);
+      });
+
+    },
   }
 }
 </script>
@@ -22,9 +455,19 @@ export default {
   &-container {
     margin: 30px;
   }
+
   &-text {
     font-size: 30px;
     line-height: 46px;
   }
 }
+
+.card {
+  width: 300px;
+  height: 150px;
+  background-color: #1ab394;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
 </style>

+ 13 - 12
src/views/login/index.vue

@@ -4,7 +4,7 @@
              label-position="left">
 
       <div class="title-container">
-        <h3 class="title">Login Form</h3>
+        <h3 class="title">OA管理系统</h3>
       </div>
 
       <el-form-item prop="username">
@@ -14,7 +14,7 @@
         <el-input
           ref="username"
           v-model="loginForm.username"
-          placeholder="Username"
+          placeholder="登录账号"
           name="username"
           type="text"
           tabindex="1"
@@ -31,7 +31,7 @@
           ref="password"
           v-model="loginForm.password"
           :type="passwordType"
-          placeholder="Password"
+          placeholder="登录密码"
           name="password"
           tabindex="2"
           auto-complete="on"
@@ -43,13 +43,9 @@
       </el-form-item>
 
       <el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;"
-                 @click.native.prevent="handleLogin">Login
+                 @click.native.prevent="handleLogin">登录
       </el-button>
 
-      <div class="tips">
-        <span style="margin-right:20px;">username: admin</span>
-        <span> password: any</span>
-      </div>
 
     </el-form>
   </div>
@@ -64,22 +60,22 @@ export default {
     const validateUsername = (rule, value, callback) => {
       callback()
       if (!validUsername(value)) {
-        callback(new Error('Please enter the correct user name'))
+        callback(new Error('请输入正确的登录账号'))
       } else {
         callback()
       }
     }
     const validatePassword = (rule, value, callback) => {
       if (value.length < 6) {
-        callback(new Error('The password can not be less than 6 digits'))
+        callback(new Error('密码长度不能小于6位'))
       } else {
         callback()
       }
     }
     return {
       loginForm: {
-        username: 'admin',
-        password: '123456'
+        username: undefined,
+        password: undefined
       },
       loginRules: {
         username: [{required: true, trigger: 'blur', validator: validateUsername}],
@@ -165,6 +161,11 @@ $cursor: #fff;
         -webkit-text-fill-color: $cursor !important;
       }
     }
+
+    .el-input__inner::placeholder {
+      color: rgba(255, 255, 255, 0.24);
+    }
+
   }
 
   .el-form-item {

+ 98 - 0
src/views/system/profile/index.vue

@@ -0,0 +1,98 @@
+<template>
+  <div class="app-container">
+    <el-row :gutter="20">
+      <el-col :span="6" :xs="24">
+        <el-card class="box-card">
+          <div slot="header" class="clearfix">
+            <span>个人信息</span>
+          </div>
+          <div>
+            <ul class="list-group list-group-striped">
+              <li class="list-group-item">
+                <svg-icon icon-class="user"/>
+                用户名称
+                <div class="pull-right">{{ user.userName }}</div>
+              </li>
+              <li class="list-group-item">
+                <svg-icon icon-class="tree"/>
+                所属部门
+                <div class="pull-right">{{ user.deptName }} / {{ user.post }}</div>
+              </li>
+              <li class="list-group-item">
+                <svg-icon icon-class="peoples"/>
+                所属角色
+                <div class="pull-right">{{ roleGroup }}</div>
+              </li>
+              <li class="list-group-item">
+                <svg-icon icon-class="phone"/>
+                手机号码
+                <div class="pull-right">{{ user.phone }}</div>
+              </li>
+              <li class="list-group-item">
+                <svg-icon icon-class="date"/>
+                生日
+                <div class="pull-right">{{ user.birthday }}</div>
+              </li>
+              <li class="list-group-item">
+                <svg-icon icon-class="email"/>
+                居住地址
+                <div class="pull-right">{{ user.address }}</div>
+              </li>
+              <li class="list-group-item">
+                <svg-icon icon-class="date"/>
+                创建日期
+                <div class="pull-right">{{ user.createTime }}</div>
+              </li>
+            </ul>
+          </div>
+        </el-card>
+      </el-col>
+      <el-col :span="18" :xs="24">
+        <el-card>
+          <div slot="header" class="clearfix">
+            <span>基本资料</span>
+          </div>
+          <el-tabs v-model="activeTab">
+            <el-tab-pane label="基本资料" name="userinfo">
+              <userInfo :user="user"/>
+            </el-tab-pane>
+            <el-tab-pane label="修改密码" name="resetPwd">
+              <resetPwd/>
+            </el-tab-pane>
+          </el-tabs>
+        </el-card>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import userInfo from "./userInfo";
+import resetPwd from "./resetPwd";
+import {getUser} from "@/api/system/user";
+
+export default {
+  name: "Profile",
+  components: {userInfo, resetPwd},
+  data() {
+    return {
+      user: {},
+      roleGroup: {},
+      activeTab: "userinfo"
+    };
+  },
+  created() {
+    this.getUser();
+  },
+  methods: {
+    getUser() {
+      getUser('').then(res => {
+        this.user = res.data.user;
+        let roles = res.data.roles.filter(r => res.data.roleIds.indexOf(r.id) > -1);
+        let roleNames = roles.map(r => r.roleName);
+        this.roleGroup = roleNames.join()
+      });
+    }
+  }
+};
+</script>

+ 68 - 0
src/views/system/profile/resetPwd.vue

@@ -0,0 +1,68 @@
+<template>
+  <el-form ref="form" :model="user" :rules="rules" label-width="80px">
+    <el-form-item label="旧密码" prop="oldPassword">
+      <el-input v-model="user.oldPassword" placeholder="请输入旧密码" type="password" show-password/>
+    </el-form-item>
+    <el-form-item label="新密码" prop="newPassword">
+      <el-input v-model="user.newPassword" placeholder="请输入新密码" type="password" show-password/>
+    </el-form-item>
+    <el-form-item label="确认密码" prop="confirmPassword">
+      <el-input v-model="user.confirmPassword" placeholder="请确认新密码" type="password" show-password/>
+    </el-form-item>
+    <el-form-item>
+      <el-button type="primary" size="mini" @click="submit">保存</el-button>
+      <el-button type="danger" size="mini" @click="close">关闭</el-button>
+    </el-form-item>
+  </el-form>
+</template>
+
+<script>
+import { updateUserPwd } from "@/api/system/user";
+
+export default {
+  data() {
+    const equalToPassword = (rule, value, callback) => {
+      if (this.user.newPassword !== value) {
+        callback(new Error("两次输入的密码不一致"));
+      } else {
+        callback();
+      }
+    };
+    return {
+      user: {
+        oldPassword: undefined,
+        newPassword: undefined,
+        confirmPassword: undefined
+      },
+      // 表单校验
+      rules: {
+        oldPassword: [
+          { required: true, message: "旧密码不能为空", trigger: "blur" }
+        ],
+        newPassword: [
+          { required: true, message: "新密码不能为空", trigger: "blur" },
+          { min: 6, max: 20, message: "长度在 6 到 20 个字符", trigger: "blur" }
+        ],
+        confirmPassword: [
+          { required: true, message: "确认密码不能为空", trigger: "blur" },
+          { required: true, validator: equalToPassword, trigger: "blur" }
+        ]
+      }
+    };
+  },
+  methods: {
+    submit() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          updateUserPwd(this.user.oldPassword, this.user.newPassword).then(response => {
+            this.$message.success("修改成功");
+          });
+        }
+      });
+    },
+    close() {
+      this.$tab.closePage();
+    }
+  }
+};
+</script>

+ 64 - 0
src/views/system/profile/userInfo.vue

@@ -0,0 +1,64 @@
+<template>
+  <el-form ref="form" :model="user" :rules="rules" label-width="80px">
+    <el-form-item label="手机号码" prop="phone">
+      <el-input v-model="user.phone" maxlength="11"/>
+    </el-form-item>
+    <el-form-item label="生日" prop="birthday">
+      <el-input v-model="user.birthday"/>
+    </el-form-item>
+    <el-form-item label="居住地址" prop="address">
+      <el-input v-model="user.address" type="textarea" maxlength="100" placeholder="请输入居住地址"></el-input>
+    </el-form-item>
+    <el-form-item label="性别">
+      <el-radio-group v-model="user.sex">
+        <el-radio label="0">男</el-radio>
+        <el-radio label="1">女</el-radio>
+      </el-radio-group>
+    </el-form-item>
+    <el-form-item>
+      <el-button type="primary" size="mini" @click="submit">保存</el-button>
+      <el-button type="danger" size="mini" @click="close">关闭</el-button>
+    </el-form-item>
+  </el-form>
+</template>
+
+<script>
+import {updateUser} from "@/api/system/user";
+
+export default {
+  props: {
+    user: {
+      type: Object
+    }
+  },
+  data() {
+    return {
+      // 表单校验
+      rules: {
+        phone: [
+          {required: true, message: "手机号码不能为空", trigger: "blur"},
+          {
+            pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
+            message: "请输入正确的手机号码",
+            trigger: "blur"
+          }
+        ]
+      }
+    };
+  },
+  methods: {
+    submit() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          updateUser(this.user).then(response => {
+            this.$message.success("修改成功");
+          });
+        }
+      });
+    },
+    close() {
+      this.$tab.closePage();
+    }
+  }
+};
+</script>