新增员工批量导入功能,注意:升级该功能需要composer install新增phpoffice/phpspreadsheet依赖包,安装该依赖包需要服务器环境安装php扩展:fileinfo和zip

This commit is contained in:
hdm 2022-09-22 23:22:28 +08:00
parent ccdd2d236b
commit 6387b28251
6 changed files with 319 additions and 22 deletions

View File

@ -0,0 +1,180 @@
<?php
/**
* @copyright Copyright (c) 2021 勾股工作室
* @license https://opensource.org/licenses/GPL-3.0
* @link https://www.gougucms.com
*/
declare (strict_types = 1);
namespace app\api\controller;
use app\api\BaseController;
use think\facade\Db;
use app\user\model\Admin;
use avatars\MDAvatars;
use Overtrue\Pinyin\Pinyin;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Shared\Date as Shared;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
class Import extends BaseController
{
//生成头像
public function to_avatars($char)
{
$defaultData = array('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'S', 'Y', 'Z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖', '拾',
'一', '二', '三', '四', '五', '六', '七', '八', '九', '十');
if (isset($char)) {
$Char = $char;
} else {
$Char = $defaultData[mt_rand(0, count($defaultData) - 1)];
}
$OutputSize = min(512, empty($_GET['size']) ? 36 : intval($_GET['size']));
$Avatar = new MDAvatars($Char, 256, 1);
$avatar_name = '/avatars/avatar_256_' . set_salt(10) . time() . '.png';
$path = get_config('filesystem.disks.public.url') . $avatar_name;
$res = $Avatar->Save('.' . $path, 256);
$Avatar->Free();
return $path;
}
//导入员工
public function import_admin(){
// 获取表单上传文件
$file[]= request()->file('file');
if($this->uid>1){
return to_assign(1,'该操作只能是超级管理员有权限操作');
}
try {
// 验证文件大小,名称等是否正确
validate(['file' => 'filesize:51200|fileExt:xls,xlsx'])->check($file);
// 日期前綴
$dataPath = date('Ym');
$md5 = $file[0]->hash('md5');
$savename = \think\facade\Filesystem::disk('public')->putFile($dataPath, $file[0], function () use ($md5) {
return $md5;
});
$fileExtendName = substr(strrchr($savename, '.'), 1);
// 有Xls和Xlsx格式两种
if ($fileExtendName == 'xlsx') {
$objReader = IOFactory::createReader('Xlsx');
} else {
$objReader = IOFactory::createReader('Xls');
}
$objReader->setReadDataOnly(TRUE);
$path = get_config('filesystem.disks.public.url');
// 读取文件tp6默认上传的文件在runtime的相应目录下可根据实际情况自己更改
$objPHPExcel = $objReader->load('.'.$path . '/' .$savename);
//$objPHPExcel = $objReader->load('./storage/202209/d11544d20b3ca1c1a5f8ce799c3b2433.xlsx');
$sheet = $objPHPExcel->getSheet(0); //excel中的第一张sheet
$highestRow = $sheet->getHighestRow(); // 取得总行数
$highestColumn = $sheet->getHighestColumn(); // 取得总列数
Coordinate::columnIndexFromString($highestColumn);
$lines = $highestRow - 1;
if ($lines <= 0) {
return to_assign(1, '数据不能为空');
exit();
}
$sex_array=['未知','男','女'];
$type_array=['未知','正式','试用','实习'];
$mobile_array = Db::name('Admin')->where([['status','>=',0]])->column('mobile');
$email_array = Db::name('Admin')->where([['status','>=',0]])->column('email');
$department_array = Db::name('Department')->where(['status' => 1])->column('title', 'id');
$position_array = Db::name('Position')->where(['status' => 1])->column('title', 'id');
//循环读取excel表格整合成数组。如果是不指定key的二维就用$data[i][j]表示。
$pinyin = new Pinyin();
for ($j = 3; $j <= $highestRow; $j++) {
$salt = set_salt(20);
$reg_pwd = '123456';
$name = $objPHPExcel->getActiveSheet()->getCell("A" . $j)->getValue();
if(empty($name)){
continue;
}
$char = mb_substr($name, 0, 1, 'utf-8');
$sex = arraySearch($sex_array,$objPHPExcel->getActiveSheet()->getCell("D" . $j)->getValue());
$department = arraySearch($department_array,$objPHPExcel->getActiveSheet()->getCell("E" . $j)->getValue());
$position = arraySearch($position_array,$objPHPExcel->getActiveSheet()->getCell("f" . $j)->getValue());
$type = arraySearch($type_array,$objPHPExcel->getActiveSheet()->getCell("G" . $j)->getValue());
$username = $pinyin->name($name,PINYIN_UMLAUT_V);
$mobile = $objPHPExcel->getActiveSheet()->getCell("B" . $j)->getValue();
$email = $objPHPExcel->getActiveSheet()->getCell("C" . $j)->getValue();
$file_check['mobile'] = $mobile;
$file_check['email'] = $email;
$validate_mobile = \think\facade\Validate::rule([
'mobile' => 'require|mobile',
]);
$validate_email = \think\facade\Validate::rule([
'email' => 'email',
]);
if (!$validate_mobile->check($file_check)) {
return to_assign(1, '第'.($j - 2).'行的手机号码'.$validate->getError());
}
else{
if(in_array($mobile,$mobile_array)){
return to_assign(1, '第'.($j - 2).'行的手机号码已存在或者重复');
}
else{
array_push($mobile_array,$mobile);
}
}
if(!empty($email)){
if (!$validate_email->check($file_check)) {
return to_assign(1, '第'.($j - 2).'行的电子邮箱'.$validate->getError());
}
else{
if(in_array($email,$email_array)){
return to_assign(1, '第'.($j - 2).'行的电子邮箱已存在或者重复');
}
else{
array_push($email_array,$email);
}
}
}
else{
$email='';
}
if(empty($department)){
return to_assign(1, '第'.($j - 2).'行的所在部门错误');
}
if(empty($department)){
return to_assign(1, '第'.($j - 2).'行的所在部门错误');
}
if(empty($position)){
return to_assign(1, '第'.($j - 2).'行的所属职位错误');
}
$data[$j - 3] = [
'name' => $name,
'nickname' => $name,
'mobile' => $mobile,
'email' => $email,
'sex' => $sex,
'did' => $department,
'position_id' => $position,
'type' => $type,
'entry_time' => Shared::excelToTimestamp($objPHPExcel->getActiveSheet()->getCell("H" . $j)->getValue(),'Asia/Shanghai'),
'username' => implode('', $username),
'salt' => $salt,
'pwd' => set_password($reg_pwd, $salt),
'reg_pwd' => $reg_pwd,
'thumb' => $this->to_avatars($char)
];
}
//dd($data);exit;
// 批量添加数据
if ((new Admin())->saveAll($data)) {
return to_assign(0, '导入成功');
}
else{
return to_assign(1, '导入失败请检查excel文件再试');
}
} catch (\think\exception\ValidateException $e) {
return to_assign(1, $e->getMessage());
}
}
}

