This commit is contained in:
guochunsi
2026-01-09 18:46:41 +08:00
parent bacf93c33e
commit 4b46d3cc0d
40 changed files with 1977 additions and 939 deletions

View File

@@ -163,6 +163,7 @@ interface Props {
remarks?: string
}
companyList: any[]
saveApi?: (data: any) => Promise<any> // 自定义保存 API 函数(用于培训单位、二期单位等)
}
const props = withDefaults(defineProps<Props>(), {
@@ -180,7 +181,8 @@ const props = withDefaults(defineProps<Props>(), {
inoutFlag: '',
remarks: ''
}),
companyList: () => []
companyList: () => [],
saveApi: undefined
})
// Emits
@@ -223,7 +225,7 @@ const formRules = {
idCard: [
{ required: true, message: '请输入正确身份证', trigger: 'blur' },
{ min: 15, max: 18, message: '长度在 15 到 18 个字符', trigger: 'blur' },
{ pattern: /^(\d{15}|\d{17}[\dXx])$/, message: '身份证号格式不正确15位或18位18位最后一位可以是X', trigger: 'blur' }
{ pattern: /^(\d{15}|\d{17}[\dXx])$/, message: '身份证号格式不正确', trigger: 'blur' }
],
mobile: [
{ required: true, message: '请填写手机号', trigger: 'blur' },
@@ -271,7 +273,12 @@ const handleSubmit = async () => {
await putObj(submitData)
message.success('修改成功')
} else {
await addObj(submitData)
// 如果提供了自定义保存函数,使用它;否则使用默认的 addObj
if (props.saveApi) {
await props.saveApi(submitData)
} else {
await addObj(submitData)
}
message.success('添加成功')
}
handleClose()

View File

@@ -0,0 +1,284 @@
<template>
<el-dialog
v-model="visible"
:title="formData.id ? '编辑' : '新增'"
width="800px"
:close-on-click-modal="false"
destroy-on-close
@close="handleClose"
>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="120px"
class="form-content"
>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="班级名称" prop="companyId">
<el-select
v-model="formData.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="formData.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="formData.realName"
placeholder="请输入姓名"
clearable
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="身份证" prop="idCard">
<el-input
v-model="formData.idCard"
placeholder="请输入身份证号"
clearable
maxlength="18"
@input="handleIdCardInput"
/>
</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="formData.mobile"
type="number"
placeholder="请输入手机号"
clearable
maxlength="11"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="允许进出" prop="inoutFlag">
<el-select
v-model="formData.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="formData.remarks"
type="textarea"
:rows="3"
placeholder="请输入备注"
clearable
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleSubmit" :loading="submitLoading">确定</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
import { useDict } from '/@/hooks/dict'
import { useMessage } from '/@/hooks/message'
import { putObj } from '/@/api/professional/stayschool/outercompanyemployee'
// Props
interface Props {
modelValue: boolean
formData: {
id?: string
companyId?: string
companyName?: string
employeeNo?: string
realName?: string
idCard?: string
mobile?: string
inoutFlag?: string
remarks?: string
}
companyList: any[]
saveApi?: (data: any) => Promise<any> // 自定义保存 API 函数
}
const props = withDefaults(defineProps<Props>(), {
modelValue: false,
formData: () => ({
id: '',
companyId: '',
companyName: '',
employeeNo: '',
realName: '',
idCard: '',
mobile: '',
inoutFlag: '',
remarks: ''
}),
companyList: () => [],
saveApi: undefined
})
// Emits
const emit = defineEmits<{
(e: 'update:modelValue', value: boolean): void
(e: 'success'): void
}>()
// 字典数据
const { yes_no_type: yesNoDict } = useDict('yes_no_type')
// 消息提示
const message = useMessage()
// 表单引用
const formRef = ref()
// 提交加载状态
const submitLoading = ref(false)
// 弹窗显示状态
const visible = ref(false)
watch(() => props.modelValue, (val) => {
visible.value = val
})
watch(visible, (val) => {
emit('update:modelValue', val)
})
// 表单验证规则
const formRules = {
companyId: [
{ required: true, message: '请选择班级', trigger: 'change' }
],
realName: [
{ required: true, message: '请填写姓名', trigger: 'blur' }
],
idCard: [
{ required: true, message: '请输入正确身份证', trigger: 'blur' },
{ min: 15, max: 18, message: '长度在 15 到 18 个字符', trigger: 'blur' },
{ pattern: /^(\d{15}|\d{17}[\dXx])$/, message: '身份证号格式不正确', trigger: 'blur' }
],
mobile: [
{ required: true, message: '请填写手机号', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
],
inoutFlag: [
{ required: true, message: '请选择是否允许进出', trigger: 'change' }
]
}
// 关闭弹窗
const handleClose = () => {
visible.value = false
formRef.value?.resetFields()
}
// 身份证号输入限制只允许数字和X/x最大长度18
const handleIdCardInput = (value: string) => {
// 只保留数字和X/x
const filtered = value.replace(/[^\dXx]/g, '')
if (filtered !== value) {
props.formData.idCard = filtered
}
}
// 提交表单
const handleSubmit = async () => {
if (!formRef.value) return
await formRef.value.validate(async (valid: boolean) => {
if (valid) {
submitLoading.value = true
try {
// 设置班级名称
const selectedCompany = props.companyList.find(item => item.id === props.formData.companyId)
const submitData = {
...props.formData,
companyName: selectedCompany?.companyName || ''
}
if (props.formData.id) {
await putObj(submitData)
message.success('修改成功')
} else {
// 使用自定义保存函数
if (props.saveApi) {
await props.saveApi(submitData)
} else {
message.error('请提供保存 API 函数')
return
}
message.success('添加成功')
}
handleClose()
emit('success')
} catch (error: any) {
message.error(error?.msg || '操作失败')
} finally {
submitLoading.value = false
}
}
})
}
</script>
<style lang="scss" scoped>
.form-content {
:deep(.el-row) {
margin-bottom: 18px;
&:last-child {
margin-bottom: 0;
}
}
:deep(.el-row .el-form-item) {
margin-bottom: 0;
}
}
</style>

View File

@@ -73,7 +73,7 @@
<div class="mb15">
<el-button
type="primary"
icon="Plus"
icon="FolderAdd"
@click="handleAdd"
v-if="permissions.professional_outercompanyemployee_add">
</el-button>
@@ -86,7 +86,7 @@
v-if="permission.scope == '1'"
>
</el-button>
<el-button
<!-- <el-button
type="warning"
plain
icon="Download"
@@ -100,7 +100,7 @@
icon="Delete"
class="ml10"
@click="batchDelect">批量删除
</el-button>
</el-button> -->
</div>
</el-row>
@@ -136,18 +136,18 @@
<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;"
/>
<el-button
type="primary"
link
icon="Picture"
@click="handlePictureCardPreview(scope.row.employeeNo)"
>
查看
</el-button>
</template>
</el-table-column>
<el-table-column prop="updateTime" label="更新时间" width="180" align="center" />
<!-- <el-table-column prop="updateTime" label="更新时间" width="180" align="center" /> -->
<el-table-column label="操作" width="250" align="center" fixed="right">
<template #default="scope">
@@ -163,17 +163,15 @@
icon="RefreshLeft"
link
type="primary"
style="margin-left: 12px"
@click="resetPassword(scope.row)">重置密码
</el-button>
<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>
</el-button> -->
</template>
</el-table-column>
</el-table>
@@ -198,28 +196,6 @@
<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
@@ -265,7 +241,7 @@ 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 { Plus, Picture } from '@element-plus/icons-vue'
import {
fetchList,
getObj,
@@ -294,7 +270,7 @@ const message = useMessage()
const messageBox = useMessageBox()
// 字典数据
const { yes_no: yesNoDict } = useDict('yes_no')
const { yes_no_type: yesNoDict } = useDict('yes_no_type')
// 获取字典标签的辅助函数
const getDictLabel = (value: string | number) => {
@@ -313,7 +289,6 @@ const showSearch = ref(true)
// 弹窗状态
const dialogVisible = ref(false)
const dialogUploadVisible = ref(false)
const dialogAvatarVisible = ref(false)
const dialogViewVisible = ref(false)
const exportLoading = ref(false)
@@ -344,28 +319,12 @@ const form = reactive({
// 头像相关
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>({
@@ -521,98 +480,12 @@ const getImageView = (employeeNo: string) => {
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 = () => {
@@ -718,11 +591,6 @@ const handleExportScore = async () => {
// 初始化
onMounted(() => {
if (!window.FileReader) {
console.error('Your browser does not support FileReader API!')
} else {
fileReader.value = new FileReader()
}
loadCompanyList()
})
</script>

View File

@@ -2,7 +2,7 @@
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<!-- 搜索表单 -->
<el-row shadow="hover" v-show="showSearch" class="ml10">
<el-row shadow="hover" v-show="showSearch">
<el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="handleFilter">
<el-form-item label="单位名称" prop="companyId">
<el-select
@@ -76,35 +76,50 @@
<!-- 操作按钮 -->
<el-row>
<div class="mb15" style="width: 100%;">
<div class="mb15" style="width: 100%; position: relative;">
<el-button
type="primary"
icon="FolderAdd"
icon="FolderAdd"
@click="handleAdd"
v-if="permissions.professional_outercompanyemployee_add">
v-if="permissions.professional_outercompanyemployee_add"
>
</el-button>
<el-button
type="primary"
plain
icon="UploadFilled"
@click="handleExportIn"
v-if="permission.scope == '1'"
>
class="ml10"
>
</el-button>
<el-button
<!-- <el-button
type="warning"
plain
icon="Download"
@click="handleExportScore"
:loading="exportLoading"
icon="Download">导出
class="ml10"
>
导出
</el-button>
<el-button
type="primary"
@click="batchDelect">批量删除
</el-button>
<right-toolbar
type="danger"
plain
icon="Delete"
@click="batchDelect"
class="ml10"
>
批量删除
</el-button> -->
<!-- <right-toolbar
v-model:showSearch="showSearch"
class="ml10"
style="float: right; margin-right: 20px"
style="position: absolute; right: 0; top: 0;"
@queryTable="getDataList"
></right-toolbar>
></right-toolbar> -->
</div>
</el-row>
@@ -140,18 +155,18 @@
<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;"
/>
<el-button
type="primary"
link
icon="Picture"
@click="handlePictureCardPreview(scope.row.employeeNo)"
>
查看
</el-button>
</template>
</el-table-column>
<el-table-column prop="updateTime" label="更新时间" width="180" align="center" />
<!-- <el-table-column prop="updateTime" label="更新时间" width="180" align="center" /> -->
<el-table-column label="操作" width="250" align="center" fixed="right">
<template #default="scope">
@@ -167,17 +182,15 @@
icon="RefreshLeft"
link
type="primary"
style="margin-left: 12px"
@click="resetPassword(scope.row)">重置密码
</el-button>
<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>
</el-button> -->
</template>
</el-table-column>
</el-table>
@@ -190,167 +203,19 @@
/>
<!-- 新增/编辑弹窗 -->
<el-dialog
<form-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>
:form-data="form"
:company-list="companyList"
:save-api="saveSecond"
@success="handleFormSuccess"
/>
<!-- 头像预览对话框 -->
<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
@@ -396,18 +261,17 @@ 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'
import FormDialog from './form.vue'
// 使用 Pinia store
const userInfoStore = useUserInfo()
@@ -427,7 +291,7 @@ const message = useMessage()
const messageBox = useMessageBox()
// 字典数据
const { yes_no: yesNoDict } = useDict('yes_no')
const { yes_no_type: yesNoDict } = useDict('yes_no_type')
// 获取字典标签的辅助函数
const getDictLabel = (value: string | number) => {
@@ -437,7 +301,6 @@ const getDictLabel = (value: string | number) => {
// 表格引用
const tableRef = ref()
const formRef = ref()
const queryRef = ref()
const uploadFormRef = ref()
@@ -447,9 +310,7 @@ 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)
// 选中的行数据
@@ -477,52 +338,15 @@ const form = reactive({
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>({
@@ -666,36 +490,9 @@ const resetPassword = (row: any) => {
}).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
}
}
})
// 表单提交成功回调
const handleFormSuccess = () => {
getDataList()
}
// 获取图片 URL
@@ -705,90 +502,12 @@ const getImageView = (employeeNo: string) => {
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 = () => {
@@ -894,12 +613,6 @@ const handleExportScore = async () => {
// 初始化
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>

View File

@@ -2,7 +2,7 @@
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<!-- 搜索表单 -->
<el-row shadow="hover" v-show="showSearch" class="ml10">
<el-row shadow="hover" v-show="showSearch">
<el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="handleFilter">
<el-form-item label="班级名称" prop="companyId">
<el-select
@@ -79,32 +79,47 @@
<div class="mb15" style="width: 100%;">
<el-button
type="primary"
icon="FolderAdd"
icon="FolderAdd"
@click="handleAdd"
v-if="permissions.professional_outercompanyemployee_add">
v-if="permissions.professional_outercompanyemployee_add"
>
</el-button>
<el-button
type="primary"
plain
icon="UploadFilled"
@click="handleExportIn"
v-if="permission.scope == '1'"
>
class="ml10"
>
</el-button>
<el-button
<!-- <el-button
type="warning"
plain
icon="Download"
@click="handleExportScore"
:loading="exportLoading"
icon="Download">导出
class="ml10"
>
导出
</el-button>
<el-button
type="primary"
@click="batchDelect">批量删除
type="danger"
plain
icon="Delete"
@click="batchDelect"
class="ml10"
>
批量删除
</el-button>
<right-toolbar
v-model:showSearch="showSearch"
class="ml10"
style="float: right; margin-right: 20px"
@queryTable="getDataList"
></right-toolbar>
></right-toolbar> -->
</div>
</el-row>
@@ -140,18 +155,18 @@
<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;"
/>
<el-button
type="primary"
link
icon="Picture"
@click="handlePictureCardPreview(scope.row.employeeNo)"
>
查看
</el-button>
</template>
</el-table-column>
<el-table-column prop="updateTime" label="更新时间" width="180" align="center" />
<!-- <el-table-column prop="updateTime" label="更新时间" width="180" align="center" /> -->
<el-table-column label="操作" width="250" align="center" fixed="right">
<template #default="scope">
@@ -167,17 +182,15 @@
icon="RefreshLeft"
link
type="primary"
style="margin-left: 12px"
@click="resetPassword(scope.row)">重置密码
</el-button>
<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>
</el-button> -->
</template>
</el-table-column>
</el-table>
@@ -190,146 +203,19 @@
/>
<!-- 新增/编辑弹窗 -->
<el-dialog
<form-train
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>
:form-data="form"
:company-list="companyList"
:save-api="saveSecond"
@success="handleFormSuccess"
/>
<!-- 头像预览对话框 -->
<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
@@ -375,18 +261,18 @@ 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 { Plus, UploadFilled, Download, Delete, Picture } 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'
import FormTrain from './formTrain.vue'
// 使用 Pinia store
const userInfoStore = useUserInfo()
@@ -406,7 +292,7 @@ const message = useMessage()
const messageBox = useMessageBox()
// 字典数据
const { yes_no: yesNoDict } = useDict('yes_no')
const { yes_no_type: yesNoDict } = useDict('yes_no_type')
// 获取字典标签的辅助函数
const getDictLabel = (value: string | number) => {
@@ -416,7 +302,6 @@ const getDictLabel = (value: string | number) => {
// 表格引用
const tableRef = ref()
const formRef = ref()
const queryRef = ref()
const uploadFormRef = ref()
@@ -426,9 +311,7 @@ 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)
// 选中的行数据
@@ -454,37 +337,15 @@ const form = reactive({
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>({
@@ -624,36 +485,9 @@ const resetPassword = (row: any) => {
}).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
}
}
})
// 表单提交成功回调
const handleFormSuccess = () => {
getDataList()
}
// 获取图片 URL
@@ -663,90 +497,12 @@ const getImageView = (employeeNo: string) => {
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 = () => {
@@ -852,12 +608,6 @@ const handleExportScore = async () => {
// 初始化
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>