<template>
<BasicModal
width="900px"
:height="600"
v-bind="$attrs"
@register="register"
:title="modalType === 'add' ? '新增' : '编辑'"
@ok="handleok"
:confirmLoading="loading"
:footer="modalType !== 'check' && undefined"
>
<div class="pt-3px pr-3px" v-if="modalType !== 'lizhi'">
<BasicForm @register="registerForm">
<template #userCode="{ model, slot }">
<a-input
v-model:value="model.userCode"
:disabled="modalType === 'edit' && !userStore.getUserRole"
placeholder="请输入"
></a-input>
</template>
<template #laborType="{ model, field }">
<a-select ref="select" v-model:value="model[field]" show-search allow-clear>
<a-select-option v-for="item in personLaborTypeListRef" :key="item.dataCode" :value="item.dataCode">{{
item.dataNameCode
}}</a-select-option>
</a-select>
</template>
</BasicForm>
<Form>
<a-row>
<a-col :span="10" :offset="2">
<FormItem label="部门" name="type">
<a-select @change="changeOA" :disabled="modalType === 'edit' && !userStore.getUserRole" ref="select" v-model:value="OAitem" show-search>
<a-select-option v-for="item in OAList" :key="item.id" :value="item.id">{{
item.name
}}</a-select-option>
</a-select>
</FormItem>
</a-col>
<a-col :span="10" :offset="2">
<FormItem label="班组" name="type">
<a-select @change="changeOB"
ref="select"
v-model:value="OBitem"
show-search
allow-clear
:filter-option="(input,option)=>{
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}">
<a-select-option v-for="item in OBList" :key="item.id" :label="item.name" :value="item.id">{{
item.name
}}</a-select-option>
</a-select>
</FormItem>
</a-col>
<a-col :span="10" :offset="2">
<FormItem label="岗位类别" name="type">
<a-select ref="select" v-model:value="positionCategory" show-search allow-clear>
<a-select-option v-for="item in positionCategoryListRef" :key="item.dataCode" :value="item.dataCode">{{
item.dataNameCode
}}</a-select-option>
</a-select>
</FormItem>
</a-col>
<a-col :span="10" :offset="2">
<FormItem label="岗位" name="type">
<a-select ref="select" v-model:value="userRole" show-search placeholder="请选择岗位">
<a-select-option v-for="item in UserRoleList" :key="item.id" :value="item.dataCode">
{{item.dataNameCode}}
</a-select-option>
</a-select>
</FormItem>
</a-col>
</a-row>
<a-row>
<a-col :span="10" :offset="2">
<FormItem label="职务" name="type">
<a-select ref="select" v-model:value="userDuty" show-search allow-clear>
<a-select-option v-for="item in userDutyList" :key="item.value" :value="item.value">{{
item.label
}}</a-select-option>
</a-select>
</FormItem>
</a-col>
</a-row>
<a-row v-if="editPhoto">
<a-col :span="10" :offset="2">
<FormItem label="上传脸卡" name="type">
<a-button style="position: relative; margin-right: 10px">
选择文件(小于200kb)
<input
type="file"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; opacity: 0"
@change="handleImageUpload"
accept="image/*"
/>
<div id="ths90"> </div>
</a-button>
<a-image
v-if="base64Image"
:src="'data:image/png;base64,' + base64Image"
alt="预览图片"
style="width: 200px; height: auto"
/>
<canvas
style="display: none"
id="canvasCamera"
:width="videoWidth"
:height="videoHeight"
></canvas>
</FormItem>
</a-col>
<a-col :span="10" :offset="2">
<FormItem label="拍照" >
<div style="display: flex; margin-bottom: 5px">
<a-button @click="openPhotoDialog">开启拍照</a-button>
<a-button class="ml-2" type="primary" @click="setImage">拍照</a-button>
<a-button class="ml-2" type="primary" @click="stopNavigator">完成关闭</a-button>
</div>
<div style="display: flex;" v-show="flag">
<video
id="videoCamera"
:width="videoWidth"
:height="videoHeight"
autoplay
></video>
</div>
</FormItem>
</a-col>
</a-row>
<a-row>
</a-row>
</Form>
</div>
<div v-if="modalType === 'lizhi'">
<a-form-item style="margin-left: 20px" label="离职时间">
<a-date-picker style="width: 100%" valueFormat="YYYY-MM-DD" :format="'YYYY-MM-DD'" v-model:value="liTime" />
</a-form-item>
<a-form-item style="margin-left: 20px" label="离职原因">
<Textarea style="width: 100%" :rows="4" v-model:value="remark" allow-clear placeholder="离职原因" />
</a-form-item>
</div>
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, nextTick, inject, onMounted } from 'vue';
import { update, quit, add } from '@/api/classManagement/personalManagement';
import { oaList, obList } from '@/api/personnel/tables';
import { BasicModal, useModalInner } from '@/components/Modal';
import { Form, FormItem, message ,Textarea } from 'ant-design-vue';
import { BasicForm, FormSchema, useForm } from '@/components/Form';
import {listChildren} from "@/api/glossary/tables";
import { useDictStore } from '@/store/modules/dict';
import { useUserStore } from '@/store/modules/user';
const editPhoto = ref(true)
const dictStore = useDictStore();
// import { add } from 'xe-utils';
// import { list as listQueStionStore } from '@/api/questionBank/tables';
const pid = ref('');
const liTime = ref('');
const remark = ref('');
const modalType = ref();
const userStore: any = useUserStore();
const userDutyList = ref([ { value: 'ME', label: 'ME' },
{ value: 'FQC', label: 'FQC' }, { value: 'IPQC', label: 'IPQC' }])
const schemas: FormSchema[] = [
{
field: 'inTime',
component: 'DatePicker',
label: '入职时间',
required: true,
colProps: {
span: 12,
},
componentProps: {
disabled: modalType.value === 'edit' && !userStore.getUserRole,
style: { width: '100%' },
valueFormat: 'YYYY-MM-DD',
},
},
{
field: 'quitDate',
component: 'DatePicker',
label: '离职时间',
colProps: {
span: 12,
},
componentProps: {
style: { width: '100%' },
valueFormat: 'YYYY-MM-DD',
},
},
{
field: 'userName',
component: 'Input',
label: '姓名',
required: true,
colProps: {
span: 12,
},
},
{
field: 'userCode',
label: '工号',
required: true,
slot: 'userCode',
colProps: {
span: 12,
},
},
{
field: 'phone',
component: 'Input',
label: '电话',
required: true,
rules: [
// {
// required: true,
// message: '请输入电话号码',
// },
{
pattern: /^1[3-9]\d{9}$/,
message: '请输入正确的手机号码',
}
],
colProps: {
span: 12,
},
},
{
field: 'card',
component: 'Input',
label: '身份证号',
colProps: {
span: 12,
},
},
{
field: 'gender',
component: 'Select',
label: '性别',
required: true,
colProps: {
span: 12,
},
componentProps: {
options: [
{ value: '男', label: '男' },
{ value: '女', label: '女' },
],
},
},
{
field: 'userCategory',
component: 'Select',
label: '类别',
colProps: {
span: 12,
},
componentProps: {
options: [
{ value: 10, label: '二线人员' },
{ value: 0, label: '一线人员' },
],
onChange:(e)=> changeCategory(e),
},
},
{
field: 'focus',
component: 'Select',
label: '是否内训师',
colProps: {
span: 12,
},
componentProps: {
options: [
{ value: 1, label: '是' },
{ value: 0, label: '否' },
],
},
},
{
field: 'hours',
component: 'Input',
label: '应学学时',
colProps: {
span: 12,
},
},
{
field: 'userState',
component: 'Select',
defaultValue: 0,
label: '在职状态',
colProps: {
span: 12,
},
componentProps: {
disabled: true,
options: [
// { value: 10, label: '新员工' },
{ value: 0, label: '在职' },
{ value: 20, label: '离职' },
],
},
},
{
field: 'laborType',
// component: 'ApiSelect',
label: '劳务类型',
colProps: {
span: 12,
},
slot: 'laborType',
required: true,
// componentProps: {
// api: () => dictStore.getDictList('personLaborType'),
// resultField: 'data',
// labelField: 'dataNameCode',
// valueField: 'dataCode',
// },
// required: true,
},
];
const initFormData = {
focus: 0,
gender: '',
groupId: '',
hours: 0,
inTime: '',
orgId: '',
phone: '',
photo: '',
userCategory: 10,
userCode: '',
userDuty: '',
positionCategory:'',
userRole: '',
userName: '',
};
const OBitem = ref('');
const OAitem = ref('');
const UserCode = ref('');
const userDuty = ref('');
const positionCategory = ref('');
const userRole = ref('');
const OAList: any = ref([]);
const OBList: any = ref([]);
const positionCategoryList: any = ref([]);
const personLaborTypeList: any = ref([]);
const UserRoleList: any = ref([]);
onMounted(async () => {
await getOaLsit();
await getDictList()
const res = await listChildren({ parentCode: 'UserRole' });
UserRoleList.value = res.data
});
const positionCategoryListRef:any = ref([]);
const personLaborTypeListRef:any = ref([]);
// 过滤岗位类别和劳务类型
const changeCategory = (val) => {
// 二线
if (val === 10) {
positionCategoryListRef.value = positionCategoryList.value.filter(item=>item.dataValue ==='10' || item.dataValue === '3')
personLaborTypeListRef.value = personLaborTypeList.value.filter(item=>item.dataValue ==='10' || item.dataValue === '3')
} else {
// 一线
positionCategoryListRef.value = positionCategoryList.value.filter(item=>item.dataValue ==='0'|| item.dataValue ==='3')
personLaborTypeListRef.value = personLaborTypeList.value.filter(item=>item.dataValue === '0'|| item.dataValue ==='3' )
}
};
const base64Image = ref(); // 存储 Base64 图片数据
const MAX_SIZE = 200 * 1024; // 200kb 的大小限制
const isPhotoDialogVisible = ref(false);
// 处理文件上传并将其转换为 Base64
const handleImageUpload = (event) => {
const file = event.target.files[0]; // 获取上传的文件
if (file) {
// 检查文件大小
if (file.size > MAX_SIZE) {
console.log(file.size);
message.error('图片大小不能超过 200kb'); // 使用Ant Design的message组件提示
return; // 直接返回,停止后续处理
}
const reader = new FileReader();
// 当读取成功时,将结果赋值给 base64Image
reader.onload = () => {
let res:any = reader.result; // reader.result 是 Base64 数据
base64Image.value = res.split(',')[1]
console.log(base64Image.value);
};
// 将文件读取为 Data URL (Base64)
reader.readAsDataURL(file);
event.target.value = '';
}
};
const getObLsit = async (id) => {
const res = await obList({ orgId: id });
OBList.value = res.data;
};
const getOaLsit = async () => {
const res = await oaList({pageIndex:1,pageSize:999 });
OAList.value = res.data.list;
};
// 获取岗位类别
const getDictList = async () => {
const res = await dictStore.getDictList('personPositionCategory');
const resList = await dictStore.getDictList('personLaborType');
console.log(res,resList);
positionCategoryList.value = res;
personLaborTypeList.value = resList;
};
const changeOA = async () => {
OBitem.value = ''
getObLsit(OAitem.value);
};
const changeOB = async () => {};
// const getList = inject('refreshInfoList') as Function;
const reload = inject('reload') as Function;
const originEdit = ref();
const [registerForm, formBox] = useForm({
labelWidth: 120,
schemas,
showActionButtonGroup: false,
actionColOptions: {
span: 24,
},
});
const [register, modalBox] = useModalInner((params) => {
formBox.resetFields()
liTime.value = ''
remark.value = ''
const { data, type } = params;
modalType.value = type;
console.log("modalType.value",);
pid.value = data.id;
// 方式1;
if (modalType.value === 'edit') {
editPhoto.value = false
originEdit.value = {};
originEdit.value = data;
OAitem.value = data.orgId;
UserCode.value = data.userCode;
userRole.value = data.userRole;
userDuty.value = data.userDuty;
positionCategory.value = data.positionCategory;
getObLsit(OAitem.value);
OBitem.value = data.groupId;
// base64Image.value = data.photo;
setTimeout(() => {
formBox.setFieldsValue(initFormData);
formBox.setFieldsValue(data);
}, 0);
} else {
formBox.resetFields();
formBox.setFieldsValue(initFormData);
editPhoto.value = true
OAitem.value = '';
UserCode.value = '';
userDuty.value = '';
positionCategory.value = '';
userRole.value = '';
OBitem.value = '';
base64Image.value = '';
}
});
const loading = ref(false);
const handleok = async () => {
if (loading.value) return;
try {
loading.value = true;
if (modalType.value === 'add') {
// 新增时额外校验部门和班组
if (!OAitem.value) {
message.error('请选择部门');
return;
}
if (!OBitem.value) {
message.error('请选择班组');
return;
}
}
if (modalType.value === 'edit') {
const val = await formBox.validateFields();
originEdit.value.orgId = OAitem.value;
originEdit.value.userRole = UserCode.value;
originEdit.value.userDuty = userDuty.value || "";
originEdit.value.positionCategory = positionCategory.value || "";
originEdit.value.userRole = userRole.value || "";
originEdit.value.groupId = OBitem.value;
val.photo = base64Image.value;
const res = await update({
...(originEdit.value || {}),
...val,
});
if (res.code == 0) {
message.success(res.message || '操作成功');
reload();
formBox.resetFields();
modalBox.closeModal();
} else {
message.error(res.message || '操作失败');
}
}
if (modalType.value === 'add') {
const val = await formBox.validateFields();
val.orgId = OAitem.value;
val.userRole = userRole.value;
val.groupId = OBitem.value;
val.userDuty = userDuty.value;
val.positionCategory = positionCategory.value;
val.photo = base64Image.value;
val.syncState = 0;
const res = await add({
...(originEdit.value || {}),
...val,
});
if (res.code === 0) {
reload();
formBox.resetFields();
modalBox.closeModal();
message.success(res?.message || '操作成功');
} else {
message.error(res?.message || '操作失败');
}
}
if (modalType.value === 'lizhi') {
const res = await quit({
idList: [pid.value],
quitDate: liTime.value,
quitReason: remark.value,
});
if (res.code === 0) {
reload();
formBox.resetFields();
modalBox.closeModal();
message.success(res?.message || '登记成功');
} else {
message.error(res?.message || '登记失败');
}
}
} catch (error) {
message.error('请填写必填项');
} finally {
loading.value = false;
}
};
const formData:any = ref(null)
const videoWidth:any = ref(195)
const videoHeight:any = ref(260)
const imgSrc:any = ref("")
const thisCancas:any = ref(null)
const thisContext:any = ref(null)
const thisVideo:any = ref(null)
const flag = ref(false)
// 打开弹框
const openPhotoDialog = () => {
nextTick(() => {
getCompetence()
})
}
// 调用权限(打开摄像头功能)
const getCompetence = () => {
thisCancas.value = document.getElementById("canvasCamera")
console.log("thisCancas", thisCancas.value)
thisContext.value = thisCancas.value?.getContext("2d")
thisVideo.value = document.getElementById("videoCamera") as HTMLVideoElement
// 旧版本浏览器可能根本不支持mediaDevices,我们首先设置一个空对象
if (navigator.mediaDevices === undefined) {
navigator.mediaDevices = {}
}
// 一些浏览器实现了部分mediaDevices,我们不能只分配一个对象
// 使用getUserMedia,因为它会覆盖现有的属性。
// 这里,如果缺少getUserMedia属性,就添加它。
if (navigator.mediaDevices.getUserMedia === undefined) {
navigator.mediaDevices.getUserMedia = (constraints) => {
// 首先获取现存的getUserMedia(如果存在)
const getUserMedia =
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.getUserMedia
// 有些浏览器不支持,会返回错误信息
// 保持接口一致
if (!getUserMedia) {
return Promise.reject(
new Error("getUserMedia is not implemented in this browser")
)
}
// 否则,使用Promise将调用包装到旧的navigator.getUserMedia
return new Promise((resolve, reject) => {
getUserMedia.call(navigator, constraints, resolve, reject)
})
}
}
const constraints = {
audio: false,
video: {
width: videoWidth.value,
height: videoHeight.value,
transform: "scaleX(-1)",
},
}
navigator.mediaDevices.getUserMedia(constraints)
.then((stream) => {
console.log("stream",stream)
// 旧的浏览器可能没有srcObject
if ("srcObject" in thisVideo.value) {
thisVideo.value.srcObject = stream
} else {
// 避免在新的浏览器中使用它,因为它正在被弃用。
thisVideo.value.src = window.URL.createObjectURL(stream)
}
thisVideo.value.onloadedmetadata = () => {
thisVideo.value.play()
}
flag.value = true
})
.catch((err) => {
flag.value = false
message.error("打开失败,未检测到摄像头信息!")
console.log(err)
})
}
// 绘制图片(拍照功能)
const setImage = () => {
console.log("thisCancas", thisCancas.value)
if (thisCancas.value === null) {
return
} else {
console.log("thisContext", thisContext.value)
if (thisContext.value === null) {
return
} else {
thisContext.value.drawImage(
thisVideo.value,
0,
0,
videoWidth.value,
videoHeight.value
)
// 获取图片base64链接
const image = thisCancas.value.toDataURL("image/png")
imgSrc.value = image
base64Image.value = imgSrc.value.split(',')[1] // 更新base64Image
console.log("imgSrc", imgSrc.value)
// emit("refreshDataList", imgSrc.value)
// submit()
}
}
}
// base64转文件
const dataURLtoFile = (dataurl, filename) => {
const arr = dataurl.split(",")
const mime = arr[0].match(/:(.*?);/)[1]
const bstr = atob(arr[1])
const n = bstr.length
const u8arr = new Uint8Array(n)
for (let i = 0; i < n; i++) {
u8arr[i] = bstr.charCodeAt(i)
}
return new File([u8arr], filename, { type: mime })
}
// 关闭摄像头
const stopNavigator = () => {
thisVideo.value.srcObject?.getTracks()[0].stop()
// 隐藏摄像头
flag.value = false
}
// qrBase64是后台传回来的base64数据
const handleDownloadQrIMg = () => {
const qrBase64 = formData.value?.photo
// 这里是获取到的图片base64编码,这里只是个例子哈,要自行编码图片替换这里才能测试看到效果
const imgUrl = `data:image/png;base64,${qrBase64}`
// 如果浏览器支持msSaveOrOpenBlob方法(也就是使用IE浏览器的时候),那么调用该方法去下载图片
if (window.navigator.msSaveOrOpenBlob) {
const bstr = atob(imgUrl.split(",")[1])
const n = bstr.length
const u8arr = new Uint8Array(n)
for (let i = 0; i < n; i++) {
u8arr[i] = bstr.charCodeAt(i)
}
const blob = new Blob([u8arr])
window.navigator.msSaveOrOpenBlob(blob, "chart-download" + "." + "png")
} else {
// 这里就按照chrome等新版浏览器来处理
const a = document.createElement("a")
a.href = imgUrl
a.setAttribute(
"download",
`${dialogData.value.code}` + `+` + `${dialogData.value.name}`
)
a.click()
}
}
const closePhotoDialog = async () => {
isPhotoDialogVisible.value = false
};
</script>
这段代码的含义