View File

@ -661,6 +661,25 @@ function get_desc_content($content, $count)
return $res;
}
//查找数组索引
function arraySearch($array, $searchFor) {
foreach($array as $key => $value) {
if(is_array($value)){
foreach($value as $key1 => $value1) {
if($value1 == $searchFor) {
return array("index" => $key, "key" => $key1);
}
}
}
else{
if($value == $searchFor) {
return $key;
}
}
}
return false;
}
/**
* PHP去除空格
* @param string $str 字符串

View File

@ -9,6 +9,11 @@
.layui-tree-line .layui-tree-set .layui-tree-set:after{top:18px;}
.tree-left{width:200px; float:left; height:calc(100% - 30px); overflow: scroll; border:1px solid #eeeeee; background-color:#FAFAFA; padding:12px 12px 12px 5px;}
.tree-left h3{font-size:16px; height:30px; padding-left:10px; font-weight:800}
.gougu-upload-files{background-color: #ffffff; border:1px solid #e4e7ed;color: #c0c4cc;cursor: not-allowed; padding:0 12px; width:180px; box-sizing: border-box; display: inline-block; font-size: inherit; height: 38px; line-height: 35px; margin-right:8px; border-radius:2px;}
.gougu-upload-tips{color:#969696}
.layui-form-item{margin-bottom:8px;}
.layui-input-block{min-height:24px;}
</style>
{/block}
<!-- 主体 -->
@ -52,6 +57,7 @@
<button class="layui-btn layui-btn-normal layui-btn-sm" lay-event="add"><i class="layui-icon">&#xe61f;</i>添加员工</button>
<button class="layui-btn layui-btn-danger layui-btn-sm" lay-event="disable"><i class="layui-icon">&#x1006;</i>禁止登录</button>
<button class="layui-btn layui-btn-sm" lay-event="recovery"><i class="layui-icon">&#xe605;</i>恢复正常</button>
<button class="layui-btn layui-btn-normal layui-btn-sm" lay-event="import"><i class="layui-icon">&#xe66f;</i>批量导入</button>
</div>
</script>
@ -63,30 +69,117 @@
<script>
const moduleInit = ['tool'];
function gouguInit() {
var table = layui.table, tool = layui.tool,tree = layui.tree,form = layui.form;
$.ajax({
url: "/api/index/get_department_tree",
type:'get',
success:function(res){
//仅节点左侧图标控制收缩
tree.render({
elem: '#depament',
data: res.trees,
onlyIconControl: true, //是否仅允许节点左侧图标控制展开收缩
click: function(obj){
//layer.msg(JSON.stringify(obj.data));
layui.pageTable.reload({
where: {did: obj.data.id}
,page:{curr:1}
var table = layui.table, tool = layui.tool,tree = layui.tree,form = layui.form,upload = layui.upload;
$.ajax({
url: "/api/index/get_department_tree",
type:'get',
success:function(res){
//仅节点左侧图标控制收缩
tree.render({
elem: '#depament',
data: res.trees,
onlyIconControl: true, //是否仅允许节点左侧图标控制展开收缩
click: function(obj){
//layer.msg(JSON.stringify(obj.data));
layui.pageTable.reload({
where: {did: obj.data.id}
,page:{curr:1}
});
$('[name="keywords"]').val('');
$('[name="status"]').val('');
$('[name="type"]').val('');
layui.form.render('select');
}
});
}
})
let uploadFiles;
function clearFile() {
for (let x in uploadFiles) {
delete uploadFiles[x];
}
$('#gougu-upload-choosed').html('');
}
function uploadImport(){
layer.open({
'title':'批量导入员工',
'type':1,
'area': ['640px', '320px'],
'content':'<div class="layui-form p-3">\
<div id="uploadType1">\
<div class="layui-form-item">\
<label class="layui-form-label">文件:</label>\
<div class="layui-input-block">\
<span class="gougu-upload-files">.xls,.xlsx</span><button type="button" class="layui-btn layui-btn-normal" id="uploadSelect">选择文件</button><a href="/static/home/file/勾股OA员工导入模板.xlsx" class="layui-btn ml-4">Execl表格模板下载</a>\
</div>\
</div>\
<div class="layui-form-item">\
<label class="layui-form-label"></label>\
<div class="layui-input-block">\
<span class="gougu-upload-tips">1、只有超级管理员才能进行批量导入操作<br>2、只能上传 .xls、.xlsx文件<br>3、数据请勿放在合并的单元格中<br>4、文件大小请勿超过2MB导入数据不能超过3000条</span>\
</div>\
</div>\
<div class="layui-form-item">\
<label class="layui-form-label"></label>\
<div class="layui-input-block green" id="gougu-upload-choosed"></div>\
</div>\
<div class="layui-form-item">\
<label class="layui-form-label"></label>\
<div class="layui-input-block red" id="gougu-upload-note"></div>\
</div>\
<div class="layui-form-item layui-form-item-sm">\
<label class="layui-form-label"></label>\
<div class="layui-input-block">\
<button type="button" class="layui-btn" id="uploadAjax">上传并导入</button>\
</div>\
</div>\
</div> \
</div>',
success: function(layero, idx){
form.render();
//选文件
let uploadImport = upload.render({
elem: '#uploadSelect'
,url: '/api/import/import_admin'
,auto: false
,accept: 'file' //普通文件
,exts: 'xls|xlsx' //只允许上传文件格式
,bindAction: '#uploadAjax'
,choose: function(obj){
uploadFiles = obj.pushFile();
// 清空,防止多次上传
clearFile();
obj.preview(function(index, file, result){
obj.pushFile();// 再添加
$('#gougu-upload-choosed').html('已选择:'+file.name);
});
$('[name="keywords"]').val('');
$('[name="status"]').val('');
$('[name="type"]').val('');
layui.form.render('select');
}
});
,before: function(obj){
}
,progress: function(n, elem, e){
$('#gougu-upload-note').html('文件上转中...');
if(n==100){
$('#gougu-upload-note').html('数据导入中...');
}
}
,error: function(index, upload){
clearFile();
$('#gougu-upload-note').html('数据导入失败,请关闭重试');
}
,done: function(res, index, upload){
clearFile();
layer.msg(res.msg);
$('#gougu-upload-note').html(res.msg);
if(res.code==0){
layer.close(idx);
layui.pageTable.reload();
}
}
});
}
})
});
}
layui.pageTable = table.render({
elem: '#test',
@ -230,6 +323,10 @@
tool.side("/user/user/add");
return;
}
if (obj.event === 'import') {
uploadImport();
return;
}
if(data.length==0){
layer.msg('请选择要操作的员工');
return false;

View File

@ -28,6 +28,7 @@
"topthink/think-captcha": "^3.0",
"phpmailer/phpmailer": "^6.6",
"firebase/php-jwt": "6.1.2",
"phpoffice/phpspreadsheet": "1.19",
"overtrue/pinyin": "^4.0"
},
"require-dev": {

View File

@ -15,7 +15,7 @@ if (empty(file_exists(__DIR__ . '/../vendor/autoload.php'))) {
require __DIR__ . '/../vendor/autoload.php';
// 定义当前版本号
define('CMS_VERSION','4.0.83');
define('CMS_VERSION','4.9.22');
// 定义Layui版本号
define('LAYUI_VERSION','2.7.6');