人员管理

This commit is contained in:
bb_pan 2025-04-14 17:33:17 +08:00
parent dfb3704669
commit 55a7131293
8 changed files with 852 additions and 1 deletions

View File

@ -0,0 +1,11 @@
import request from '@/utils/request'
// 班组管理-列表
export const getProjectListAPI = (data) => {
return request.get(`/smart-site/bm_project/list`, { params: data })
}
// 班组管理-删除
export const deleteApi = (data) => {
return request.post('/smart-site/bm_project/delete/' + data)
}

View File

@ -0,0 +1,181 @@
<template>
<!-- 新增修改弹框 -->
<div>
<el-row :gutter="20">
<el-col :span="12">
<el-form label-width="auto" :model="addOrEditForm" :rules="formRules" ref="addOrEditFormRef">
<el-form-item label="姓名:" prop="userName">
<el-input
v-model="addOrEditForm.userName"
clearable
placeholder="请输入姓名"
maxlength="30"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="身份证号:" prop="idCard">
<el-input
v-model="addOrEditForm.idCard"
clearable
placeholder="请输入身份证号"
maxlength="18"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="入场工程:" prop="proName">
<el-select v-model="addOrEditForm.proName" placeholder="请选择入场工程" style="width: 100%">
<el-option v-for="item in proNameList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="联系方式:" prop="phone">
<el-input
v-model="addOrEditForm.phone"
placeholder="请输入联系方式"
clearable
maxlength="11"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="工种/岗位:" prop="work">
<el-input
v-model="addOrEditForm.work"
placeholder="请输入工种/岗位"
clearable
maxlength="30"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="民族:" prop="nation">
<el-input
v-model="addOrEditForm.nation"
placeholder="请输入民族"
clearable
maxlength="30"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="是否关键人员:" prop="isKeys">
<el-radio-group v-model="addOrEditForm.isKeys">
<el-radio-button label="1"></el-radio-button>
<el-radio-button label="0"></el-radio-button>
</el-radio-group>
</el-form-item>
</el-form>
</el-col>
<el-col :span="12" class="dialog-header-btn">
<el-upload
:class="{ hide: hideUpload }"
action="#"
:auto-upload="false"
accept="image/*"
:limit="1"
:file-list="facePhotoList"
list-type="picture-card"
:on-preview="handlePictureCardPreview"
:on-change="changeImage"
:on-exceed="handleExceed"
>
<i class="el-icon-plus"></i>
</el-upload>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
props: {
onSubmitDialog: {
type: Function,
default: () => {},
},
},
data() {
return {
//
addOrEditForm: {
userName: '', //
idCard: '', //
proName: '', //
phone: '', //
work: '', // /
nation: '', //
isKeys: '0', //
facePhoto: '', //
},
facePhotoList: [], //
hideUpload: false, //
// -
proNameList: [
{ label: '工程1', value: '1' },
{ label: '工程2', value: '2' },
{ label: '工程3', value: '3' },
],
//
formRules: {
userName: [{ required: true, message: '请输入班组名称', trigger: 'blur' }],
idCard: [
{ required: true, message: '请输入身份证号', trigger: 'blur' },
{
pattern: /^[1-9]\d{5}(?:18|19|20)\d{2}(?:0[1-9]|10|11|12)(?:0[1-9]|[1-2]\d|30|31)\d{3}[\dXx]$/,
message: '请输入正确的身份证号',
trigger: 'blur',
},
],
proName: [{ required: true, message: '请选择入场工程', trigger: 'change' }],
phone: [
{ required: true, message: '请输入联系方式', trigger: 'blur' },
{
pattern: /^(?:(?:\+|00)86)?1[3-9]\d{9}$/,
message: '请输入正确的手机号',
trigger: 'blur',
},
],
work: [{ required: true, message: '请输入工种/岗位', trigger: 'blur' }],
isKeys: [{ required: true, message: '请选择是否关键人员', trigger: 'change' }],
},
}
},
created() {
console.log('🚀 ~ created ~ this.addOrEdit:')
},
methods: {
handleEdit(row) {
console.log('🚀 ~ handleEdit ~ :', row)
},
handleExceed(file) {
//
this.$message.error('只能上传一张图片')
},
changeImage(file, fileList) {
console.log('🚀 ~ changeImage ~ file:', file)
},
handleRemove(file, fileList) {
console.log(file, fileList)
},
handlePictureCardPreview(file) {
console.log('🚀 ~ handlePictureCardPreview ~ file:', file)
},
//
onSubmitForm() {
this.$refs.addOrEditFormRef.validate(async (valid) => {
if (valid) {
try {
// const res = await addAPI(this.addOrEditForm)
this.$emit('closeDialog')
} catch (error) {
console.error('🚀 ~ onSubmitForm ~ error:', error)
}
}
})
},
},
}
</script>
<style lang="scss" scoped>
::v-deep .hide .el-upload--picture-card {
display: none !important;
}
</style>

