hrm前端一版

This commit is contained in:
2025-12-22 10:57:47 +08:00
parent 6858648b07
commit 40f96069ab
7 changed files with 1784 additions and 0 deletions

View File

@@ -0,0 +1,190 @@
<template>
<div class="hrm-page">
<section class="panel-grid">
<el-card class="metal-panel" shadow="hover">
<div slot="header" class="panel-header">
<span>组织树</span>
<el-button size="mini" icon="el-icon-refresh" @click="loadOrg">刷新</el-button>
</div>
<el-tree
v-loading="orgLoading"
:data="orgTree"
node-key="orgId"
:props="{ label: 'orgName', children: 'children' }"
accordion
highlight-current
@node-click="handleOrgClick"
>
<span slot-scope="{ data }" class="custom-tree-node">
<span>{{ data.orgName }}</span>
<el-tag size="mini" effect="plain" type="info" class="tree-tag">{{ data.orgType || '组织' }}</el-tag>
</span>
</el-tree>
</el-card>
<el-card class="metal-panel" shadow="hover">
<div slot="header" class="panel-header">
<span>员工档案</span>
<div class="actions-inline">
<el-input
v-model="empQuery.empName"
placeholder="姓名/工号"
size="mini"
clearable
@keyup.enter.native="loadEmployee"
style="width: 180px"
/>
<el-select
v-model="empQuery.status"
size="mini"
placeholder="状态"
clearable
style="width: 140px"
@change="loadEmployee"
>
<el-option label="在职" value="active" />
<el-option label="离职" value="inactive" />
</el-select>
<el-button size="mini" type="primary" icon="el-icon-search" @click="loadEmployee">查询</el-button>
</div>
</div>
<el-table :data="employeeList" v-loading="empLoading" height="700" stripe>
<el-table-column label="工号" prop="empNo" min-width="110" />
<el-table-column label="姓名" prop="empName" min-width="120" />
<el-table-column label="性别" prop="gender" min-width="80" />
<el-table-column label="手机" prop="mobile" min-width="130" />
<el-table-column label="雇佣类型" prop="employmentType" min-width="120" />
<el-table-column label="状态" prop="status" min-width="100">
<template slot-scope="scope">
<el-tag :type="statusType(scope.row.status)">{{ scope.row.status || '-' }}</el-tag>
</template>
</el-table-column>
<el-table-column label="入职日期" prop="hireDate" min-width="140">
<template slot-scope="scope">{{ formatDate(scope.row.hireDate) }}</template>
</el-table-column>
<el-table-column label="备注" prop="remark" min-width="160" show-overflow-tooltip />
</el-table>
</el-card>
</section>
</div>
</template>
<script>
import { listOrg, listEmployee } from '@/api/hrm'
export default {
name: 'HrmOrgEmployee',
data() {
return {
orgTree: [],
orgLoading: false,
orgSelected: null,
employeeList: [],
empLoading: false,
empQuery: { empName: '', status: undefined, mainOrgId: undefined }
}
},
created() {
this.loadOrg()
},
methods: {
statusType(status) {
if (!status) return 'info'
const map = { pending: 'warning', draft: 'info', approved: 'success', rejected: 'danger', active: 'success', inactive: 'info' }
return map[status] || 'info'
},
formatDate(val) {
if (!val) return ''
const d = new Date(val)
const p = n => (n < 10 ? `0${n}` : n)
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`
},
loadOrg() {
this.orgLoading = true
listOrg({ pageNum: 1, pageSize: 999 })
.then(res => {
const rows = res.rows || []
this.orgTree = this.buildTree(rows)
if (!this.orgSelected && this.orgTree.length) this.orgSelected = this.orgTree[0].orgId
this.empQuery.mainOrgId = this.orgSelected
this.loadEmployee()
})
.finally(() => {
this.orgLoading = false
})
},
buildTree(list) {
const map = {}
list.forEach(item => {
map[item.orgId] = { ...item, children: [] }
})
const roots = []
list.forEach(item => {
const parent = map[item.parentId]
if (parent) parent.children.push(map[item.orgId])
else roots.push(map[item.orgId])
})
return roots
},
handleOrgClick(node) {
this.orgSelected = node.orgId
this.empQuery.mainOrgId = node.orgId
this.loadEmployee()
},
loadEmployee() {
if (!this.empQuery.mainOrgId) return
this.empLoading = true
listEmployee({ ...this.empQuery, pageNum: 1, pageSize: 50 })
.then(res => {
this.employeeList = res.rows || []
})
.finally(() => {
this.empLoading = false
})
}
}
}
</script>
<style lang="scss" scoped>
.hrm-page {
padding: 16px 20px 32px;
background: #f8f9fb;
}
.panel-grid {
display: grid;
grid-template-columns: 280px 1fr;
gap: 12px;
}
.metal-panel {
border: 1px solid #d7d9df;
border-radius: 10px;
background: #fff;
}
.panel-header {
display: flex;
justify-content: space-between;
align-items: center;
font-weight: 600;
color: #303133;
}
.actions-inline {
display: flex;
gap: 8px;
align-items: center;
}
.custom-tree-node {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}
.tree-tag {
margin-left: 8px;
}
@media (max-width: 1200px) {
.panel-grid {
grid-template-columns: 1fr;
}
}
</style>