ren
This commit is contained in:
919
src/views/professional/outercompanyemployee/index.vue
Executable file
919
src/views/professional/outercompanyemployee/index.vue
Executable file
@@ -0,0 +1,919 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<!-- 搜索表单 -->
|
||||
<el-row shadow="hover" v-show="showSearch" class="ml10">
|
||||
<el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="handleFilter">
|
||||
<el-form-item label="单位名称" prop="companyId">
|
||||
<el-select
|
||||
v-model="state.queryForm.companyId"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="请选择单位"
|
||||
style="width: 200px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in companyList"
|
||||
:key="item.id"
|
||||
:label="item.companyName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="职员编号" prop="employeeNo">
|
||||
<el-input
|
||||
v-model="state.queryForm.employeeNo"
|
||||
placeholder="请输入职员编号"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="姓名" prop="realName">
|
||||
<el-input
|
||||
v-model="state.queryForm.realName"
|
||||
placeholder="请输入姓名"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证" prop="idCard">
|
||||
<el-input
|
||||
v-model="state.queryForm.idCard"
|
||||
placeholder="请输入身份证"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机" prop="mobile">
|
||||
<el-input
|
||||
v-model="state.queryForm.mobile"
|
||||
placeholder="请输入手机"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="允许进出" prop="inoutFlag">
|
||||
<el-select
|
||||
v-model="state.queryForm.inoutFlag"
|
||||
clearable
|
||||
placeholder="请选择"
|
||||
style="width: 200px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in yesNoDict"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button icon="search" type="primary" @click="handleFilter">查询</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-row>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<el-row>
|
||||
<div class="mb15" style="width: 100%;">
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="FolderAdd"
|
||||
@click="handleAdd"
|
||||
v-if="permissions.professional_outercompanyemployee_add">新 增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handleExportIn"
|
||||
v-if="permission.scope == '1'"
|
||||
>导 入
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
@click="handleExportScore"
|
||||
:loading="exportLoading"
|
||||
icon="Download">导出
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="batchDelect">批量删除
|
||||
</el-button>
|
||||
<right-toolbar
|
||||
v-model:showSearch="showSearch"
|
||||
class="ml10"
|
||||
style="float: right; margin-right: 20px"
|
||||
@queryTable="getDataList"
|
||||
></right-toolbar>
|
||||
</div>
|
||||
</el-row>
|
||||
|
||||
<!-- 表格 -->
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
:data="state.dataList"
|
||||
v-loading="state.loading"
|
||||
border
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
|
||||
<el-table-column prop="companyName" label="单位名称" min-width="150" align="center" show-overflow-tooltip />
|
||||
|
||||
<el-table-column prop="employeeNo" label="职员编号" min-width="120" align="center" show-overflow-tooltip />
|
||||
|
||||
<el-table-column prop="realName" label="姓名" min-width="100" align="center" show-overflow-tooltip />
|
||||
|
||||
<el-table-column prop="idCard" label="身份证" min-width="180" align="center" show-overflow-tooltip />
|
||||
|
||||
<el-table-column prop="mobile" label="手机" min-width="120" align="center" show-overflow-tooltip />
|
||||
|
||||
<el-table-column prop="inoutFlag" label="允许进出" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.inoutFlag">{{ getDictLabel(scope.row.inoutFlag) }}</el-tag>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="头像" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<img
|
||||
width="50px"
|
||||
height="50px"
|
||||
@click="handlePictureCardPreview(scope.row.employeeNo)"
|
||||
:src="getImageView(scope.row.employeeNo)"
|
||||
@error="handleImageError"
|
||||
style="cursor: pointer; object-fit: cover; border-radius: 4px;"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="updateTime" label="更新时间" width="180" align="center" />
|
||||
|
||||
<el-table-column label="操作" width="250" align="center" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-if="permissions.professional_outercompanyemployee_edit"
|
||||
icon="edit-pen"
|
||||
link
|
||||
type="primary"
|
||||
@click="handleEdit(scope.row)">修改
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="permissions.professional_outercompanyemployee_reset_pw"
|
||||
icon="RefreshLeft"
|
||||
link
|
||||
type="primary"
|
||||
style="margin-left: 12px"
|
||||
@click="resetPassword(scope.row)">重置密码
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="permissions.professional_outercompanyemployee_del"
|
||||
icon="delete"
|
||||
link
|
||||
type="primary"
|
||||
style="margin-left: 12px"
|
||||
@click="handleDel(scope.row)">删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<pagination
|
||||
v-bind="state.pagination"
|
||||
@current-change="currentChangeHandle"
|
||||
@size-change="sizeChangeHandle"
|
||||
/>
|
||||
|
||||
<!-- 新增/编辑弹窗 -->
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="form.id ? '编辑' : '新增'"
|
||||
width="800px"
|
||||
:close-on-click-modal="false"
|
||||
destroy-on-close
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="formRules"
|
||||
label-width="120px"
|
||||
>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="单位名称" prop="companyId">
|
||||
<el-select
|
||||
v-model="form.companyId"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="请选择单位"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in companyList"
|
||||
:key="item.id"
|
||||
:label="item.companyName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="职员编号" prop="employeeNo">
|
||||
<el-input
|
||||
v-model="form.employeeNo"
|
||||
placeholder="系统自动生成"
|
||||
disabled
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="姓名" prop="realName">
|
||||
<el-input
|
||||
v-model="form.realName"
|
||||
placeholder="请输入姓名"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="身份证" prop="idCard">
|
||||
<el-input
|
||||
v-model="form.idCard"
|
||||
placeholder="请输入身份证号"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="手机" prop="mobile">
|
||||
<el-input
|
||||
v-model="form.mobile"
|
||||
placeholder="请输入手机号"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="职位" prop="position">
|
||||
<el-input
|
||||
v-model="form.position"
|
||||
placeholder="请输入职位"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="家庭地址" prop="address">
|
||||
<el-input
|
||||
v-model="form.address"
|
||||
placeholder="请输入家庭地址"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="允许进出" prop="inoutFlag">
|
||||
<el-select
|
||||
v-model="form.inoutFlag"
|
||||
clearable
|
||||
placeholder="请选择"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in yesNoDict"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="备注" prop="remarks">
|
||||
<el-input
|
||||
v-model="form.remarks"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请输入备注"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit" :loading="submitLoading">确定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 头像预览对话框 -->
|
||||
<el-dialog v-model="dialogUploadVisible" title="头像预览" append-to-body>
|
||||
<img width="100%" :src="dialogImageUrl" alt="" style="max-height: 600px; object-fit: contain;" />
|
||||
</el-dialog>
|
||||
|
||||
<!-- 上传头像对话框 -->
|
||||
<el-dialog v-model="dialogAvatarVisible" title="上传头像" width="50%" append-to-body>
|
||||
<el-upload
|
||||
action="/basic/basicstudent/uploadAvatarBase64"
|
||||
list-type="picture-card"
|
||||
name="file"
|
||||
:headers="headers"
|
||||
:limit="1"
|
||||
:data="uploadData"
|
||||
:file-list="fileList"
|
||||
:before-upload="beforeUpload"
|
||||
:on-remove="removeHandler"
|
||||
:http-request="httpRequest"
|
||||
:on-success="uploadSuccess"
|
||||
>
|
||||
<el-icon><Plus /></el-icon>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">上传头像,人脸识别用</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 导入对话框 -->
|
||||
<el-dialog v-model="dialogViewVisible" title="导入文件" append-to-body>
|
||||
<el-upload
|
||||
class="upload-container"
|
||||
ref="uploadFormRef"
|
||||
action="doUpload"
|
||||
:limit="1"
|
||||
:file-list="filesList"
|
||||
:before-upload="fileUpload"
|
||||
:auto-upload="false"
|
||||
>
|
||||
<template #trigger>
|
||||
<el-button type="primary">选取文件</el-button>
|
||||
</template>
|
||||
<a href="outercomanyemployee.xlsx" rel="external nofollow" download="模板" style="margin-left: 20px">
|
||||
<el-button type="success">下载模板</el-button>
|
||||
</a>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">只能上传excel文件,且不超过5MB</div>
|
||||
<div class="el-upload__tip">{{ fileName }}</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="dialogViewVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitUpload">导入</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useUserInfo } from '/@/stores/userInfo'
|
||||
import { BasicTableProps, useTable } from '/@/hooks/table'
|
||||
import { useMessage } from '/@/hooks/message'
|
||||
import { useMessageBox } from '/@/hooks/message'
|
||||
import { useDict } from '/@/hooks/dict'
|
||||
import { Session } from '/@/utils/storage'
|
||||
import { validateNull } from '/@/utils/validate'
|
||||
import axios from 'axios'
|
||||
import request from '/@/utils/request'
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
import {
|
||||
fetchList,
|
||||
getObj,
|
||||
addObj,
|
||||
putObj,
|
||||
delObj,
|
||||
batchDel,
|
||||
resetPassWord
|
||||
} from '/@/api/professional/stayschool/outercompanyemployee'
|
||||
import { getList as getCompanyList } from '/@/api/professional/stayschool/outercompany'
|
||||
|
||||
// 使用 Pinia store
|
||||
const userInfoStore = useUserInfo()
|
||||
const { userInfos } = storeToRefs(userInfoStore)
|
||||
|
||||
// 创建权限对象
|
||||
const permissions = computed(() => {
|
||||
const perms: Record<string, boolean> = {}
|
||||
userInfos.value.authBtnList.forEach((perm: string) => {
|
||||
perms[perm] = true
|
||||
})
|
||||
return perms
|
||||
})
|
||||
|
||||
// 消息提示 hooks
|
||||
const message = useMessage()
|
||||
const messageBox = useMessageBox()
|
||||
|
||||
// 字典数据
|
||||
const { yes_no: yesNoDict } = useDict('yes_no')
|
||||
|
||||
// 获取字典标签的辅助函数
|
||||
const getDictLabel = (value: string | number) => {
|
||||
const item = yesNoDict.value.find((i: any) => i.value === value)
|
||||
return item ? item.label : ''
|
||||
}
|
||||
|
||||
// 表格引用
|
||||
const tableRef = ref()
|
||||
const formRef = ref()
|
||||
const queryRef = ref()
|
||||
const uploadFormRef = ref()
|
||||
|
||||
// 搜索显示
|
||||
const showSearch = ref(true)
|
||||
|
||||
// 弹窗状态
|
||||
const dialogVisible = ref(false)
|
||||
const dialogUploadVisible = ref(false)
|
||||
const dialogAvatarVisible = ref(false)
|
||||
const dialogViewVisible = ref(false)
|
||||
const submitLoading = ref(false)
|
||||
const exportLoading = ref(false)
|
||||
|
||||
// 选中的行数据
|
||||
const selectList = ref<any[]>([])
|
||||
const permission = reactive({
|
||||
hasPermission: "0",
|
||||
scope: "0"
|
||||
})
|
||||
|
||||
// 单位列表
|
||||
const companyList = ref<any[]>([])
|
||||
|
||||
// 表单数据
|
||||
const form = reactive({
|
||||
id: '',
|
||||
companyId: '',
|
||||
companyName: '',
|
||||
employeeNo: '',
|
||||
realName: '',
|
||||
idCard: '',
|
||||
mobile: '',
|
||||
position: '',
|
||||
address: '',
|
||||
inoutFlag: '',
|
||||
remarks: ''
|
||||
})
|
||||
|
||||
// 表单验证规则
|
||||
const formRules = {
|
||||
companyId: [
|
||||
{ required: true, message: '请选择单位', trigger: 'change' }
|
||||
],
|
||||
realName: [
|
||||
{ required: true, message: '请填写姓名', trigger: 'blur' }
|
||||
],
|
||||
idCard: [
|
||||
{ required: true, message: '请填写身份证号', trigger: 'blur' }
|
||||
],
|
||||
mobile: [
|
||||
{ required: true, message: '请填写手机号', trigger: 'blur' }
|
||||
],
|
||||
address: [
|
||||
{ required: true, message: '请填写家庭住址', trigger: 'blur' }
|
||||
],
|
||||
inoutFlag: [
|
||||
{ required: true, message: '请选择是否允许进出', trigger: 'change' }
|
||||
]
|
||||
}
|
||||
|
||||
// 头像相关
|
||||
const dialogImageUrl = ref('')
|
||||
const fileList = ref<any[]>([])
|
||||
const rowData = ref<any>({})
|
||||
const fileReader = ref<FileReader | null>(null)
|
||||
|
||||
// 上传相关
|
||||
const uploadData = reactive({
|
||||
bucketName: "base",
|
||||
module: "basic",
|
||||
username: ""
|
||||
})
|
||||
|
||||
// 导入相关
|
||||
const fileName = ref('')
|
||||
const filesList = ref<any[]>([])
|
||||
let files: File | null = null
|
||||
|
||||
// 请求头
|
||||
const headers = computed(() => {
|
||||
return {
|
||||
"Authorization": 'Bearer ' + Session.getToken()
|
||||
}
|
||||
})
|
||||
|
||||
// 配置 useTable - 注意这个 API 返回的数据结构特殊
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
pageList: async (params: any) => {
|
||||
const response = await fetchList({
|
||||
...params,
|
||||
companyType: '0'
|
||||
})
|
||||
// 特殊处理:API 返回的是 response.data.data.dataList.records
|
||||
const dataList = response.data?.data?.dataList || response.data?.dataList || {}
|
||||
permission.hasPermission = response.data?.data?.permission?.hasPermission || "0"
|
||||
permission.scope = response.data?.data?.permission?.scope || "0"
|
||||
return {
|
||||
data: {
|
||||
records: dataList.records || [],
|
||||
total: dataList.total || 0
|
||||
}
|
||||
}
|
||||
},
|
||||
queryForm: {
|
||||
companyId: '',
|
||||
employeeNo: '',
|
||||
realName: '',
|
||||
idCard: '',
|
||||
mobile: '',
|
||||
inoutFlag: ''
|
||||
}
|
||||
})
|
||||
|
||||
const { getDataList, currentChangeHandle, sizeChangeHandle, tableStyle } = useTable(state)
|
||||
|
||||
// 获取单位列表
|
||||
const loadCompanyList = async () => {
|
||||
try {
|
||||
const response = await getCompanyList({ companyType: '0' })
|
||||
companyList.value = response.data || []
|
||||
} catch (error) {
|
||||
// 获取单位列表失败
|
||||
}
|
||||
}
|
||||
|
||||
// 重置查询
|
||||
const resetQuery = () => {
|
||||
queryRef.value?.resetFields()
|
||||
getDataList()
|
||||
}
|
||||
|
||||
// 处理搜索
|
||||
const handleFilter = () => {
|
||||
getDataList()
|
||||
}
|
||||
|
||||
// 多选变化
|
||||
const handleSelectionChange = (selection: any[]) => {
|
||||
selectList.value = selection
|
||||
}
|
||||
|
||||
// 打开新增窗口
|
||||
const handleAdd = () => {
|
||||
Object.assign(form, {
|
||||
id: '',
|
||||
companyId: '',
|
||||
companyName: '',
|
||||
employeeNo: '',
|
||||
realName: '',
|
||||
idCard: '',
|
||||
mobile: '',
|
||||
position: '',
|
||||
address: '',
|
||||
inoutFlag: '',
|
||||
remarks: ''
|
||||
})
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 打开编辑窗口
|
||||
const handleEdit = async (row: any) => {
|
||||
try {
|
||||
const response = await getObj(row.id)
|
||||
const data = response.data
|
||||
Object.assign(form, {
|
||||
id: data.id,
|
||||
companyId: data.companyId || '',
|
||||
companyName: data.companyName || '',
|
||||
employeeNo: data.employeeNo || '',
|
||||
realName: data.realName || '',
|
||||
idCard: data.idCard || '',
|
||||
mobile: data.mobile || '',
|
||||
position: data.position || '',
|
||||
address: data.address || '',
|
||||
inoutFlag: data.inoutFlag || '',
|
||||
remarks: data.remarks || ''
|
||||
})
|
||||
dialogVisible.value = true
|
||||
} catch (error) {
|
||||
// 获取详情失败
|
||||
}
|
||||
}
|
||||
|
||||
// 删除
|
||||
const handleDel = (row: any) => {
|
||||
messageBox.confirm('是否确认删除该条记录').then(async () => {
|
||||
await delObj(row.id)
|
||||
message.success('删除成功')
|
||||
// 如果当前页只剩一条数据,且不是第一页,则跳转到上一页
|
||||
if (state.pagination && state.dataList && state.dataList.length === 1 && state.pagination.current && state.pagination.current > 1) {
|
||||
state.pagination.current = state.pagination.current - 1
|
||||
}
|
||||
getDataList()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
// 批量删除
|
||||
const batchDelect = () => {
|
||||
if (selectList.value.length === 0) {
|
||||
message.warning('请至少选择一条数据')
|
||||
return
|
||||
}
|
||||
messageBox.confirm('是否确认删除').then(async () => {
|
||||
await batchDel(selectList.value)
|
||||
message.success('删除成功')
|
||||
selectList.value = []
|
||||
getDataList()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
// 重置密码
|
||||
const resetPassword = (row: any) => {
|
||||
messageBox.confirm('是否确定重置密码?', '提示').then(async () => {
|
||||
try {
|
||||
const response = await resetPassWord(row)
|
||||
const pw = response.data?.data
|
||||
if (!validateNull(pw)) {
|
||||
messageBox.alert('重置后密码为: ' + pw, '重置密码')
|
||||
} else {
|
||||
messageBox.alert('系统繁忙,请重试', '重置密码')
|
||||
}
|
||||
} catch (error) {
|
||||
// 重置密码失败
|
||||
}
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
if (!formRef.value) return
|
||||
|
||||
await formRef.value.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
submitLoading.value = true
|
||||
try {
|
||||
// 设置单位名称
|
||||
const selectedCompany = companyList.value.find(item => item.id === form.companyId)
|
||||
if (selectedCompany) {
|
||||
form.companyName = selectedCompany.companyName
|
||||
}
|
||||
|
||||
if (form.id) {
|
||||
await putObj(form)
|
||||
message.success('修改成功')
|
||||
} else {
|
||||
await addObj(form)
|
||||
message.success('添加成功')
|
||||
}
|
||||
dialogVisible.value = false
|
||||
getDataList()
|
||||
} catch (error: any) {
|
||||
message.error(error?.msg || '操作失败')
|
||||
} finally {
|
||||
submitLoading.value = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 获取图片 URL
|
||||
const getImageView = (employeeNo: string) => {
|
||||
const timestamp = Date.parse(new Date().toString())
|
||||
const baseUrl = import.meta.env.VITE_API_URL || ''
|
||||
return `${baseUrl}/admin/user/photo/${employeeNo}?${timestamp}`
|
||||
}
|
||||
|
||||
// 图片加载错误处理
|
||||
const handleImageError = (event: Event) => {
|
||||
const img = event.target as HTMLImageElement
|
||||
img.src = '/img/default/no_pic.png'
|
||||
}
|
||||
|
||||
// 预览头像
|
||||
const handlePictureCardPreview = (employeeNo: string) => {
|
||||
dialogImageUrl.value = getImageView(employeeNo)
|
||||
dialogUploadVisible.value = true
|
||||
}
|
||||
|
||||
// 上传头像对话框
|
||||
const uploadAvatarDialog = (row: any) => {
|
||||
rowData.value = row
|
||||
fileList.value = []
|
||||
uploadData.username = row.employeeNo
|
||||
dialogAvatarVisible.value = true
|
||||
}
|
||||
|
||||
// 上传前验证
|
||||
const beforeUpload = (file: File) => {
|
||||
const isLt5M = file.size < 1 * 1024 * 1024
|
||||
if (fileList.value.length >= 1) {
|
||||
message.warning('只能上传一张头像')
|
||||
return false
|
||||
}
|
||||
if (!isLt5M) {
|
||||
message.warning('文件大小不能超过1M')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 自定义上传
|
||||
const httpRequest = (options: any) => {
|
||||
const file = options.file
|
||||
if (!fileReader.value) {
|
||||
fileReader.value = new FileReader()
|
||||
}
|
||||
|
||||
if (file) {
|
||||
fileReader.value.readAsDataURL(file)
|
||||
}
|
||||
|
||||
fileReader.value.onload = () => {
|
||||
const base64Str = fileReader.value?.result as string
|
||||
|
||||
const config = {
|
||||
url: '/basic/basicstudent/uploadAvatarBase64',
|
||||
method: 'post',
|
||||
data: {
|
||||
base64Str: base64Str.split(',')[1],
|
||||
username: rowData.value.employeeNo
|
||||
},
|
||||
timeout: 10000,
|
||||
onUploadProgress: (progressEvent: any) => {
|
||||
progressEvent.percent = progressEvent.loaded / progressEvent.total * 100
|
||||
options.onProgress(progressEvent, file)
|
||||
}
|
||||
}
|
||||
|
||||
axios(config)
|
||||
.then(res => {
|
||||
options.onSuccess(res, file)
|
||||
getDataList()
|
||||
})
|
||||
.catch(err => {
|
||||
options.onError(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 移除文件
|
||||
const removeHandler = (file: any) => {
|
||||
const index = fileList.value.findIndex(f => f.uid === file.uid)
|
||||
if (index !== -1) {
|
||||
fileList.value.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// 上传成功
|
||||
const uploadSuccess = (res: any, file: any) => {
|
||||
if (res.data) {
|
||||
const data = res.data
|
||||
file.key = data.key
|
||||
fileList.value.push(file)
|
||||
message.success('上传成功')
|
||||
dialogAvatarVisible.value = false
|
||||
getDataList()
|
||||
}
|
||||
}
|
||||
|
||||
// 导入
|
||||
const handleExportIn = () => {
|
||||
fileName.value = ""
|
||||
filesList.value = []
|
||||
files = null
|
||||
dialogViewVisible.value = true
|
||||
}
|
||||
|
||||
// 文件上传验证
|
||||
const fileUpload = (file: File) => {
|
||||
const fileLast = file.name.split('.')
|
||||
const extension = fileLast[fileLast.length - 1] === 'xls'
|
||||
const extension2 = fileLast[fileLast.length - 1] === 'xlsx'
|
||||
const isLt2M = file.size / 1024 / 1024 < 5
|
||||
|
||||
if (!extension && !extension2) {
|
||||
message.warning('上传模板只能是 xls、xlsx格式!')
|
||||
return false
|
||||
}
|
||||
if (!isLt2M) {
|
||||
message.warning('上传模板大小不能超过 5MB!')
|
||||
return false
|
||||
}
|
||||
fileName.value = file.name
|
||||
files = file
|
||||
return false // 返回false不会自动上传
|
||||
}
|
||||
|
||||
// 提交导入
|
||||
const submitUpload = async () => {
|
||||
if (!fileName.value || !files) {
|
||||
message.warning('请选择要上传的文件!')
|
||||
return
|
||||
}
|
||||
|
||||
const fileFormData = new FormData()
|
||||
fileFormData.append('file', files, fileName.value)
|
||||
|
||||
try {
|
||||
const response = await request({
|
||||
url: `/professional/file/exportOuterCompanyEmployee?companyType=0`,
|
||||
method: 'post',
|
||||
data: fileFormData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
|
||||
if (response.code === 0) {
|
||||
message.success('操作成功')
|
||||
dialogViewVisible.value = false
|
||||
getDataList()
|
||||
} else {
|
||||
message.error(response.msg || '导入失败')
|
||||
}
|
||||
} catch (error: any) {
|
||||
message.error(error?.msg || '导入失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 导出
|
||||
const handleExportScore = async () => {
|
||||
if (!state.queryForm || Object.keys(state.queryForm).length === 0) {
|
||||
message.warning('请选择导出条件')
|
||||
return
|
||||
}
|
||||
|
||||
exportLoading.value = true
|
||||
try {
|
||||
const params = {
|
||||
...state.queryForm,
|
||||
companyType: "0"
|
||||
}
|
||||
|
||||
const response = await axios({
|
||||
method: 'post',
|
||||
url: '/professional/outercompanyemployee/export',
|
||||
data: params,
|
||||
responseType: 'blob',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
const blob = new Blob([response.data])
|
||||
const fileName = '驻校单位人员导出表.xls'
|
||||
const elink = document.createElement('a')
|
||||
elink.download = fileName
|
||||
elink.style.display = 'none'
|
||||
elink.href = URL.createObjectURL(blob)
|
||||
document.body.appendChild(elink)
|
||||
elink.click()
|
||||
URL.revokeObjectURL(elink.href)
|
||||
document.body.removeChild(elink)
|
||||
message.success('导出成功')
|
||||
} catch (error: any) {
|
||||
message.error(error?.msg || '导出失败')
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
if (!window.FileReader) {
|
||||
console.error('Your browser does not support FileReader API!')
|
||||
} else {
|
||||
fileReader.value = new FileReader()
|
||||
}
|
||||
loadCompanyList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.upload-container {
|
||||
:deep(.el-upload) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
913
src/views/professional/outercompanyemployee/indexSecond.vue
Executable file
913
src/views/professional/outercompanyemployee/indexSecond.vue
Executable file
@@ -0,0 +1,913 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<!-- 搜索表单 -->
|
||||
<el-row shadow="hover" v-show="showSearch" class="ml10">
|
||||
<el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="handleFilter">
|
||||
<el-form-item label="单位名称" prop="companyId">
|
||||
<el-select
|
||||
v-model="state.queryForm.companyId"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="请选择单位"
|
||||
style="width: 200px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in companyList"
|
||||
:key="item.id"
|
||||
:label="item.companyName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="职员编号" prop="employeeNo">
|
||||
<el-input
|
||||
v-model="state.queryForm.employeeNo"
|
||||
placeholder="请输入职员编号"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="姓名" prop="realName">
|
||||
<el-input
|
||||
v-model="state.queryForm.realName"
|
||||
placeholder="请输入姓名"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证" prop="idCard">
|
||||
<el-input
|
||||
v-model="state.queryForm.idCard"
|
||||
placeholder="请输入身份证"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机" prop="mobile">
|
||||
<el-input
|
||||
v-model="state.queryForm.mobile"
|
||||
placeholder="请输入手机"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="允许进出" prop="inoutFlag">
|
||||
<el-select
|
||||
v-model="state.queryForm.inoutFlag"
|
||||
clearable
|
||||
placeholder="请选择"
|
||||
style="width: 200px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in yesNoDict"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button icon="search" type="primary" @click="handleFilter">查询</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-row>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<el-row>
|
||||
<div class="mb15" style="width: 100%;">
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="FolderAdd"
|
||||
@click="handleAdd"
|
||||
v-if="permissions.professional_outercompanyemployee_add">新 增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handleExportIn"
|
||||
v-if="permission.scope == '1'"
|
||||
>导 入
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
@click="handleExportScore"
|
||||
:loading="exportLoading"
|
||||
icon="Download">导出
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="batchDelect">批量删除
|
||||
</el-button>
|
||||
<right-toolbar
|
||||
v-model:showSearch="showSearch"
|
||||
class="ml10"
|
||||
style="float: right; margin-right: 20px"
|
||||
@queryTable="getDataList"
|
||||
></right-toolbar>
|
||||
</div>
|
||||
</el-row>
|
||||
|
||||
<!-- 表格 -->
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
:data="state.dataList"
|
||||
v-loading="state.loading"
|
||||
border
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
|
||||
<el-table-column prop="companyName" label="单位名称" min-width="150" align="center" show-overflow-tooltip />
|
||||
|
||||
<el-table-column prop="employeeNo" label="职员编号" min-width="120" align="center" show-overflow-tooltip />
|
||||
|
||||
<el-table-column prop="realName" label="姓名" min-width="100" align="center" show-overflow-tooltip />
|
||||
|
||||
<el-table-column prop="idCard" label="身份证" min-width="180" align="center" show-overflow-tooltip />
|
||||
|
||||
<el-table-column prop="mobile" label="手机" min-width="120" align="center" show-overflow-tooltip />
|
||||
|
||||
<el-table-column prop="inoutFlag" label="允许进出" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.inoutFlag">{{ getDictLabel(scope.row.inoutFlag) }}</el-tag>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="头像" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<img
|
||||
width="50px"
|
||||
height="50px"
|
||||
@click="handlePictureCardPreview(scope.row.employeeNo)"
|
||||
:src="getImageView(scope.row.employeeNo)"
|
||||
@error="handleImageError"
|
||||
style="cursor: pointer; object-fit: cover; border-radius: 4px;"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="updateTime" label="更新时间" width="180" align="center" />
|
||||
|
||||
<el-table-column label="操作" width="250" align="center" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-if="permissions.professional_outercompanyemployee_edit"
|
||||
icon="edit-pen"
|
||||
link
|
||||
type="primary"
|
||||
@click="handleEdit(scope.row)">修改
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="permissions.professional_outercompanyemployee_reset_pw"
|
||||
icon="RefreshLeft"
|
||||
link
|
||||
type="primary"
|
||||
style="margin-left: 12px"
|
||||
@click="resetPassword(scope.row)">重置密码
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="permissions.professional_outercompanyemployee_del"
|
||||
icon="delete"
|
||||
link
|
||||
type="primary"
|
||||
style="margin-left: 12px"
|
||||
@click="handleDel(scope.row)">删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<pagination
|
||||
v-bind="state.pagination"
|
||||
@current-change="currentChangeHandle"
|
||||
@size-change="sizeChangeHandle"
|
||||
/>
|
||||
|
||||
<!-- 新增/编辑弹窗 -->
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="form.id ? '编辑' : '新增'"
|
||||
width="800px"
|
||||
:close-on-click-modal="false"
|
||||
destroy-on-close
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="formRules"
|
||||
label-width="120px"
|
||||
>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="单位名称" prop="companyId">
|
||||
<el-select
|
||||
v-model="form.companyId"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="请选择单位"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in companyList"
|
||||
:key="item.id"
|
||||
:label="item.companyName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="职员编号" prop="employeeNo">
|
||||
<el-input
|
||||
v-model="form.employeeNo"
|
||||
placeholder="系统自动生成"
|
||||
disabled
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="姓名" prop="realName">
|
||||
<el-input
|
||||
v-model="form.realName"
|
||||
placeholder="请输入姓名"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="身份证" prop="idCard">
|
||||
<el-input
|
||||
v-model="form.idCard"
|
||||
placeholder="请输入身份证号"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="手机" prop="mobile">
|
||||
<el-input
|
||||
v-model="form.mobile"
|
||||
placeholder="请输入手机号"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="职位" prop="position">
|
||||
<el-input
|
||||
v-model="form.position"
|
||||
placeholder="请输入职位"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="家庭地址" prop="address">
|
||||
<el-input
|
||||
v-model="form.address"
|
||||
placeholder="请输入家庭地址"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="允许进出" prop="inoutFlag">
|
||||
<el-select
|
||||
v-model="form.inoutFlag"
|
||||
clearable
|
||||
placeholder="请选择"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in yesNoDict"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="备注" prop="remarks">
|
||||
<el-input
|
||||
v-model="form.remarks"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请输入备注"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit" :loading="submitLoading">确定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 头像预览对话框 -->
|
||||
<el-dialog v-model="dialogUploadVisible" title="头像预览" append-to-body>
|
||||
<img width="100%" :src="dialogImageUrl" alt="" style="max-height: 600px; object-fit: contain;" />
|
||||
</el-dialog>
|
||||
|
||||
<!-- 上传头像对话框 -->
|
||||
<el-dialog v-model="dialogAvatarVisible" title="上传头像" width="50%" append-to-body>
|
||||
<el-upload
|
||||
action="/basic/basicstudent/uploadAvatarBase64"
|
||||
list-type="picture-card"
|
||||
name="file"
|
||||
:headers="headers"
|
||||
:limit="1"
|
||||
:data="uploadData"
|
||||
:file-list="fileList"
|
||||
:before-upload="beforeUpload"
|
||||
:on-remove="removeHandler"
|
||||
:http-request="httpRequest"
|
||||
:on-success="uploadSuccess"
|
||||
>
|
||||
<el-icon><Plus /></el-icon>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">上传头像,人脸识别用</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 导入对话框 -->
|
||||
<el-dialog v-model="dialogViewVisible" title="导入文件" append-to-body>
|
||||
<el-upload
|
||||
class="upload-container"
|
||||
ref="uploadFormRef"
|
||||
action="doUpload"
|
||||
:limit="1"
|
||||
:file-list="filesList"
|
||||
:before-upload="fileUpload"
|
||||
:auto-upload="false"
|
||||
>
|
||||
<template #trigger>
|
||||
<el-button type="primary">选取文件</el-button>
|
||||
</template>
|
||||
<a href="outercomanyemployee.xlsx" rel="external nofollow" download="模板" style="margin-left: 20px">
|
||||
<el-button type="success">下载模板</el-button>
|
||||
</a>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">只能上传excel文件,且不超过5MB</div>
|
||||
<div class="el-upload__tip">{{ fileName }}</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="dialogViewVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitUpload">导入</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useUserInfo } from '/@/stores/userInfo'
|
||||
import { BasicTableProps, useTable } from '/@/hooks/table'
|
||||
import { useMessage } from '/@/hooks/message'
|
||||
import { useMessageBox } from '/@/hooks/message'
|
||||
import { useDict } from '/@/hooks/dict'
|
||||
import { Session } from '/@/utils/storage'
|
||||
import { validateNull } from '/@/utils/validate'
|
||||
import axios from 'axios'
|
||||
import request from '/@/utils/request'
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import {
|
||||
fetchList,
|
||||
getObj,
|
||||
saveSecond,
|
||||
putObj,
|
||||
delObj,
|
||||
batchDel,
|
||||
resetPassWord
|
||||
} from '/@/api/professional/stayschool/outercompanyemployee'
|
||||
import { getList as getCompanyList } from '/@/api/professional/stayschool/outercompany'
|
||||
|
||||
// 使用 Pinia store
|
||||
const userInfoStore = useUserInfo()
|
||||
const { userInfos } = storeToRefs(userInfoStore)
|
||||
|
||||
// 创建权限对象
|
||||
const permissions = computed(() => {
|
||||
const perms: Record<string, boolean> = {}
|
||||
userInfos.value.authBtnList.forEach((perm: string) => {
|
||||
perms[perm] = true
|
||||
})
|
||||
return perms
|
||||
})
|
||||
|
||||
// 消息提示 hooks
|
||||
const message = useMessage()
|
||||
const messageBox = useMessageBox()
|
||||
|
||||
// 字典数据
|
||||
const { yes_no: yesNoDict } = useDict('yes_no')
|
||||
|
||||
// 获取字典标签的辅助函数
|
||||
const getDictLabel = (value: string | number) => {
|
||||
const item = yesNoDict.value.find((i: any) => i.value === value)
|
||||
return item ? item.label : ''
|
||||
}
|
||||
|
||||
// 表格引用
|
||||
const tableRef = ref()
|
||||
const formRef = ref()
|
||||
const queryRef = ref()
|
||||
const uploadFormRef = ref()
|
||||
|
||||
// 搜索显示
|
||||
const showSearch = ref(true)
|
||||
|
||||
// 弹窗状态
|
||||
const dialogVisible = ref(false)
|
||||
const dialogUploadVisible = ref(false)
|
||||
const dialogAvatarVisible = ref(false)
|
||||
const dialogViewVisible = ref(false)
|
||||
const submitLoading = ref(false)
|
||||
const exportLoading = ref(false)
|
||||
|
||||
// 选中的行数据
|
||||
const selectList = ref<any[]>([])
|
||||
const permission = reactive({
|
||||
hasPermission: "0",
|
||||
scope: "0"
|
||||
})
|
||||
|
||||
// 单位列表
|
||||
const companyList = ref<any[]>([])
|
||||
|
||||
// 表单数据
|
||||
const form = reactive({
|
||||
id: '',
|
||||
companyId: '',
|
||||
companyName: '',
|
||||
employeeNo: '',
|
||||
realName: '',
|
||||
idCard: '',
|
||||
mobile: '',
|
||||
position: '',
|
||||
address: '',
|
||||
inoutFlag: '',
|
||||
remarks: ''
|
||||
})
|
||||
|
||||
// 表单验证规则
|
||||
const formRules = {
|
||||
companyId: [
|
||||
{ required: true, message: '请选择单位', trigger: 'change' }
|
||||
],
|
||||
realName: [
|
||||
{ required: true, message: '请填写姓名', trigger: 'blur' }
|
||||
],
|
||||
idCard: [
|
||||
{ required: true, message: '请填写身份证号', trigger: 'blur' }
|
||||
],
|
||||
mobile: [
|
||||
{ required: true, message: '请填写手机号', trigger: 'blur' }
|
||||
],
|
||||
address: [
|
||||
{ required: true, message: '请填写家庭住址', trigger: 'blur' }
|
||||
],
|
||||
inoutFlag: [
|
||||
{ required: true, message: '请选择是否允许进出', trigger: 'change' }
|
||||
]
|
||||
}
|
||||
|
||||
// 头像相关
|
||||
const dialogImageUrl = ref('')
|
||||
const fileList = ref<any[]>([])
|
||||
const rowData = ref<any>({})
|
||||
const fileReader = ref<FileReader | null>(null)
|
||||
|
||||
// 上传相关
|
||||
const uploadData = reactive({
|
||||
bucketName: "base",
|
||||
module: "basic",
|
||||
username: ""
|
||||
})
|
||||
|
||||
// 导入相关
|
||||
const fileName = ref('')
|
||||
const filesList = ref<any[]>([])
|
||||
let files: File | null = null
|
||||
|
||||
// 请求头
|
||||
const headers = computed(() => {
|
||||
return {
|
||||
"Authorization": 'Bearer ' + Session.getToken()
|
||||
}
|
||||
})
|
||||
|
||||
// 配置 useTable - 注意这个 API 返回的数据结构特殊
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
pageList: async (params: any) => {
|
||||
const response = await fetchList({
|
||||
...params,
|
||||
companyType: '2' // 二期单位
|
||||
})
|
||||
// 特殊处理:API 返回的是 response.data.data.dataList.records
|
||||
const dataList = response.data?.data?.dataList || response.data?.dataList || {}
|
||||
permission.hasPermission = response.data?.data?.permission?.hasPermission || "0"
|
||||
permission.scope = response.data?.data?.permission?.scope || "0"
|
||||
return {
|
||||
data: {
|
||||
records: dataList.records || [],
|
||||
total: dataList.total || 0
|
||||
}
|
||||
}
|
||||
},
|
||||
queryForm: {
|
||||
companyId: '',
|
||||
employeeNo: '',
|
||||
realName: '',
|
||||
idCard: '',
|
||||
mobile: '',
|
||||
inoutFlag: ''
|
||||
}
|
||||
})
|
||||
|
||||
const { getDataList, currentChangeHandle, sizeChangeHandle, tableStyle } = useTable(state)
|
||||
|
||||
// 获取单位列表
|
||||
const loadCompanyList = async () => {
|
||||
try {
|
||||
const response = await getCompanyList({ companyType: '2' })
|
||||
companyList.value = response.data || []
|
||||
} catch (error) {
|
||||
// 获取单位列表失败
|
||||
}
|
||||
}
|
||||
|
||||
// 重置查询
|
||||
const resetQuery = () => {
|
||||
queryRef.value?.resetFields()
|
||||
getDataList()
|
||||
}
|
||||
|
||||
// 处理搜索
|
||||
const handleFilter = () => {
|
||||
getDataList()
|
||||
}
|
||||
|
||||
// 多选变化
|
||||
const handleSelectionChange = (selection: any[]) => {
|
||||
selectList.value = selection
|
||||
}
|
||||
|
||||
// 打开新增窗口
|
||||
const handleAdd = () => {
|
||||
Object.assign(form, {
|
||||
id: '',
|
||||
companyId: '',
|
||||
companyName: '',
|
||||
employeeNo: '',
|
||||
realName: '',
|
||||
idCard: '',
|
||||
mobile: '',
|
||||
position: '',
|
||||
address: '',
|
||||
inoutFlag: '',
|
||||
remarks: ''
|
||||
})
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 打开编辑窗口
|
||||
const handleEdit = async (row: any) => {
|
||||
try {
|
||||
const response = await getObj(row.id)
|
||||
const data = response.data
|
||||
Object.assign(form, {
|
||||
id: data.id,
|
||||
companyId: data.companyId || '',
|
||||
companyName: data.companyName || '',
|
||||
employeeNo: data.employeeNo || '',
|
||||
realName: data.realName || '',
|
||||
idCard: data.idCard || '',
|
||||
mobile: data.mobile || '',
|
||||
position: data.position || '',
|
||||
address: data.address || '',
|
||||
inoutFlag: data.inoutFlag || '',
|
||||
remarks: data.remarks || ''
|
||||
})
|
||||
dialogVisible.value = true
|
||||
} catch (error) {
|
||||
// 获取详情失败
|
||||
}
|
||||
}
|
||||
|
||||
// 删除
|
||||
const handleDel = (row: any) => {
|
||||
messageBox.confirm('是否确认删除该条记录').then(async () => {
|
||||
await delObj(row.id)
|
||||
message.success('删除成功')
|
||||
// 如果当前页只剩一条数据,且不是第一页,则跳转到上一页
|
||||
if (state.pagination && state.dataList && state.dataList.length === 1 && state.pagination.current && state.pagination.current > 1) {
|
||||
state.pagination.current = state.pagination.current - 1
|
||||
}
|
||||
getDataList()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
// 批量删除
|
||||
const batchDelect = () => {
|
||||
if (selectList.value.length === 0) {
|
||||
message.warning('请至少选择一条数据')
|
||||
return
|
||||
}
|
||||
messageBox.confirm('是否确认删除').then(async () => {
|
||||
await batchDel(selectList.value)
|
||||
message.success('删除成功')
|
||||
selectList.value = []
|
||||
getDataList()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
// 重置密码
|
||||
const resetPassword = (row: any) => {
|
||||
messageBox.confirm('是否确定重置密码?').then(async () => {
|
||||
try {
|
||||
const response = await resetPassWord(row)
|
||||
const pw = response.data?.data
|
||||
if (!validateNull(pw)) {
|
||||
ElMessageBox.alert('重置后密码为: ' + pw, '重置密码')
|
||||
} else {
|
||||
ElMessageBox.alert('系统繁忙,请重试', '重置密码')
|
||||
}
|
||||
} catch (error) {
|
||||
// 重置密码失败
|
||||
}
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
if (!formRef.value) return
|
||||
|
||||
await formRef.value.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
submitLoading.value = true
|
||||
try {
|
||||
// 设置单位名称
|
||||
const selectedCompany = companyList.value.find(item => item.id === form.companyId)
|
||||
if (selectedCompany) {
|
||||
form.companyName = selectedCompany.companyName
|
||||
}
|
||||
|
||||
if (form.id) {
|
||||
await putObj(form)
|
||||
message.success('修改成功')
|
||||
} else {
|
||||
await saveSecond(form) // 使用 saveSecond API
|
||||
message.success('添加成功')
|
||||
}
|
||||
dialogVisible.value = false
|
||||
getDataList()
|
||||
} catch (error: any) {
|
||||
message.error(error?.msg || '操作失败')
|
||||
} finally {
|
||||
submitLoading.value = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 获取图片 URL
|
||||
const getImageView = (employeeNo: string) => {
|
||||
const timestamp = Date.parse(new Date().toString())
|
||||
const baseUrl = import.meta.env.VITE_API_URL || ''
|
||||
return `${baseUrl}/admin/user/photo/${employeeNo}?${timestamp}`
|
||||
}
|
||||
|
||||
// 图片加载错误处理
|
||||
const handleImageError = (event: Event) => {
|
||||
const img = event.target as HTMLImageElement
|
||||
img.src = '/img/default/no_pic.png'
|
||||
}
|
||||
|
||||
// 预览头像
|
||||
const handlePictureCardPreview = (employeeNo: string) => {
|
||||
dialogImageUrl.value = getImageView(employeeNo)
|
||||
dialogUploadVisible.value = true
|
||||
}
|
||||
|
||||
// 上传前验证
|
||||
const beforeUpload = (file: File) => {
|
||||
const isLt5M = file.size < 1 * 1024 * 1024
|
||||
if (fileList.value.length >= 1) {
|
||||
message.warning('只能上传一张头像')
|
||||
return false
|
||||
}
|
||||
if (!isLt5M) {
|
||||
message.warning('文件大小不能超过1M')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 自定义上传
|
||||
const httpRequest = (options: any) => {
|
||||
const file = options.file
|
||||
if (!fileReader.value) {
|
||||
fileReader.value = new FileReader()
|
||||
}
|
||||
|
||||
if (file) {
|
||||
fileReader.value.readAsDataURL(file)
|
||||
}
|
||||
|
||||
fileReader.value.onload = () => {
|
||||
const base64Str = fileReader.value?.result as string
|
||||
|
||||
const config = {
|
||||
url: '/basic/basicstudent/uploadAvatarBase64',
|
||||
method: 'post',
|
||||
data: {
|
||||
base64Str: base64Str.split(',')[1],
|
||||
username: rowData.value.employeeNo
|
||||
},
|
||||
timeout: 10000,
|
||||
onUploadProgress: (progressEvent: any) => {
|
||||
progressEvent.percent = progressEvent.loaded / progressEvent.total * 100
|
||||
options.onProgress(progressEvent, file)
|
||||
}
|
||||
}
|
||||
|
||||
axios(config)
|
||||
.then(res => {
|
||||
options.onSuccess(res, file)
|
||||
getDataList()
|
||||
})
|
||||
.catch(err => {
|
||||
options.onError(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 移除文件
|
||||
const removeHandler = (file: any) => {
|
||||
const index = fileList.value.findIndex(f => f.uid === file.uid)
|
||||
if (index !== -1) {
|
||||
fileList.value.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// 上传成功
|
||||
const uploadSuccess = (res: any, file: any) => {
|
||||
if (res.data) {
|
||||
const data = res.data
|
||||
file.key = data.key
|
||||
fileList.value.push(file)
|
||||
message.success('上传成功')
|
||||
dialogAvatarVisible.value = false
|
||||
getDataList()
|
||||
}
|
||||
}
|
||||
|
||||
// 导入
|
||||
const handleExportIn = () => {
|
||||
fileName.value = ""
|
||||
filesList.value = []
|
||||
files = null
|
||||
dialogViewVisible.value = true
|
||||
}
|
||||
|
||||
// 文件上传验证
|
||||
const fileUpload = (file: File) => {
|
||||
const fileLast = file.name.split('.')
|
||||
const extension = fileLast[fileLast.length - 1] === 'xls'
|
||||
const extension2 = fileLast[fileLast.length - 1] === 'xlsx'
|
||||
const isLt2M = file.size / 1024 / 1024 < 5
|
||||
|
||||
if (!extension && !extension2) {
|
||||
message.warning('上传模板只能是 xls、xlsx格式!')
|
||||
return false
|
||||
}
|
||||
if (!isLt2M) {
|
||||
message.warning('上传模板大小不能超过 5MB!')
|
||||
return false
|
||||
}
|
||||
fileName.value = file.name
|
||||
files = file
|
||||
return false // 返回false不会自动上传
|
||||
}
|
||||
|
||||
// 提交导入
|
||||
const submitUpload = async () => {
|
||||
if (!fileName.value || !files) {
|
||||
message.warning('请选择要上传的文件!')
|
||||
return
|
||||
}
|
||||
|
||||
const fileFormData = new FormData()
|
||||
fileFormData.append('file', files, fileName.value)
|
||||
|
||||
try {
|
||||
const response = await request({
|
||||
url: `/professional/file/exportOuterCompanyEmployee?companyType=2`,
|
||||
method: 'post',
|
||||
data: fileFormData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
|
||||
if (response.code === 0) {
|
||||
message.success('操作成功')
|
||||
dialogViewVisible.value = false
|
||||
getDataList()
|
||||
} else {
|
||||
message.error(response.msg || '导入失败')
|
||||
}
|
||||
} catch (error: any) {
|
||||
message.error(error?.msg || '导入失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 导出
|
||||
const handleExportScore = async () => {
|
||||
if (!state.queryForm || Object.keys(state.queryForm).length === 0) {
|
||||
message.warning('请选择导出条件')
|
||||
return
|
||||
}
|
||||
|
||||
exportLoading.value = true
|
||||
try {
|
||||
const params = {
|
||||
...state.queryForm,
|
||||
companyType: "2"
|
||||
}
|
||||
|
||||
const response = await axios({
|
||||
method: 'post',
|
||||
url: '/professional/outercompanyemployee/export',
|
||||
data: params,
|
||||
responseType: 'blob',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
const blob = new Blob([response.data])
|
||||
const fileName = '二期单位人员导出表.xls'
|
||||
const elink = document.createElement('a')
|
||||
elink.download = fileName
|
||||
elink.style.display = 'none'
|
||||
elink.href = URL.createObjectURL(blob)
|
||||
document.body.appendChild(elink)
|
||||
elink.click()
|
||||
URL.revokeObjectURL(elink.href)
|
||||
document.body.removeChild(elink)
|
||||
message.success('导出成功')
|
||||
} catch (error: any) {
|
||||
message.error(error?.msg || '导出失败')
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
if (!window.FileReader) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Your browser does not support FileReader API!')
|
||||
} else {
|
||||
fileReader.value = new FileReader()
|
||||
}
|
||||
loadCompanyList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.upload-container {
|
||||
:deep(.el-upload) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
871
src/views/professional/outercompanyemployee/indexTrain.vue
Executable file
871
src/views/professional/outercompanyemployee/indexTrain.vue
Executable file
@@ -0,0 +1,871 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<!-- 搜索表单 -->
|
||||
<el-row shadow="hover" v-show="showSearch" class="ml10">
|
||||
<el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="handleFilter">
|
||||
<el-form-item label="班级名称" prop="companyId">
|
||||
<el-select
|
||||
v-model="state.queryForm.companyId"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="请选择班级"
|
||||
style="width: 200px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in companyList"
|
||||
:key="item.id"
|
||||
:label="item.companyName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="学员编号" prop="employeeNo">
|
||||
<el-input
|
||||
v-model="state.queryForm.employeeNo"
|
||||
placeholder="请输入学员编号"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="姓名" prop="realName">
|
||||
<el-input
|
||||
v-model="state.queryForm.realName"
|
||||
placeholder="请输入姓名"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证" prop="idCard">
|
||||
<el-input
|
||||
v-model="state.queryForm.idCard"
|
||||
placeholder="请输入身份证"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机" prop="mobile">
|
||||
<el-input
|
||||
v-model="state.queryForm.mobile"
|
||||
placeholder="请输入手机"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="允许进出" prop="inoutFlag">
|
||||
<el-select
|
||||
v-model="state.queryForm.inoutFlag"
|
||||
clearable
|
||||
placeholder="请选择"
|
||||
style="width: 200px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in yesNoDict"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button icon="search" type="primary" @click="handleFilter">查询</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-row>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<el-row>
|
||||
<div class="mb15" style="width: 100%;">
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="FolderAdd"
|
||||
@click="handleAdd"
|
||||
v-if="permissions.professional_outercompanyemployee_add">新 增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handleExportIn"
|
||||
v-if="permission.scope == '1'"
|
||||
>导 入
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
@click="handleExportScore"
|
||||
:loading="exportLoading"
|
||||
icon="Download">导出
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="batchDelect">批量删除
|
||||
</el-button>
|
||||
<right-toolbar
|
||||
v-model:showSearch="showSearch"
|
||||
class="ml10"
|
||||
style="float: right; margin-right: 20px"
|
||||
@queryTable="getDataList"
|
||||
></right-toolbar>
|
||||
</div>
|
||||
</el-row>
|
||||
|
||||
<!-- 表格 -->
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
:data="state.dataList"
|
||||
v-loading="state.loading"
|
||||
border
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
|
||||
<el-table-column prop="companyName" label="班级名称" min-width="150" align="center" show-overflow-tooltip />
|
||||
|
||||
<el-table-column prop="employeeNo" label="学员编号" min-width="120" align="center" show-overflow-tooltip />
|
||||
|
||||
<el-table-column prop="realName" label="姓名" min-width="100" align="center" show-overflow-tooltip />
|
||||
|
||||
<el-table-column prop="idCard" label="身份证" min-width="180" align="center" show-overflow-tooltip />
|
||||
|
||||
<el-table-column prop="mobile" label="手机" min-width="120" align="center" show-overflow-tooltip />
|
||||
|
||||
<el-table-column prop="inoutFlag" label="允许进出" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.inoutFlag">{{ getDictLabel(scope.row.inoutFlag) }}</el-tag>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="头像" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<img
|
||||
width="50px"
|
||||
height="50px"
|
||||
@click="handlePictureCardPreview(scope.row.employeeNo)"
|
||||
:src="getImageView(scope.row.employeeNo)"
|
||||
@error="handleImageError"
|
||||
style="cursor: pointer; object-fit: cover; border-radius: 4px;"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="updateTime" label="更新时间" width="180" align="center" />
|
||||
|
||||
<el-table-column label="操作" width="250" align="center" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-if="permissions.professional_outercompanyemployee_edit"
|
||||
icon="edit-pen"
|
||||
link
|
||||
type="primary"
|
||||
@click="handleEdit(scope.row)">修改
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="permissions.professional_outercompanyemployee_reset_pw"
|
||||
icon="RefreshLeft"
|
||||
link
|
||||
type="primary"
|
||||
style="margin-left: 12px"
|
||||
@click="resetPassword(scope.row)">重置密码
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="permissions.professional_outercompanyemployee_del"
|
||||
icon="delete"
|
||||
link
|
||||
type="primary"
|
||||
style="margin-left: 12px"
|
||||
@click="handleDel(scope.row)">删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<pagination
|
||||
v-bind="state.pagination"
|
||||
@current-change="currentChangeHandle"
|
||||
@size-change="sizeChangeHandle"
|
||||
/>
|
||||
|
||||
<!-- 新增/编辑弹窗 -->
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="form.id ? '编辑' : '新增'"
|
||||
width="800px"
|
||||
:close-on-click-modal="false"
|
||||
destroy-on-close
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="formRules"
|
||||
label-width="120px"
|
||||
>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="班级名称" prop="companyId">
|
||||
<el-select
|
||||
v-model="form.companyId"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="请选择班级"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in companyList"
|
||||
:key="item.id"
|
||||
:label="item.companyName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="学员编号" prop="employeeNo">
|
||||
<el-input
|
||||
v-model="form.employeeNo"
|
||||
placeholder="系统自动生成"
|
||||
disabled
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="姓名" prop="realName">
|
||||
<el-input
|
||||
v-model="form.realName"
|
||||
placeholder="请输入姓名"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="身份证" prop="idCard">
|
||||
<el-input
|
||||
v-model="form.idCard"
|
||||
placeholder="请输入身份证号"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="手机" prop="mobile">
|
||||
<el-input
|
||||
v-model="form.mobile"
|
||||
placeholder="请输入手机号"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="允许进出" prop="inoutFlag">
|
||||
<el-select
|
||||
v-model="form.inoutFlag"
|
||||
clearable
|
||||
placeholder="请选择"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in yesNoDict"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="备注" prop="remarks">
|
||||
<el-input
|
||||
v-model="form.remarks"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请输入备注"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit" :loading="submitLoading">确定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 头像预览对话框 -->
|
||||
<el-dialog v-model="dialogUploadVisible" title="头像预览" append-to-body>
|
||||
<img width="100%" :src="dialogImageUrl" alt="" style="max-height: 600px; object-fit: contain;" />
|
||||
</el-dialog>
|
||||
|
||||
<!-- 上传头像对话框 -->
|
||||
<el-dialog v-model="dialogAvatarVisible" title="上传头像" width="50%" append-to-body>
|
||||
<el-upload
|
||||
action="/basic/basicstudent/uploadAvatarBase64"
|
||||
list-type="picture-card"
|
||||
name="file"
|
||||
:headers="headers"
|
||||
:limit="1"
|
||||
:data="uploadData"
|
||||
:file-list="fileList"
|
||||
:before-upload="beforeUpload"
|
||||
:on-remove="removeHandler"
|
||||
:http-request="httpRequest"
|
||||
:on-success="uploadSuccess"
|
||||
>
|
||||
<el-icon><Plus /></el-icon>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">上传头像,人脸识别用</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 导入对话框 -->
|
||||
<el-dialog v-model="dialogViewVisible" title="导入文件" append-to-body>
|
||||
<el-upload
|
||||
class="upload-container"
|
||||
ref="uploadFormRef"
|
||||
action="doUpload"
|
||||
:limit="1"
|
||||
:file-list="filesList"
|
||||
:before-upload="fileUpload"
|
||||
:auto-upload="false"
|
||||
>
|
||||
<template #trigger>
|
||||
<el-button type="primary">选取文件</el-button>
|
||||
</template>
|
||||
<a href="outercomanyemployee.xlsx" rel="external nofollow" download="模板" style="margin-left: 20px">
|
||||
<el-button type="success">下载模板</el-button>
|
||||
</a>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">只能上传excel文件,且不超过5MB</div>
|
||||
<div class="el-upload__tip">{{ fileName }}</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="dialogViewVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitUpload">导入</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useUserInfo } from '/@/stores/userInfo'
|
||||
import { BasicTableProps, useTable } from '/@/hooks/table'
|
||||
import { useMessage } from '/@/hooks/message'
|
||||
import { useMessageBox } from '/@/hooks/message'
|
||||
import { useDict } from '/@/hooks/dict'
|
||||
import { Session } from '/@/utils/storage'
|
||||
import { validateNull } from '/@/utils/validate'
|
||||
import axios from 'axios'
|
||||
import request from '/@/utils/request'
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import {
|
||||
fetchList,
|
||||
getObj,
|
||||
saveSecond,
|
||||
putObj,
|
||||
delObj,
|
||||
batchDel,
|
||||
resetPassWord
|
||||
} from '/@/api/professional/stayschool/outercompanyemployee'
|
||||
import { getList as getCompanyList } from '/@/api/professional/stayschool/outercompany'
|
||||
|
||||
// 使用 Pinia store
|
||||
const userInfoStore = useUserInfo()
|
||||
const { userInfos } = storeToRefs(userInfoStore)
|
||||
|
||||
// 创建权限对象
|
||||
const permissions = computed(() => {
|
||||
const perms: Record<string, boolean> = {}
|
||||
userInfos.value.authBtnList.forEach((perm: string) => {
|
||||
perms[perm] = true
|
||||
})
|
||||
return perms
|
||||
})
|
||||
|
||||
// 消息提示 hooks
|
||||
const message = useMessage()
|
||||
const messageBox = useMessageBox()
|
||||
|
||||
// 字典数据
|
||||
const { yes_no: yesNoDict } = useDict('yes_no')
|
||||
|
||||
// 获取字典标签的辅助函数
|
||||
const getDictLabel = (value: string | number) => {
|
||||
const item = yesNoDict.value.find((i: any) => i.value === value)
|
||||
return item ? item.label : ''
|
||||
}
|
||||
|
||||
// 表格引用
|
||||
const tableRef = ref()
|
||||
const formRef = ref()
|
||||
const queryRef = ref()
|
||||
const uploadFormRef = ref()
|
||||
|
||||
// 搜索显示
|
||||
const showSearch = ref(true)
|
||||
|
||||
// 弹窗状态
|
||||
const dialogVisible = ref(false)
|
||||
const dialogUploadVisible = ref(false)
|
||||
const dialogAvatarVisible = ref(false)
|
||||
const dialogViewVisible = ref(false)
|
||||
const submitLoading = ref(false)
|
||||
const exportLoading = ref(false)
|
||||
|
||||
// 选中的行数据
|
||||
const selectList = ref<any[]>([])
|
||||
const permission = reactive({
|
||||
hasPermission: "0",
|
||||
scope: "0"
|
||||
})
|
||||
|
||||
// 单位列表
|
||||
const companyList = ref<any[]>([])
|
||||
|
||||
// 表单数据
|
||||
const form = reactive({
|
||||
id: '',
|
||||
companyId: '',
|
||||
companyName: '',
|
||||
employeeNo: '',
|
||||
realName: '',
|
||||
idCard: '',
|
||||
mobile: '',
|
||||
inoutFlag: '',
|
||||
remarks: ''
|
||||
})
|
||||
|
||||
// 表单验证规则 - 培训单位没有必填验证
|
||||
const formRules = {
|
||||
companyId: [
|
||||
{ required: true, message: '请选择班级', trigger: 'change' }
|
||||
]
|
||||
}
|
||||
|
||||
// 头像相关
|
||||
const dialogImageUrl = ref('')
|
||||
const fileList = ref<any[]>([])
|
||||
const rowData = ref<any>({})
|
||||
const fileReader = ref<FileReader | null>(null)
|
||||
|
||||
// 上传相关
|
||||
const uploadData = reactive({
|
||||
bucketName: "base",
|
||||
module: "basic",
|
||||
username: ""
|
||||
})
|
||||
|
||||
// 导入相关
|
||||
const fileName = ref('')
|
||||
const filesList = ref<any[]>([])
|
||||
let files: File | null = null
|
||||
|
||||
// 请求头
|
||||
const headers = computed(() => {
|
||||
return {
|
||||
"Authorization": 'Bearer ' + Session.getToken()
|
||||
}
|
||||
})
|
||||
|
||||
// 配置 useTable - 注意这个 API 返回的数据结构特殊
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
pageList: async (params: any) => {
|
||||
const response = await fetchList({
|
||||
...params,
|
||||
companyType: '1' // 培训单位
|
||||
})
|
||||
// 特殊处理:API 返回的是 response.data.data.dataList.records
|
||||
const dataList = response.data?.data?.dataList || response.data?.dataList || {}
|
||||
permission.hasPermission = response.data?.data?.permission?.hasPermission || "0"
|
||||
permission.scope = response.data?.data?.permission?.scope || "0"
|
||||
return {
|
||||
data: {
|
||||
records: dataList.records || [],
|
||||
total: dataList.total || 0
|
||||
}
|
||||
}
|
||||
},
|
||||
queryForm: {
|
||||
companyId: '',
|
||||
employeeNo: '',
|
||||
realName: '',
|
||||
idCard: '',
|
||||
mobile: '',
|
||||
inoutFlag: ''
|
||||
}
|
||||
})
|
||||
|
||||
const { getDataList, currentChangeHandle, sizeChangeHandle, tableStyle } = useTable(state)
|
||||
|
||||
// 获取单位列表
|
||||
const loadCompanyList = async () => {
|
||||
try {
|
||||
const response = await getCompanyList({ companyType: '1' })
|
||||
companyList.value = response.data || []
|
||||
} catch (error) {
|
||||
// 获取单位列表失败
|
||||
}
|
||||
}
|
||||
|
||||
// 重置查询
|
||||
const resetQuery = () => {
|
||||
queryRef.value?.resetFields()
|
||||
getDataList()
|
||||
}
|
||||
|
||||
// 处理搜索
|
||||
const handleFilter = () => {
|
||||
getDataList()
|
||||
}
|
||||
|
||||
// 多选变化
|
||||
const handleSelectionChange = (selection: any[]) => {
|
||||
selectList.value = selection
|
||||
}
|
||||
|
||||
// 打开新增窗口
|
||||
const handleAdd = () => {
|
||||
Object.assign(form, {
|
||||
id: '',
|
||||
companyId: '',
|
||||
companyName: '',
|
||||
employeeNo: '',
|
||||
realName: '',
|
||||
idCard: '',
|
||||
mobile: '',
|
||||
inoutFlag: '',
|
||||
remarks: ''
|
||||
})
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 打开编辑窗口
|
||||
const handleEdit = async (row: any) => {
|
||||
try {
|
||||
const response = await getObj(row.id)
|
||||
const data = response.data
|
||||
Object.assign(form, {
|
||||
id: data.id,
|
||||
companyId: data.companyId || '',
|
||||
companyName: data.companyName || '',
|
||||
employeeNo: data.employeeNo || '',
|
||||
realName: data.realName || '',
|
||||
idCard: data.idCard || '',
|
||||
mobile: data.mobile || '',
|
||||
inoutFlag: data.inoutFlag || '',
|
||||
remarks: data.remarks || ''
|
||||
})
|
||||
dialogVisible.value = true
|
||||
} catch (error) {
|
||||
// 获取详情失败
|
||||
}
|
||||
}
|
||||
|
||||
// 删除
|
||||
const handleDel = (row: any) => {
|
||||
messageBox.confirm('是否确认删除该条记录').then(async () => {
|
||||
await delObj(row.id)
|
||||
message.success('删除成功')
|
||||
// 如果当前页只剩一条数据,且不是第一页,则跳转到上一页
|
||||
if (state.pagination && state.dataList && state.dataList.length === 1 && state.pagination.current && state.pagination.current > 1) {
|
||||
state.pagination.current = state.pagination.current - 1
|
||||
}
|
||||
getDataList()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
// 批量删除
|
||||
const batchDelect = () => {
|
||||
if (selectList.value.length === 0) {
|
||||
message.warning('请至少选择一条数据')
|
||||
return
|
||||
}
|
||||
messageBox.confirm('是否确认删除').then(async () => {
|
||||
await batchDel(selectList.value)
|
||||
message.success('删除成功')
|
||||
selectList.value = []
|
||||
getDataList()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
// 重置密码
|
||||
const resetPassword = (row: any) => {
|
||||
messageBox.confirm('是否确定重置密码?').then(async () => {
|
||||
try {
|
||||
const response = await resetPassWord(row)
|
||||
const pw = response.data?.data
|
||||
if (!validateNull(pw)) {
|
||||
ElMessageBox.alert('重置后密码为: ' + pw, '重置密码')
|
||||
} else {
|
||||
ElMessageBox.alert('系统繁忙,请重试', '重置密码')
|
||||
}
|
||||
} catch (error) {
|
||||
// 重置密码失败
|
||||
}
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
if (!formRef.value) return
|
||||
|
||||
await formRef.value.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
submitLoading.value = true
|
||||
try {
|
||||
// 设置单位名称
|
||||
const selectedCompany = companyList.value.find(item => item.id === form.companyId)
|
||||
if (selectedCompany) {
|
||||
form.companyName = selectedCompany.companyName
|
||||
}
|
||||
|
||||
if (form.id) {
|
||||
await putObj(form)
|
||||
message.success('修改成功')
|
||||
} else {
|
||||
await saveSecond(form) // 使用 saveSecond API
|
||||
message.success('添加成功')
|
||||
}
|
||||
dialogVisible.value = false
|
||||
getDataList()
|
||||
} catch (error: any) {
|
||||
message.error(error?.msg || '操作失败')
|
||||
} finally {
|
||||
submitLoading.value = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 获取图片 URL
|
||||
const getImageView = (employeeNo: string) => {
|
||||
const timestamp = Date.parse(new Date().toString())
|
||||
const baseUrl = import.meta.env.VITE_API_URL || ''
|
||||
return `${baseUrl}/admin/user/photo/${employeeNo}?${timestamp}`
|
||||
}
|
||||
|
||||
// 图片加载错误处理
|
||||
const handleImageError = (event: Event) => {
|
||||
const img = event.target as HTMLImageElement
|
||||
img.src = '/img/default/no_pic.png'
|
||||
}
|
||||
|
||||
// 预览头像
|
||||
const handlePictureCardPreview = (employeeNo: string) => {
|
||||
dialogImageUrl.value = getImageView(employeeNo)
|
||||
dialogUploadVisible.value = true
|
||||
}
|
||||
|
||||
// 上传前验证
|
||||
const beforeUpload = (file: File) => {
|
||||
const isLt5M = file.size < 1 * 1024 * 1024
|
||||
if (fileList.value.length >= 1) {
|
||||
message.warning('只能上传一张头像')
|
||||
return false
|
||||
}
|
||||
if (!isLt5M) {
|
||||
message.warning('文件大小不能超过1M')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 自定义上传
|
||||
const httpRequest = (options: any) => {
|
||||
const file = options.file
|
||||
if (!fileReader.value) {
|
||||
fileReader.value = new FileReader()
|
||||
}
|
||||
|
||||
if (file) {
|
||||
fileReader.value.readAsDataURL(file)
|
||||
}
|
||||
|
||||
fileReader.value.onload = () => {
|
||||
const base64Str = fileReader.value?.result as string
|
||||
|
||||
const config = {
|
||||
url: '/basic/basicstudent/uploadAvatarBase64',
|
||||
method: 'post',
|
||||
data: {
|
||||
base64Str: base64Str.split(',')[1],
|
||||
username: rowData.value.employeeNo
|
||||
},
|
||||
timeout: 10000,
|
||||
onUploadProgress: (progressEvent: any) => {
|
||||
progressEvent.percent = progressEvent.loaded / progressEvent.total * 100
|
||||
options.onProgress(progressEvent, file)
|
||||
}
|
||||
}
|
||||
|
||||
axios(config)
|
||||
.then(res => {
|
||||
options.onSuccess(res, file)
|
||||
getDataList()
|
||||
})
|
||||
.catch(err => {
|
||||
options.onError(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 移除文件
|
||||
const removeHandler = (file: any) => {
|
||||
const index = fileList.value.findIndex(f => f.uid === file.uid)
|
||||
if (index !== -1) {
|
||||
fileList.value.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// 上传成功
|
||||
const uploadSuccess = (res: any, file: any) => {
|
||||
if (res.data) {
|
||||
const data = res.data
|
||||
file.key = data.key
|
||||
fileList.value.push(file)
|
||||
message.success('上传成功')
|
||||
dialogAvatarVisible.value = false
|
||||
getDataList()
|
||||
}
|
||||
}
|
||||
|
||||
// 导入
|
||||
const handleExportIn = () => {
|
||||
fileName.value = ""
|
||||
filesList.value = []
|
||||
files = null
|
||||
dialogViewVisible.value = true
|
||||
}
|
||||
|
||||
// 文件上传验证
|
||||
const fileUpload = (file: File) => {
|
||||
const fileLast = file.name.split('.')
|
||||
const extension = fileLast[fileLast.length - 1] === 'xls'
|
||||
const extension2 = fileLast[fileLast.length - 1] === 'xlsx'
|
||||
const isLt2M = file.size / 1024 / 1024 < 5
|
||||
|
||||
if (!extension && !extension2) {
|
||||
message.warning('上传模板只能是 xls、xlsx格式!')
|
||||
return false
|
||||
}
|
||||
if (!isLt2M) {
|
||||
message.warning('上传模板大小不能超过 5MB!')
|
||||
return false
|
||||
}
|
||||
fileName.value = file.name
|
||||
files = file
|
||||
return false // 返回false不会自动上传
|
||||
}
|
||||
|
||||
// 提交导入
|
||||
const submitUpload = async () => {
|
||||
if (!fileName.value || !files) {
|
||||
message.warning('请选择要上传的文件!')
|
||||
return
|
||||
}
|
||||
|
||||
const fileFormData = new FormData()
|
||||
fileFormData.append('file', files, fileName.value)
|
||||
|
||||
try {
|
||||
const response = await request({
|
||||
url: `/professional/file/exportOuterCompanyEmployee?companyType=1`,
|
||||
method: 'post',
|
||||
data: fileFormData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
|
||||
if (response.code === 0) {
|
||||
message.success('操作成功')
|
||||
dialogViewVisible.value = false
|
||||
getDataList()
|
||||
} else {
|
||||
message.error(response.msg || '导入失败')
|
||||
}
|
||||
} catch (error: any) {
|
||||
message.error(error?.msg || '导入失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 导出
|
||||
const handleExportScore = async () => {
|
||||
if (!state.queryForm || Object.keys(state.queryForm).length === 0) {
|
||||
message.warning('请选择导出条件')
|
||||
return
|
||||
}
|
||||
|
||||
exportLoading.value = true
|
||||
try {
|
||||
const params = {
|
||||
...state.queryForm,
|
||||
companyType: "1"
|
||||
}
|
||||
|
||||
const response = await axios({
|
||||
method: 'post',
|
||||
url: '/professional/outercompanyemployee/export',
|
||||
data: params,
|
||||
responseType: 'blob',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
const blob = new Blob([response.data])
|
||||
const fileName = '培训单位人员导出表.xls'
|
||||
const elink = document.createElement('a')
|
||||
elink.download = fileName
|
||||
elink.style.display = 'none'
|
||||
elink.href = URL.createObjectURL(blob)
|
||||
document.body.appendChild(elink)
|
||||
elink.click()
|
||||
URL.revokeObjectURL(elink.href)
|
||||
document.body.removeChild(elink)
|
||||
message.success('导出成功')
|
||||
} catch (error: any) {
|
||||
message.error(error?.msg || '导出失败')
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
if (!window.FileReader) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Your browser does not support FileReader API!')
|
||||
} else {
|
||||
fileReader.value = new FileReader()
|
||||
}
|
||||
loadCompanyList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.upload-container {
|
||||
:deep(.el-upload) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user