View File

@ -0,0 +1,97 @@
<template>
<div>
<el-table :data="tableList" fit highlight-current-row style="width: 100%">
<el-table-column
type="index"
width="55"
label="序号"
align="center"
:index="(index) => (queryParams.pageNum - 1) * queryParams.pageSize + index + 1"
/>
<el-table-column
v-for="(column, index) in tableColumns"
show-overflow-tooltip
:key="index"
:label="column.label"
:prop="column.prop"
align="center"
>
<!-- 插槽 -->
<template v-slot="{ row }" v-if="column.prop == 'status'">
<el-tag v-if="row.status == 1" size="mini" type="success">在场</el-tag>
<el-tag v-else-if="row.status == 2" size="mini" type="warning">休息</el-tag>
<el-tag v-else-if="row.status == 3" size="mini" type="danger">离场</el-tag>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination
v-show="total > 0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</div>
</template>
<script>
export default {
data() {
return {
total: 0, //
queryParams: {
pageNum: 1,
pageSize: 10,
},
tableColumns: [
{ label: '姓名', prop: 'userName' },
{ label: '工种/岗位', prop: 'work' },
{ label: '所在工程', prop: 'projectName' },
{ label: '入场时间', prop: 'entryTime' },
{ label: '出场时间', prop: 'exitTime' },
{ label: '人员状态', prop: 'status' },
],
//
tableList: [
{
userName: '张三',
work: '电工',
projectName: '项目A',
entryTime: '2023-01-01',
exitTime: '2023-12-31',
status: '1',
},
{
userName: '李四',
work: '焊工',
projectName: '项目B',
entryTime: '2023-02-01',
exitTime: '2023-11-30',
status: '2',
},
],
}
},
created() {
this.getList()
},
methods: {
//
async getList() {
console.log('🚀 ~ getList ~')
const loading = this.$loading({ text: '加载中...' })
try {
// const res = await api(this.queryParams)
loading.close()
} catch (error) {
console.error('获取数据失败:', error)
loading.close()
}
},
},
}
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,81 @@
<template>
<!-- 新增修改弹框 -->
<div>
<el-form label-width="auto" :model="addOrEditForm" :rules="formRules" ref="addOrEditFormRef">
<el-form-item label="班组名称:" prop="teamName">
<el-input v-model="addOrEditForm.teamName" clearable placeholder="请输入班组名称" style="width: 100%" />
</el-form-item>
<el-form-item label="班组类型:" prop="teamType">
<el-select v-model="addOrEditForm.teamType" placeholder="请选择班组类型" style="width: 100%">
<el-option v-for="item in teamTypeList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="班组长:" prop="teamForeman">
<el-select v-model="addOrEditForm.teamForeman" placeholder="请选择班组长" style="width: 100%">
<el-option v-for="item in teamForemanList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-form>
</div>
</template>
<script>
// import { } from '@/api/'
export default {
props: {
onSubmitDialog: {
type: Function,
default: () => {},
},
},
data() {
return {
//
addOrEditForm: {
teamName: '', //
teamType: '', //
teamForeman: '', //
remark: '', //
},
// -
teamTypeList: [
{ label: '工人', value: '1' },
{ label: '管理人员', value: '2' },
{ label: '特殊人员', value: '3' },
],
// -
teamForemanList: [
{ label: '张三', value: '1' },
{ label: '李四', value: '2' },
{ label: '王五', value: '3' },
],
//
formRules: {
teamName: [{ required: true, message: '请输入班组名称', trigger: 'blur' }],
},
}
},
created() {
console.log('🚀 ~ created ~ this.addOrEdit:')
},
methods: {
//
onSubmitForm() {
this.$refs.addOrEditFormRef.validate(async (valid) => {
if (valid) {
try {
// const res = await addAPI(this.addOrEditForm)
this.$emit('closeDialog')
} catch (error) {
console.error('🚀 ~ onSubmitForm ~ error:', error)
}
}
})
},
},
}
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,20 @@
export const formLabel = [
{ f_label: '搜索关键词', f_model: 'keyWord', f_type: 'ipt', isShow: false },
{
f_label: '状态',
f_model: 'status',
f_type: 'sel',
isShow: false,
f_selList: [
{ value: '1', label: '启用' },
{ value: '2', label: '停用' },
],
},
]
export const columnsList = [
{ t_props: 'teamName', t_label: '班组名称' },
{ t_props: 'teamType', t_label: '班组类型' },
{ t_props: 'teamForeman', t_label: '班组长' },
{ t_props: 'planEndTime', t_label: '计划竣工日期' },
{ t_props: 'remark', t_label: '备注' },
]

View File

@ -0,0 +1,104 @@
<template>
<!-- 项目管理 -->
<div class="app-container">
<TableModel ref="tableModel" :formLabel="formLabel" :columnsList="columnsList" :request-api="getProjectListAPI">
<template slot="btn">
<el-button size="mini" type="" icon="el-icon-download" @click="handleExport()">导出</el-button>
<el-button size="mini" plain type="primary" icon="el-icon-plus" @click="handleAddData()">新增</el-button>
</template>
<template slot="handle" slot-scope="{ data }">
<el-button size="mini" type="primary" icon="el-icon-edit" @click="handleEditData(data)">修改</el-button>
<el-button size="mini" type="danger" icon="el-icon-delete" @click="handleDeleteData(data.id)">移除</el-button>
</template>
</TableModel>
<el-dialog :title="dialogConfig.outerTitle" :visible.sync="dialogConfig.outerVisible" width="40%">
<AddEditForm v-if="dialogConfig.outerVisible" ref="addEditForm" @closeDialog="closeDialog" />
<span slot="footer" class="dialog-footer">
<el-button @click="dialogConfig.outerVisible = false"> </el-button>
<el-button type="primary" @click="onSubmitDialog"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import TableModel from '@/components/TableModel'
import DialogModel from '@/components/DialogModel'
import AddEditForm from './components/add-edit-form'
import { formLabel, columnsList } from './config'
import { getProjectListAPI } from '@/api/personnel-manage/index.js'
export default {
components: {
TableModel,
DialogModel,
AddEditForm,
},
data() {
return {
formLabel, //
columnsList, //
dialogConfig: {
outerTitle: '新建项目',
outerVisible: false,
}, //
getProjectListAPI, //
}
},
methods: {
handleExport() {
console.log('导出')
this.$message({
type: 'warning',
message: '导出功能开发中,敬请期待!',
})
return
try {
let fileName = `人员出入场_${new Date().getTime()}.xLsx`
let url = ''
const params = { ...this.queryParams }
console.log('🚀 ~ 导出 ~ params:', params)
this.download(url, params, fileName)
} catch (error) {
console.log('导出数据失败', error)
}
},
handleAddData() {
console.log('新增')
this.dialogConfig.outerTitle = '新增班组'
this.dialogConfig.outerVisible = true
},
handleEditData() {
this.dialogConfig.outerTitle = '编辑班组'
this.dialogConfig.outerVisible = true
},
handleDeleteData(id) {
console.log('🚀 ~ handleDeleteData ~ id:', id)
this.$confirm('是否删除该数据?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(async () => {
// const params = { }
// const res = await
this.$refs.tableModel.getTableList()
this.$message({
type: 'success',
message: '删除成功!',
})
})
},
/** 关闭外侧弹框 */
closeDialog() {
this.dialogConfig.outerVisible = false
this.$refs.tableModel.getTableList()
},
// -
onSubmitDialog() {
this.$refs.addEditForm.onSubmitForm()
},
},
}
</script>
<style></style>

View File

@ -0,0 +1,357 @@
<template>
<!-- 基础页面 -->
<div class="app-container">
<el-form v-show="showSearch" :model="queryParams" ref="queryForm" size="small" inline @submit.native.prevent>
<el-form-item prop="keyWord">
<el-input
v-model="queryParams.keyWord"
placeholder="请输入关键字"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item prop="work">
<el-select v-model="queryParams.work" placeholder="请选择工种/岗位" clearable>
<el-option v-for="item in workOpts" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
<el-option v-for="item in statusOpts" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 表单按钮 -->
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">查询</el-button>
<el-button icon="el-icon-refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="" plain icon="el-icon-download" size="mini" @click="handleExport">导出</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd">新增</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table :data="tableList" fit highlight-current-row style="width: 100%">
<el-table-column
type="index"
width="55"
label="序号"
align="center"
:index="(index) => (queryParams.pageNum - 1) * queryParams.pageSize + index + 1"
/>
<el-table-column
v-for="(column, index) in tableColumns"
show-overflow-tooltip
:key="index"
:label="column.label"
:prop="column.prop"
align="center"
>
<!-- 插槽 -->
<template v-slot="{ row }" v-if="column.prop == 'status'">
<el-tag v-if="row.status == 1" size="mini" type="success">在场</el-tag>
<el-tag v-else-if="row.status == 2" size="mini" type="warning">休息</el-tag>
<el-tag v-else-if="row.status == 3" size="mini" type="danger">离场</el-tag>
</template>
<template v-slot="{ row }" v-else-if="column.prop == 'isKey'">
<span v-if="row.isKey == 1"></span>
<span v-else-if="row.isKey == 0"></span>
</template>
<template v-slot="{ row }" v-else-if="column.prop == 'facePhoto'">
<el-image
v-if="row.facePhoto"
style="width: 30px; height: 30px; border-radius: 5px"
:src="row.facePhoto"
fit="cover"
:preview-src-list="[row.facePhoto]"
></el-image>
<span v-else>未上传</span>
</template>
</el-table-column>
<!-- 操作 -->
<el-table-column label="操作" align="center" width="280">
<template slot-scope="{ row }">
<el-button type="primary" size="mini" icon="el-icon-right" @click="handleDetails(row)">出入场记录</el-button>
<el-dropdown size="mini">
<el-button size="mini" type="" icon="el-icon-arrow-down">更多</el-button>
<el-dropdown-menu slot="dropdown" style="width: 90px; text-align: center">
<el-dropdown-item
><span @click="handleEdit(row)" style="color: #409eff"
><i class="el-icon-edit"></i>修改</span
></el-dropdown-item
>
<el-dropdown-item
><span @click="handleRest(row)" style="color: #e6a23c"
><i class="el-icon-s-tools"></i>休息</span
></el-dropdown-item
>
<el-dropdown-item
><span @click="handleOut(row)" style="color: #f56c6c"
><i class="el-icon-s-tools"></i>出场</span
></el-dropdown-item
>
<el-dropdown-item
><span @click="handleDelete(row)" style="color: #f56c6c"
><i class="el-icon-delete"></i>删除</span
></el-dropdown-item
>
</el-dropdown-menu>
</el-dropdown>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination
v-show="total > 0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 弹框 -->
<el-dialog title="人员信息" :visible.sync="dialogVisible" width="50%">
<AddEditPersonnel v-if="dialogVisible" ref="dialogForm" @closeDialog="closeDialog"></AddEditPersonnel>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="dialogConfirm"> </el-button>
</span>
</el-dialog>
<el-dialog title="出入场记录" :visible.sync="dialogDetails" width="50%">
<OutInDetails v-if="dialogDetails" ref="dialogDetails"></OutInDetails>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogDetails = false"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import AddEditPersonnel from './components/AddEditPersonnel'
import OutInDetails from './components/OutInDetails'
export default {
components: {
AddEditPersonnel,
OutInDetails,
},
data() {
return {
showSearch: true,
queryParams: {
pageNum: 1,
pageSize: 10,
keyWord: '', //
work: '', //
status: '', //
},
total: 0, //
workOpts: [
{ label: '工种1', value: '1' },
{ label: '工种2', value: '2' },
{ label: '工种3', value: '3' },
], //
statusOpts: [
{ label: '新建', value: '0' },
{ label: '进行中', value: '1' },
{ label: '已完成', value: '2' },
{ label: '已关闭', value: '3' },
], //
//
tableColumns: [
{ label: '姓名', prop: 'userName' },
{ label: '身份证号', prop: 'idCard' },
{ label: '联系方式', prop: 'phone' },
{ label: '民族', prop: 'nation' },
{ label: '工种/岗位', prop: 'work' },
{ label: '所在工程', prop: 'projectName' },
{ label: '是否关键人员', prop: 'isKey' },
{ label: '人员状态', prop: 'status' },
{ label: '人脸照片', prop: 'facePhoto' },
],
//
tableList: [
{
userName: '张三',
idCard: '123456789012345678',
phone: '13800138000',
nation: '汉族',
work: '工种1',
projectName: '工程1',
isKey: '1',
status: '1',
facePhoto: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg',
},
{
userName: '李四',
idCard: '123456789012345679',
phone: '13800138001',
nation: '汉族',
work: '工种2',
projectName: '工程2',
isKey: '0',
status: '2',
facePhoto: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg',
},
{
userName: '王五',
idCard: '123456789012345680',
phone: '13800138002',
nation: '汉族',
work: '工种3',
projectName: '工程3',
isKey: '1',
status: '3',
facePhoto: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg',
},
],
dialogVisible: false,
dialogDetails: false,
}
},
created() {
this.getList()
},
methods: {
//
handleQuery() {
this.getList()
},
//
handleReset() {
this.queryParams.pageNum = 1
this.queryParams.pageSize = 10
this.$refs.queryForm.resetFields()
this.getList()
},
//
async getList() {
console.log('列表-查询', this.queryParams)
const loading = this.$loading({ text: '加载中...' })
try {
const params = { ...this.queryParams }
// const res = await
// console.log('🚀 ~ ~ res:', res)
// this.tableList = res.rows
// this.total = res.total
loading.close()
} catch (error) {
console.log('🚀 ~ 获取列表 ~ error:', error)
this.tableList = []
this.total = 0
loading.close()
}
},
handleAdd() {
console.log('新增')
this.dialogVisible = true
},
//
handleDetails(row) {
console.log('出入场记录', row)
this.dialogDetails = true
this.$nextTick(() => {
// this.$refs.dialogDetails.handleDetails(row)
})
},
//
handleEdit(row) {
console.log('编辑', row)
this.dialogVisible = true
this.$nextTick(() => {
this.$refs.dialogForm.handleEdit(row)
})
},
//
handleRest(row) {
console.log('休息', row)
this.$confirm('', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
message:
'<span style="font-weight: bold">是否确定设置休息?</span><br /><span>设置人员状态: 结束休息? 状态改为在场状态</span>',
dangerouslyUseHTMLString: true,
type: 'warning',
}).then(async () => {
// const params = { }
// const res = await
this.$message({
type: 'success',
message: '操作成功!',
})
})
},
//
handleOut(row) {
console.log('出场', row)
this.$confirm('', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
message: '<span style="font-weight: bold">是否确定出场?</span><br /><span>出场自动解绑工程, 请确认操作</span>',
dangerouslyUseHTMLString: true,
type: 'warning',
}).then(async () => {
// const params = { }
// const res = await
this.$message({
type: 'success',
message: '操作成功!',
})
})
},
//
handleDelete(row) {
console.log('删除', row)
this.$confirm('是否确定删除?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(async () => {
// const params = { }
// const res = await
this.$message({
type: 'success',
message: '删除成功!',
})
})
},
//
handleExport() {
this.$message({
type: 'warning',
message: '导出功能开发中,敬请期待!',
})
return
try {
let fileName = `人员出入场_${new Date().getTime()}.xLsx`
let url = ''
const params = { ...this.queryParams }
console.log('🚀 ~ 导出 ~ params:', params)
this.download(url, params, fileName)
} catch (error) {
console.log('导出数据失败', error)
}
},
//
dialogConfirm() {
console.log('dialogConfirm')
this.$refs.dialogForm.onSubmitForm()
},
//
closeDialog() {
this.dialogVisible = false
this.getList()
},
},
}
</script>
<style lang="scss" scoped></style>

View File

@ -36,7 +36,7 @@ module.exports = {
// detail: https://cli.vuejs.org/config/#devserver-proxy
[process.env.VUE_APP_BASE_API]: {
// target: `http://192.168.2.209:38080`,
target: `http://192.168.2.76:38080`,
target: `http://192.168.0.60:38080`,
// target: `http://192.168.0.244:18877`,
changeOrigin: true,
pathRewrite: {