1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | Description: 自定义字段模块数据Excel导入导出
  4. // +----------------------------------------------------------------------
  5. // | Author: Michael_xu | gengxiaoxu@5kcrm.com
  6. // +----------------------------------------------------------------------
  7. namespace app\admin\model;
  8. use app\admin\model\Common;
  9. use app\admin\model\Message;
  10. use app\work\model\Task;
  11. use com\PseudoQueue as Queue;
  12. use think\Cache;
  13. use PhpOffice\PhpSpreadsheet\Spreadsheet;
  14. class Excel extends Common
  15. {
  16. /**
  17. * 支持自定义字段的表,不包含表前缀
  18. *
  19. * @var array
  20. */
  21. private $types_arr = [
  22. 'crm_leads',
  23. 'crm_customer',
  24. 'crm_contacts',
  25. 'crm_product',
  26. 'admin_user',
  27. 'task'
  28. ];
  29. /**
  30. * 字段类型为 map_address 的地址类型字段,导入导出时占四个字段,四个单元格
  31. */
  32. private $map_address = ['省', '市', '区/县', '详细地址'];
  33. /**
  34. * 导入锁缓存名称
  35. */
  36. const IMPORT_QUEUE = DB_NAME . 'IMPORT_QUEUE';
  37. /**
  38. * 导出锁缓存名称
  39. */
  40. const EXPORT_QUEUE = DB_NAME . 'EXPORT_QUEUE';
  41. /**
  42. *获取excel相关列
  43. **/
  44. public function stringFromColumnIndex($pColumnIndex = 0)
  45. {
  46. static $_indexCache = array();
  47. if (!isset($_indexCache[$pColumnIndex])) {
  48. if ($pColumnIndex < 26) {
  49. $_indexCache[$pColumnIndex] = chr(65 + $pColumnIndex);
  50. } elseif ($pColumnIndex < 702) {
  51. $_indexCache[$pColumnIndex] = chr(64 + ($pColumnIndex / 26)) . chr(65 + $pColumnIndex % 26);
  52. } else {
  53. $_indexCache[$pColumnIndex] = chr(64 + (($pColumnIndex - 26) / 676)) . chr(65 + ((($pColumnIndex - 26) % 676) / 26)) . chr(65 + $pColumnIndex % 26);
  54. }
  55. }
  56. return $_indexCache[$pColumnIndex];
  57. }
  58. /**
  59. * 自定义字段模块导入模板下载
  60. * @param $field_list 自定义字段数据
  61. * @param $types 分类
  62. * @author
  63. **/
  64. public function excelImportDownload($field_list, $types, $save_path = '')
  65. {
  66. $fieldModel = new \app\admin\model\Field();
  67. //实例化主文件
  68. $objPHPExcel = new Spreadsheet();
  69. $objProps = $objPHPExcel->getProperties(); // 设置excel文档的属性
  70. $objProps->setCreator("5kcrm"); //创建人
  71. $objProps->setLastModifiedBy("5kcrm"); //最后修改人
  72. $objProps->setTitle("5kcrm"); //标题
  73. $objProps->setSubject("5kcrm data"); //题目
  74. $objProps->setDescription("5kcrm data"); //描述
  75. $objProps->setKeywords("5kcrm data"); //关键字
  76. $objProps->setCategory("5kcrm"); //种类
  77. $objPHPExcel->setActiveSheetIndex(0); //设置当前的sheet
  78. $objActSheet = $objPHPExcel->getActiveSheet();
  79. $objActSheet->setTitle('悟空软件导入模板' . date('Y-m-d', time())); //设置sheet的标题
  80. //存储Excel数据源到其他工作薄
  81. $objPHPExcel->createSheet();
  82. $subObject = $objPHPExcel->getSheet(1);
  83. $subObject->setTitle('data');
  84. //保护数据源
  85. $subObject->getProtection()->setSheet(true);
  86. $subObject->protectCells('A1:C1000', time());
  87. //填充边框
  88. $styleArray = [
  89. 'borders' => [
  90. 'outline' => [
  91. 'style' => \PHPExcel_Style_Border::BORDER_THICK, //设置边框
  92. 'color' => ['argb' => '#F0F8FF'], //设置颜色
  93. ],
  94. ],
  95. ];
  96. if ($save_path) {
  97. $objActSheet->setCellValue('A2', '错误原因(导入时需删除本列)');
  98. $objActSheet->getColumnDimension('A')->setWidth(40); //设置单元格宽度
  99. $k = 1;
  100. } else {
  101. $k = 0;
  102. }
  103. foreach ($field_list as $field) {
  104. if ($field['form_type'] == 'map_address' && $types == 'crm_customer') {
  105. for ($a = 0; $a <= 3; $a++) {
  106. $objActSheet->getColumnDimension($this->stringFromColumnIndex($k))->setWidth(20); //设置单元格宽度
  107. //如果是所在省的话
  108. $objActSheet->setCellValue($this->stringFromColumnIndex($k) . '2', $this->map_address[$a]);
  109. $k++;
  110. }
  111. } else {
  112. $objActSheet->getColumnDimension($this->stringFromColumnIndex($k))->setWidth(20); //设置单元格宽度
  113. if ($field['form_type'] == 'select' || $field['form_type'] == 'checkbox' || $field['form_type'] == 'radio' || $field['form_type'] == 'category') {
  114. //产品类别
  115. if ($field['form_type'] == 'category' && $field['types'] == 'crm_product') {
  116. $setting = db('crm_product_category')->order('pid asc')->column('name');
  117. } else {
  118. $setting = $field['setting'] ?: [];
  119. }
  120. $select_value = implode(',', $setting);
  121. //解决下拉框数据来源字串长度过大:将每个来源字串分解到一个空闲的单元格中
  122. $str_len = strlen($select_value);
  123. $selectList = array();
  124. if ($str_len >= 255) {
  125. $str_list_arr = explode(',', $select_value);
  126. if ($str_list_arr) {
  127. foreach ($str_list_arr as $i1 => $d) {
  128. $c = $this->stringFromColumnIndex($k) . ($i1 + 1);
  129. $subObject->setCellValue($c, $d);
  130. $selectList[$d] = $d;
  131. }
  132. $endcell = $c;
  133. }
  134. for ($j = 3; $j <= 70; $j++) {
  135. $objActSheet->getStyle($this->stringFromColumnIndex($k) . $j)->getNumberFormat()->setFormatCode(\PHPExcel_Style_NumberFormat::FORMAT_TEXT);//设置单元格格式 (文本)
  136. //数据有效性 start
  137. $objValidation = $objActSheet->getCell($this->stringFromColumnIndex($k) . $j)->getDataValidation();
  138. $objValidation->setType(\PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_LIST)
  139. ->setErrorStyle(\PhpOffice\PhpSpreadsheet\Cell\DataValidation::STYLE_INFORMATION)
  140. ->setAllowBlank(false)
  141. ->setShowInputMessage(true)
  142. ->setShowErrorMessage(true)
  143. ->setShowDropDown(true)
  144. ->setErrorTitle('输入的值有误')
  145. ->setError('您输入的值不在下拉框列表内.')
  146. ->setPromptTitle('--请选择--')
  147. ->setFormula1('data!$' . $this->stringFromColumnIndex($k) . '$1:$' . $this->stringFromColumnIndex($k) . '$' . count(explode(',', $select_value)));
  148. //数据有效性 end
  149. }
  150. } else {
  151. if ($select_value) {
  152. for ($j = 3; $j <= 70; $j++) {
  153. $objActSheet->getStyle($this->stringFromColumnIndex($k) . $j)->getNumberFormat()->setFormatCode(\PHPExcel_Style_NumberFormat::FORMAT_TEXT);//设置单元格格式 (文本)
  154. //数据有效性 start
  155. $objValidation = $objActSheet->getCell($this->stringFromColumnIndex($k) . $j)->getDataValidation();
  156. $objValidation->setType(\PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_LIST)
  157. ->setErrorStyle(\PhpOffice\PhpSpreadsheet\Cell\DataValidation::STYLE_INFORMATION)
  158. ->setAllowBlank(false)
  159. ->setShowInputMessage(true)
  160. ->setShowErrorMessage(true)
  161. ->setShowDropDown(true)
  162. ->setErrorTitle('输入的值有误')
  163. ->setError('您输入的值不在下拉框列表内.')
  164. ->setPromptTitle('--请选择--')
  165. ->setFormula1('"' . $select_value . '"');
  166. //数据有效性 end
  167. }
  168. }
  169. }
  170. }
  171. $objActSheet->getStyle($this->stringFromColumnIndex($k))->getNumberFormat()->setFormatCode(\PHPExcel_Style_NumberFormat::FORMAT_TEXT);//设置单元格格式 (文本)
  172. //检查该字段若必填,加上"*"
  173. $field['name'] = sign_required($field['is_null'], $field['name']);
  174. $objActSheet->setCellValue($this->stringFromColumnIndex($k) . '2', $field['name']);
  175. $k++;
  176. }
  177. }
  178. $max_row = $this->stringFromColumnIndex($k - 1);
  179. $mark_row = $this->stringFromColumnIndex($k);
  180. $objActSheet->mergeCells('A1:' . $max_row . '1');
  181. $objActSheet->getStyle('A1:' . $mark_row . '1')->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER); //水平居中
  182. $objActSheet->getStyle('A1:' . $mark_row . '1')->getAlignment()->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER); //垂直居中
  183. $objActSheet->getRowDimension(1)->setRowHeight(28); //设置行高
  184. $objActSheet->getStyle('A1')->getFont()->getColor()->setARGB('FFFF0000');
  185. $objActSheet->getStyle('A1')->getAlignment()->setWrapText(true);
  186. $objActSheet->getStyle('A1')->getFont()->getColor()->setARGB('FFFF0000');
  187. $objActSheet->getStyle('A1')->getAlignment()->setWrapText(true);
  188. //设置单元格格式范围的字体、字体大小、加粗
  189. $objActSheet->getStyle('A1:' . $max_row . '1')->getFont()->setName("微软雅黑")->setSize(13)->getColor()->setARGB('#00000000');
  190. //给单元格填充背景色
  191. $objActSheet->getStyle('A1:' . $max_row . '1')->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)->getStartColor()->setARGB('#ff9900');
  192. switch ($types) {
  193. case 'crm_leads' :
  194. $types_name = '线索信息';
  195. $type_name = 'leads';
  196. break;
  197. case 'crm_customer' :
  198. $types_name = '客户信息';
  199. $type_name = 'customer';
  200. break;
  201. case 'crm_contacts' :
  202. $types_name = '联系人信息';
  203. $type_name = 'contacts';
  204. break;
  205. case 'crm_product' :
  206. $types_name = '产品信息';
  207. $type_name = 'product';
  208. break;
  209. case 'crm_business' :
  210. $types_name = '商机信息';
  211. $type_name = 'business';
  212. break;
  213. case 'crm_contract' :
  214. $types_name = '合同信息';
  215. $type_name = 'contract';
  216. break;
  217. case 'crm_receivables' :
  218. $types_name = '回款信息';
  219. $type_name = 'receivables';
  220. break;
  221. case 'admin_user' :
  222. $types_name = '员工信息';
  223. $type_name = 'user';
  224. break;
  225. default :
  226. $types_name = '悟空软件';
  227. $type_name = 'WuKong';
  228. break;
  229. }
  230. $content = $types_name . '(*代表必填项;时间格式:' . date('Y-m-d H:i:s') . ')';
  231. $objActSheet->setCellValue('A1', $content);
  232. $objWriter = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($objPHPExcel, 'Xls');
  233. ob_end_clean();
  234. if ($save_path) {
  235. $objWriter->save($save_path);
  236. } else {
  237. header("Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
  238. header("Content-Disposition:attachment;filename=" . $type_name .'_'. date('Y-m-d') . ".xls");
  239. header("Pragma:no-cache");
  240. header("Expires:0");
  241. $objWriter = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($objPHPExcel, 'Xls');
  242. $objWriter->save('php://output');
  243. }
  244. }
  245. /**
  246. * 自定义字段模块导出csv
  247. * @param $file_name 导出文件名称
  248. * @param $field_list 导出字段列表
  249. * @param $callback 回调函数,查询需要导出的数据
  250. * @author
  251. **/
  252. public function exportCsv($file_name, $field_list, $callback)
  253. {
  254. $fieldModel = new \app\admin\model\Field();
  255. ini_set('memory_limit', '1024M');
  256. ini_set('max_execution_time', '300');
  257. // set_time_limit(0);
  258. //调试时,先把下面这个两个header注释即可
  259. header("Access-Control-Expose-Headers: Content-Disposition");
  260. header("Content-type:application/vnd.ms-excel;charset=UTF-8");
  261. header("Content-Disposition:attachment;filename=" . $file_name . ".csv");
  262. header('Expires: 0');
  263. header('Cache-control: private');
  264. header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
  265. header('Content-Description: File Transfer');
  266. header('Content-Encoding: UTF-8');
  267. // 加上bom头,防止用office打开时乱码
  268. echo "\xEF\xBB\xBF"; // UTF-8 BOM
  269. // 打开PHP文件句柄,php://output 表示直接输出到浏览器
  270. $fp = fopen('php://output', 'a');
  271. // 将中文标题转换编码,否则乱码
  272. foreach ($field_list as $i => $v) {
  273. $title_cell[$i] = $v['name'];
  274. }
  275. // 将标题名称通过fputcsv写到文件句柄
  276. fputcsv($fp, $title_cell);
  277. // $export_data = $callback(0);
  278. $round = round(1000, 9999);
  279. cache($file_name . $round, $callback['list']);
  280. $sheetContent = cache($file_name . $round);
  281. $sheetCount = $callback['dataCount'];
  282. $forCount = 1000; //每次取出1000个
  283. for ($i = 0; $i <= ceil(round($sheetCount / $forCount, 2)); $i++) {
  284. $_sub = array_slice($sheetContent, ($i) * $forCount, 1000);
  285. foreach ($_sub as $kk => $item) {
  286. $rows = [];
  287. foreach ($field_list as $key => $rule) {
  288. $rows[] = ($key = $item[$key]);
  289. }
  290. fputcsv($fp, $rows);
  291. }
  292. ob_flush();//清除内存
  293. flush();
  294. }
  295. // 将已经写到csv中的数据存储变量销毁,释放内存占用
  296. //$m = memory_get_usage();
  297. Cache::rm($file_name . $round);
  298. ob_flush();
  299. flush();
  300. fclose($fp);
  301. exit();
  302. }
  303. /**
  304. * 分批导出csv
  305. *
  306. * @param string $file_name 下载文件名称
  307. * @param string $temp_file 临时文件名称 (不带 .csv 后缀)
  308. * @param array $field_list 字段表头
  309. * @param int $page 响应页码默认1
  310. * @param callback $callback 回调函数,返回数据
  311. * ($page, $page_size) 查询页码,查询数量
  312. * @param array $config 设置信息
  313. * - response_size 单次请求响应数量 默认2000
  314. * - page_size 单次数据库查询数量 默认 500
  315. * @author Ymob
  316. */
  317. public function batchExportCsv($file_name, $temp_file, $field_list, $page, $callback, $config = [])
  318. {
  319. $queue = new Queue(self::EXPORT_QUEUE, 3);
  320. $export_queue_index = input('export_queue_index');
  321. if (!$export_queue_index) {
  322. if (!$export_queue_index = $queue->makeTaskId()) {
  323. return resultArray(['error' => $queue->error]);
  324. }
  325. } else {
  326. if (!$queue->setTaskId($export_queue_index)) {
  327. return resultArray(['error' => $queue->error]);
  328. }
  329. }
  330. // 已取消
  331. if ($page == -1) {
  332. $queue->dequeue();
  333. return resultArray([
  334. 'data' => [
  335. 'msg' => '导出已取消',
  336. 'page' => -1
  337. ]
  338. ]);
  339. }
  340. // 排队中
  341. if (!$queue->canExec()) {
  342. return resultArray([
  343. 'data' => [
  344. 'page' => -2,
  345. 'export_queue_index' => $export_queue_index,
  346. 'info' => $queue->error
  347. ]
  348. ]);
  349. }
  350. // 没有临时文件名,代表第一次导出,生成临时文件名称,并写入表头数据
  351. if ($temp_file === null) {
  352. // 生成临时文件路径
  353. $file_path = tempFileName('csv');
  354. $fp = fopen($file_path, 'a');
  355. $title_cell = [];
  356. foreach ($field_list as $v) {
  357. if ($v['form_type'] == 'customer_address') {
  358. $title_cell[] = $this->map_address[0];
  359. $title_cell[] = $this->map_address[1];
  360. $title_cell[] = $this->map_address[2];
  361. } else {
  362. $title_cell[] = $v['name'];
  363. }
  364. }
  365. fputcsv($fp, $title_cell);
  366. $temp_file = \substr($file_path, strlen(TEMP_DIR));
  367. } else {
  368. $file_path = TEMP_DIR . $temp_file;
  369. if (!file_exists($file_path)) {
  370. return resultArray(['error' => '参数错误,临时文件不存在']);
  371. }
  372. $fp = fopen($file_path, 'a');
  373. }
  374. // 自定义字段模型
  375. $fieldModel = new \app\admin\model\Field();
  376. // 单次响应条数 (必须是单次查询条数的整数倍)
  377. $response_size = $config['response_size'] ?: 10000;
  378. // 单次查询条数
  379. $page_size = $config['page_size'] ?: 200;
  380. // 最多查询次数
  381. $max_query_count = $response_size / $page_size;
  382. // 总数
  383. $total = 0;
  384. for ($i = 1; $i <= $max_query_count; $i++) {
  385. // 两个参数,第一个参数是 page (传入 model\customer::getDataList 方法的参数),
  386. $data = $callback($i + ($page - 1) * ($response_size / $page_size), $page_size);
  387. $total = $data['dataCount'];
  388. foreach ($data['list'] as $val) {
  389. $val['create_time']=strtotime($val['create_time']);
  390. $val['update_time']=strtotime($val['update_time']);
  391. $val['last_time']=strtotime($val['last_time']);
  392. $val['next_time']=strtotime($val['next_time']);
  393. $rows = [];
  394. foreach ($field_list as $rule) {
  395. if ($rule['form_type'] == 'customer_address') {
  396. $address_arr = explode(chr(10), $val['address']);
  397. $rows[] = $address_arr[0] ?: '';
  398. $rows[] = $address_arr[1] ?: '';
  399. $rows[] = $address_arr[2] ?: '';
  400. } else {
  401. $rows[] = $fieldModel->getValueByFormtype($val[$rule['field']], $rule['form_type']);
  402. }
  403. }
  404. fputcsv($fp, $rows);
  405. }
  406. }
  407. fclose($fp);
  408. // 已查询数据条数 小于 数据总数
  409. $done = $page * $response_size;
  410. if ($done < $total) {
  411. return resultArray([
  412. 'data' => [
  413. 'export_queue_index' => $export_queue_index,
  414. 'temp_file' => $temp_file,
  415. // 总数
  416. 'total' => $total,
  417. // 已完成
  418. 'done' => $done,
  419. // 返回前端页码
  420. 'page' => $page + 1
  421. ]
  422. ]);
  423. }
  424. $res = $queue->dequeue();
  425. // 所有数据已导入 csv 文件,返回文件流完成后删除
  426. return download($file_path, $file_name . '.csv', true);
  427. }
  428. /**
  429. * 分批导入文件
  430. *
  431. * @param null|array|\think\File $file
  432. * @param array $param
  433. * @param Controller $controller
  434. * @return bool
  435. *
  436. * @author Ymob
  437. */
  438. public function batchImportData($file, $param, $controller = null)
  439. {
  440. // 导入模块
  441. $types = $param['types'];
  442. if (!in_array($types, $this->types_arr)) {
  443. $this->error = '参数错误!';
  444. $queue->dequeue();
  445. return false;
  446. }
  447. $user_id = $param['owner_user_id'];
  448. // 采用伪队列 允许三人同时导入数据
  449. $queue = new Queue(self::IMPORT_QUEUE, 5);
  450. $import_queue_index = input('import_queue_index');
  451. // 队列任务ID
  452. if (!$import_queue_index) {
  453. if (!$import_queue_index = $queue->makeTaskId()) {
  454. $this->error = $queue->error;
  455. $queue->dequeue();
  456. return false;
  457. }
  458. } else {
  459. if (!$queue->setTaskId($import_queue_index)) {
  460. $this->error = $queue->error;
  461. $queue->dequeue();
  462. return false;
  463. }
  464. }
  465. // 取消导入
  466. if ($param['page'] == -1) {
  467. @unlink(UPLOAD_PATH . $param['temp_file']);
  468. $this->error = [
  469. 'msg' => '导入已取消',
  470. 'page' => -1
  471. ];
  472. if ($param['error']) {
  473. $this->error['error_file_path'] = 'temp/' . $param['error_file'];
  474. } else {
  475. @unlink(TEMP_DIR . $param['error_file']);
  476. }
  477. $temp = $queue->cache('last_import_cache');
  478. (new ImportRecord())->createData([
  479. 'type' => $types,
  480. 'total' => $temp['total'],
  481. 'done' => $temp['done'],
  482. 'cover' => $temp['cover'],
  483. 'error' => $temp['error'],
  484. 'user_id' => $user_id,
  485. 'error_data_file_path' => $temp['error'] ? 'temp/' . $error_data_file_name : ''
  486. ]);
  487. $queue->dequeue();
  488. return true;
  489. }
  490. if (!empty($file) || $param['temp_file']) {
  491. // 导入初始化 上传文件
  492. if (!empty($file)) {
  493. $save_name = $this->upload($file);
  494. if ($save_name === false) {
  495. $queue->dequeue();
  496. return false;
  497. }
  498. } else {
  499. $save_name = $param['temp_file'];
  500. }
  501. // 文件类型
  502. $ext = pathinfo($save_name, PATHINFO_EXTENSION);
  503. // 文件路径
  504. $save_path = UPLOAD_PATH . $save_name;
  505. // 队列-判断是否需要排队
  506. if (!$queue->canExec()) {
  507. $this->error = [
  508. 'temp_file' => $save_name,
  509. 'page' => -2,
  510. 'import_queue_index' => $import_queue_index,
  511. 'info' => $queue->error
  512. ];
  513. return true;
  514. }
  515. // 加载类库
  516. vendor("phpexcel.PHPExcel");
  517. vendor("phpexcel.PHPExcel.Writer.Excel5");
  518. vendor("phpexcel.PHPExcel.Writer.Excel2007");
  519. vendor("phpexcel.PHPExcel.IOFactory");
  520. // 错误数据临时文件路径 错误数据开始行数
  521. if ($param['error_file']) {
  522. $error_path = TEMP_DIR . $param['error_file'];
  523. $error_row = $param['error'] + 3;
  524. $cover = $param['cover'] ?: 0;
  525. } else {
  526. // 生成临时文件名称
  527. $error_path = tempFileName($ext);
  528. // 将导入模板保存至临时路径
  529. $controller->excelDownload($error_path);
  530. $error_row = 3;
  531. $cover = 0;
  532. }
  533. // 错误数据临时文件名称 相对于临时目录
  534. $error_data_file_name = \substr($error_path, strlen(TEMP_DIR));
  535. // 加载错误数据文件
  536. $err_PHPExcel = \PHPExcel_IOFactory::load($error_path);
  537. $error_sheet = $err_PHPExcel->setActiveSheetIndex(0);
  538. /**
  539. * 添加错误数据到临时文件
  540. *
  541. * @param array $data 原数据
  542. * @param string $error 错误原因
  543. * @return void
  544. */
  545. $error_data_func = function ($data, $error) use ($error_sheet, &$error_row) {
  546. foreach ($data as $key => $val) {
  547. // 第一列为错误原因 所以+1
  548. $error_col = \PHPExcel_Cell::stringFromColumnIndex($key + 1);
  549. $error_sheet->setCellValue($error_col . $error_row, $val);
  550. }
  551. $error_sheet->setCellValue('A' . $error_row, $error);
  552. $error_sheet->getStyle('A' . $error_row)->getFont()->getColor()->setARGB('FF000000');
  553. $error_sheet->getStyle('A' . $error_row)->getFill()->setFillType(\PHPExcel_Style_Fill::FILL_SOLID)->getStartColor()->setARGB('FFFF0000');
  554. $error_row++;
  555. };
  556. // 字段列表条件
  557. $fieldParam = [];
  558. // 导入模块
  559. switch ($types) {
  560. case 'crm_leads' :
  561. $dataModel = new \app\crm\model\Leads();
  562. $db = 'crm_leads';
  563. $db_id = 'leads_id';
  564. break;
  565. case 'crm_customer' :
  566. $dataModel = new \app\crm\model\Customer();
  567. $db = 'crm_customer';
  568. $db_id = 'customer_id';
  569. $fieldParam['form_type'] = ['not in', ['file', 'form', 'user', 'structure']];
  570. break;
  571. case 'crm_contacts' :
  572. $dataModel = new \app\crm\model\Contacts();
  573. $db = 'crm_contacts';
  574. $db_id = 'contacts_id';
  575. break;
  576. case 'crm_product' :
  577. $model = db('crm_product');
  578. $dataModel = new \app\crm\model\Product();
  579. $db = 'crm_product';
  580. $db_id = 'product_id';
  581. // 产品分类
  582. $productCategoryArr = db('crm_product_category')->field('category_id', 'name')->select();
  583. break;
  584. case 'admin_user' :
  585. $dataModel = new User();
  586. $db_id = 'id';
  587. break;
  588. }
  589. // 字段
  590. $fieldModel = new \app\admin\model\Field();
  591. $fieldParam['types'] = $types;
  592. $fieldParam['action'] = 'excel';
  593. $field_list = $fieldModel->field($fieldParam);
  594. $field_list = array_map(function ($val) {
  595. if (method_exists($val, 'toArray')) {
  596. return $val->toArray();
  597. } else {
  598. return $val;
  599. }
  600. }, $field_list);
  601. $field_key_name_list = array_column($field_list, 'name', 'field');
  602. // 加载导入数据文件
  603. $objRender = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xls');
  604. $objRender->setReadDataOnly(true);
  605. $ExcelObj = $objRender->load($save_path);
  606. // 指定工作表
  607. $sheet = $ExcelObj->getSheet(0);
  608. // 总行数
  609. $max_row = $sheet->getHighestRow();
  610. // 最大列数
  611. $max_col_num = count($field_list) - 1;
  612. // customer_address地址类字段 占4列
  613. $max_col_num += 3 * array_count_values(array_column($field_list, 'form_type'))['map_address'];
  614. $max_col = \PHPExcel_Cell::stringFromColumnIndex($max_col_num);
  615. // 检测导入文件是否使用最新模板
  616. $header = $sheet->rangeToArray("A2:{$max_col}2")[0];
  617. $temp = 0;
  618. for ($i = 0; $i < count($field_list); $i++) {
  619. if (
  620. $header[$i] == $field_list[$i]['name']
  621. || $header[$i] == '*' . $field_list[$i]['name']
  622. ) {
  623. $temp++;
  624. // 字段为地址时,占四列
  625. } elseif ($field_list[$i]['form_type'] == 'map_address') {
  626. if (
  627. $header[$i] == $this->map_address[0]
  628. && $header[$i + 1] == $this->map_address[1]
  629. && $header[$i + 2] == $this->map_address[2]
  630. && $header[$i + 3] == $this->map_address[3]
  631. ) {
  632. $temp++;
  633. }
  634. }
  635. }
  636. // 每次导入条数
  637. $page_size = 100;
  638. // 当前页码
  639. $page = ((int)$param['page']) ?: 1;
  640. // 数据总数
  641. $total = $max_row - 2;
  642. // 总页数
  643. $max_page = ceil($total / $page_size);
  644. if ($page > $max_page) {
  645. // $this->error = 'page参数错误';
  646. // @unlink($save_path);
  647. // $queue->dequeue();
  648. // return false;
  649. }
  650. // 开始行 +3 跳过表头
  651. $start_row = ($page - 1) * $page_size + 3;
  652. // 结束行
  653. $end_row = $start_row + $page_size - 1;
  654. if ($end_row > $max_row) {
  655. $end_row = $max_row;
  656. }
  657. // 读取数据
  658. $dataList = $sheet->rangeToArray("A{$start_row}:{$max_col}{$end_row}");
  659. // 数据重复时的处理方式 0跳过 1覆盖
  660. $config = $param['config'] ? : 0;
  661. // 默认数据
  662. $default_data = [
  663. 'create_user_id' => $param['create_user_id'],
  664. 'owner_user_id' => $param['owner_user_id'],
  665. 'create_time' => time(),
  666. 'update_time' => time(),
  667. ];
  668. if ($temp !== count($field_list)) {
  669. // $this->error = '请使用最新导入模板';
  670. @unlink($save_path);
  671. $queue->dequeue();
  672. foreach ($dataList as $val) {
  673. $error_data_func($val,'请使用最新导入模板');
  674. }
  675. // 错误数据文件保存
  676. $objWriter = \PHPExcel_IOFactory::createWriter($err_PHPExcel, 'Excel5');
  677. $objWriter->save($error_path);
  678. $error = [
  679. // 文件总计条数
  680. 'total' => $total,
  681. // 已完成条数
  682. 'done' => 0,
  683. // 覆盖
  684. 'cover' => 0,
  685. // 错误数据写入行号
  686. 'error' => $total,
  687. 'error_file_path' =>'temp/' . $error_data_file_name
  688. ];
  689. $queue->cache('last_import_cache', [
  690. 'total' => $total,
  691. 'done' => 0,
  692. 'cover' => 0,
  693. 'error' => $total
  694. ]);
  695. (new ImportRecord())->createData([
  696. 'type' => $types,
  697. 'total' => $total,
  698. 'done' => 0,
  699. 'cover' => 0,
  700. 'error' => $total,
  701. 'user_id' => $user_id,
  702. 'error_data_file_path' =>'temp/' . $error_data_file_name
  703. ]);
  704. Cache::rm('item');
  705. Cache::rm('excel_item');
  706. Cache::set('item', 1);
  707. Cache::set('excel_item', serialize($error));
  708. return true;
  709. }else{
  710. // 开始导入数据
  711. foreach ($dataList as $val) {
  712. $data = [];
  713. $unique_where = [];
  714. $empty_count = 0;
  715. $not_null_field = [];
  716. $fk = 0;
  717. foreach ($field_list as $field) {
  718. if ($field['form_type'] == 'map_address') {
  719. $data['address'] = $address = [
  720. trim((string)$val[$fk]),
  721. trim((string)$val[$fk + 1]),
  722. trim((string)$val[$fk + 2]),
  723. ];
  724. $data['detail_address'] = trim($val[$fk + 3]);
  725. $fk += 4;
  726. continue;
  727. } else {
  728. $temp_value = trim($val[$fk]);
  729. }
  730. if ($field['field'] == 'category_id' && $types == 'crm_product') {
  731. $data['category_id'] = $productCategoryArr[$temp_value] ? : 0;
  732. $data['category_str'] = $dataModel->getPidStr($productCategoryArr[$temp_value], '', 1);
  733. }
  734. // 特殊字段特殊处理
  735. $temp_value = $this->handleData($temp_value, $field);
  736. $data[$field['field']] = $temp_value;
  737. // 查重字段
  738. if ($field['is_unique'] && $temp_value) {
  739. $unique_where[$field['field']] = $temp_value;
  740. }
  741. if ($temp_value == '') {
  742. if ($field['is_null']) {
  743. $not_null_field[] = $field['name'];
  744. }
  745. $empty_count++;
  746. }
  747. $fk++;
  748. }
  749. if (!empty($not_null_field)) {
  750. $error_data_func($val, implode(', ', $not_null_field) . '不能为空');
  751. continue;
  752. }
  753. if ($empty_count == count($field_list)) {
  754. $error_data_func($val, '空行');
  755. continue;
  756. }
  757. $old_data_id_list = [];
  758. if ($unique_where) {
  759. if($types == 'crm_product'){
  760. $old_data_id_list = $model->whereOr($unique_where)->where('delete_user_id',0)->column($db_id);
  761. }else{
  762. $old_data_id_list = $dataModel->whereOr($unique_where)->column($db_id);
  763. }
  764. }
  765. // 数据重复时
  766. if ($old_data_id_list) {
  767. // 是否覆盖
  768. if ($config) {
  769. $data = array_merge($data, $default_data);
  770. $data['user_id'] = $param['create_user_id'];
  771. $data['update_time'] = time();
  772. $data['update_time'] = time();
  773. if(isset($data['next_time'])){
  774. $data['next_time']=$data['next_time']?date('Y-m-d H:i:s', $data['next_time']):'';
  775. }
  776. $dataModel->startTrans();
  777. try {
  778. $up_success_count = 0;
  779. foreach ($old_data_id_list as $id) {
  780. if($types=='crm_customer'){
  781. $owner = db('crm_customer')->where(['name' => $data['name']])->find();
  782. if (!empty($owner) && $owner['owner_user_id'] == 0) {
  783. $temp_error = $owner['name'] .' '. '公海数据,无覆盖权限';
  784. $error_data_func($val, $temp_error);
  785. break;
  786. }
  787. }
  788. if (!$dataModel->updateDataById($data, $id)) {
  789. $temp_error = $dataModel->getError();
  790. if ($temp_error == '无权操作') {
  791. $temp_error = '当前导入人员对该数据无写入权限';
  792. }
  793. $error_data_func($val, $temp_error);
  794. $dataModel->rollback();
  795. break;
  796. }
  797. $up_success_count++;
  798. }
  799. // 全部更新完成
  800. if ($up_success_count === count($old_data_id_list)) {
  801. $cover++;
  802. $dataModel->commit();
  803. }
  804. } catch (\Exception $e) {
  805. $dataModel->rollback();
  806. }
  807. } else {
  808. // 重复字段标记
  809. $unique_field = [];
  810. foreach ($old_data_id_list as $id) {
  811. $old_data = $dataModel->getDataById($id);
  812. foreach ($unique_where as $k => $v) {
  813. if (trim($old_data[$k]) == $v) {
  814. $unique_field[] = $field_key_name_list[$k];
  815. }
  816. }
  817. }
  818. $unique_field = array_unique($unique_field);
  819. $error_data_func($val, implode(', ', $unique_field) . ' 根据查重规则,该条数据重复');
  820. }
  821. } else {
  822. $data = array_merge($data, $default_data);
  823. if(isset($data['next_time'])){
  824. $data['next_time']=$data['next_time']?date('Y-m-d H:i:s', $data['next_time']):'';
  825. }
  826. if (!$resData = $dataModel->createData($data)) {
  827. $error_data_func($val, $dataModel->getError());
  828. }
  829. }
  830. }
  831. // 完成数(已导入数)
  832. $done = ($page - 1) * $page_size + count($dataList);
  833. if ($page == $max_page) {
  834. $done = $total;
  835. }
  836. // 错误数
  837. $error = $error_row - 3;
  838. // 错误数据文件保存
  839. $objWriter = \PHPExcel_IOFactory::createWriter($err_PHPExcel, 'Excel5');
  840. $objWriter->save($error_path);
  841. $this->error = [
  842. // 数据导入文件临时路径
  843. 'temp_file' => $save_name,
  844. // 错误数据文件路径
  845. 'error_file' => $error_data_file_name,
  846. // 文件总计条数
  847. 'total' => $total,
  848. // 已完成条数
  849. 'done' => $done,
  850. // 覆盖
  851. 'cover' => $cover,
  852. // 错误数据写入行号
  853. 'error' => $error,
  854. // 下次页码
  855. 'page' => $page + 1,
  856. // 导入任务ID
  857. 'import_queue_index' => $import_queue_index
  858. ];
  859. $queue->cache('last_import_cache', [
  860. 'total' => $total,
  861. 'done' => $done,
  862. 'cover' => $cover,
  863. 'error' => $error
  864. ]);
  865. // 执行完成
  866. if ($done >= $total) {
  867. // 出队
  868. $queue->dequeue();
  869. // 错误数据文件路径
  870. $this->error['error_file_path'] = 'temp/' . $error_data_file_name;
  871. // 删除导入文件
  872. @unlink($save_path);
  873. // 没有错误数据时,删除错误文件
  874. if ($error == 0) {
  875. @unlink($error_path);
  876. }
  877. (new ImportRecord())->createData([
  878. 'type' => $types,
  879. 'total' => $total,
  880. 'done' => $done,
  881. 'cover' => $cover,
  882. 'error' => $error,
  883. 'user_id' => $user_id,
  884. 'error_data_file_path' => $error ? 'temp/' . $error_data_file_name : ''
  885. ]);
  886. Cache::set('item', 1);
  887. Cache::set('excel_item', serialize($this->error));
  888. }else{
  889. $excelData['cover']=$cover;
  890. $excelData['page']=$page+1;
  891. $excelData['types']=$types;
  892. $excelData['temp_file']=$save_name;
  893. $excelData['error_file']=$error_data_file_name;
  894. $excelData['create_user_id']=$param['create_user_id'];
  895. $excelData['import_queue_index']=$import_queue_index;
  896. $excelData['config']=$config;
  897. $excelData['owner_user_id']=$user_id;
  898. $excelData['base']='batchImportData';
  899. Cache::set('item', 0);
  900. Cache::set('excel', $excelData);
  901. }
  902. return true;
  903. }
  904. } else {
  905. $this->error = '请选择导入文件';
  906. $queue->dequeue();
  907. return false;
  908. }
  909. }
  910. /**
  911. * 导入数据时 读取xls表格数据
  912. *
  913. * @param PHPExcel_Worksheet $sheet
  914. * @param integer $start 开始行
  915. * @param integer $end 结束行 0时表示所有
  916. * @param array $fields 字段名称
  917. * @return array
  918. * @author Ymob
  919. */
  920. public function readSheet($sheet, $start = 1, $end = 0, $fields = [])
  921. {
  922. $data = [];
  923. for ($row = $start; $row <= $end; $row++) {
  924. $temp = [];
  925. foreach ($fields as $key => $field) {
  926. $col = Coordinate::stringFromColumnIndex($key);
  927. $temp[$field] = $sheet->getCell($col . $row);
  928. }
  929. $data[] = $temp;
  930. }
  931. return $data;
  932. }
  933. /**
  934. * 上传文件导入数据文件
  935. *
  936. * @param [type] $file
  937. * @return mixed 上传文件路径 | 上传失败错误信息
  938. * @author Ymob
  939. */
  940. public function upload($file)
  941. {
  942. $get_filesize_byte = get_upload_max_filesize_byte();
  943. $info = $file->validate(['size' => $get_filesize_byte, 'ext' => 'xls'])->move(FILE_PATH . 'public' . DS . 'uploads'); //验证规则
  944. if (!$info) {
  945. $this->error = $file->getError();
  946. return false;
  947. }
  948. $saveName = $info->getSaveName(); //保存路径
  949. if (!$saveName) {
  950. $this->error = '文件上传失败,请重试!';
  951. return false;
  952. }
  953. return $saveName;
  954. }
  955. /**
  956. * 自定义字段模块数据导入(默认2000行)
  957. * @param $types 分类
  958. * @param $file 导入文件
  959. * @param $create_user_id 创建人ID
  960. * @param $owner_user_id 负责人ID
  961. * @return todo 导入记录
  962. * @author Michael_xu
  963. */
  964. public function importExcel($file, $param, $controller = null)
  965. {
  966. $queue = new Queue(self::IMPORT_QUEUE, 1);
  967. $import_queue_index = input('import_queue_index');
  968. $user_id = $param['owner_user_id'];
  969. if (!$import_queue_index) {
  970. if (!$import_queue_index = $queue->makeTaskId()) {
  971. $this->error = $queue->error;
  972. $queue->dequeue();
  973. return false;
  974. }
  975. } else {
  976. if (!$queue->setTaskId($import_queue_index)) {
  977. $this->error = $queue->error;
  978. $queue->dequeue();
  979. return false;
  980. }
  981. }
  982. if ($param['page'] == -1) {
  983. $queue->dequeue();
  984. $this->error = [
  985. 'msg' => '导入已取消',
  986. 'page' => -1
  987. ];
  988. if ($param['error']) {
  989. $this->error['error_file_path'] = 'temp/' . $param['error_file'];
  990. }
  991. return true;
  992. }
  993. $config = $param['config'] ?: '';
  994. if (!empty($file) || $param['temp_file']) {
  995. $types = $param['types'];
  996. if (!in_array($types, $this->types_arr)) {
  997. $this->error = '参数错误!';
  998. $queue->dequeue();
  999. return false;
  1000. }
  1001. // 导入初始化 上传文件
  1002. if (!empty($file)) {
  1003. $get_filesize_byte = get_upload_max_filesize_byte();
  1004. $info = $file->validate(['size' => $get_filesize_byte, 'ext' => 'xls,xlsx,csv'])->move(UPLOAD_PATH); //验证规则
  1005. if (!$info) {
  1006. $this->error = $file->getError();
  1007. $queue->dequeue();
  1008. return false;
  1009. }
  1010. $save_name = $info->getSaveName(); //保存路径
  1011. if (!$save_name) {
  1012. $this->error = '文件上传失败,请重试!';
  1013. $queue->dequeue();
  1014. return false;
  1015. }
  1016. } else {
  1017. $save_name = $param['temp_file'];
  1018. }
  1019. $ext = pathinfo($save_name, PATHINFO_EXTENSION); //文件后缀
  1020. $save_path = UPLOAD_PATH . $save_name;
  1021. if (!$queue->canExec()) {
  1022. $this->error = [
  1023. 'temp_file' => $save_name,
  1024. 'page' => -2,
  1025. 'import_queue_index' => $import_queue_index,
  1026. 'info' => $queue->error
  1027. ];
  1028. return true;
  1029. }
  1030. if ($param['error_file']) {
  1031. $error_path = TEMP_DIR . $param['error_file'];
  1032. $error_row = $param['error'] + 3;
  1033. } else {
  1034. $error_path = tempFileName($ext);
  1035. // 生成错误数据文件
  1036. $controller->excelDownload($error_path);
  1037. $error_row = 3;
  1038. }
  1039. vendor("phpexcel.PHPExcel");
  1040. vendor("phpexcel.PHPExcel.Writer.Excel5");
  1041. vendor("phpexcel.PHPExcel.Writer.Excel2007");
  1042. vendor("phpexcel.PHPExcel.IOFactory");
  1043. $err_PHPExcel = \PHPExcel_IOFactory::load($error_path);
  1044. $sheet = $err_PHPExcel->setActiveSheetIndex(0);
  1045. // 添加错误数据到临时文件
  1046. $error_data_func = function ($data, $error) use ($sheet, &$error_row) {
  1047. foreach ($data as $key => $val) {
  1048. // 第一列为错误原因 所以+1
  1049. $error_col = \PHPExcel_Cell::stringFromColumnIndex($key + 1);
  1050. $sheet->setCellValue($error_col . $error_row, $val);
  1051. }
  1052. $sheet->setCellValue('A' . $error_row, $error);
  1053. $sheet->getStyle('A' . $error_row)->getFont()->getColor()->setARGB('FF000000');
  1054. $sheet->getStyle('A' . $error_row)->getFill()->setFillType(\PHPExcel_Style_Fill::FILL_SOLID)->getStartColor()->setARGB('FFFF0000');
  1055. $error_row++;
  1056. };
  1057. // 错误数据临时文件名称
  1058. $error_data_file_name = \substr($error_path, strlen(TEMP_DIR));
  1059. //实例化主文件
  1060. set_time_limit(1800);
  1061. ini_set("memory_limit", "256M");
  1062. $objPHPExcel = new Spreadsheet();
  1063. if ($ext == 'xlsx') {
  1064. $objRender = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx');
  1065. // $objRender->setReadDataOnly(true);
  1066. $ExcelObj = $objRender->load($save_path);
  1067. } elseif ($ext == 'xls') {
  1068. $objRender = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xls');
  1069. // $objRender->setReadDataOnly(true);
  1070. $ExcelObj = $objRender->load($save_path);
  1071. } elseif ($ext == 'csv') {
  1072. $objWriter = new \PhpOffice\PhpSpreadsheet\Reader\Csv($objPHPExcel);
  1073. //默认输入字符集
  1074. $objWriter->setInputEncoding('UTF-8');
  1075. //默认的分隔符
  1076. $objWriter->setDelimiter(',');
  1077. //载入文件
  1078. $ExcelObj = $objWriter->load($save_path);
  1079. }
  1080. $currentSheet = $ExcelObj->getSheet(0);
  1081. $data = $currentSheet->rangeToArray('A3:C12');
  1082. //查看有几个sheet
  1083. $sheetContent = $ExcelObj->getSheet(0)->toArray();
  1084. //获取总行数
  1085. $sheetCount = $ExcelObj->getSheet(0)->getHighestRow();
  1086. // if ($sheetCount > 2002) {
  1087. // $this->error = '单文件一次最多导入2000条数据';
  1088. // return false;
  1089. // }
  1090. //读取表头
  1091. $excelHeader = $sheetContent[1];
  1092. unset($sheetContent[0]);
  1093. unset($sheetContent[1]);
  1094. //取出文件的内容描述信息,循环取出数据,写入数据库
  1095. switch ($types) {
  1096. case 'crm_leads' :
  1097. $dataModel = new \app\crm\model\Leads();
  1098. $db = 'crm_leads';
  1099. $db_id = 'leads_id';
  1100. break;
  1101. case 'crm_customer' :
  1102. $dataModel = new \app\crm\model\Customer();
  1103. $db = 'crm_customer';
  1104. $db_id = 'customer_id';
  1105. $fieldParam['form_type'] = array('not in', ['file', 'form', 'user', 'structure']);
  1106. break;
  1107. case 'crm_contacts' :
  1108. $dataModel = new \app\crm\model\Contacts();
  1109. $db = 'crm_contacts';
  1110. $db_id = 'contacts_id';
  1111. break;
  1112. case 'crm_product' :
  1113. $dataModel = new \app\crm\model\Product();
  1114. $db = 'crm_product';
  1115. $db_id = 'product_id';
  1116. break;
  1117. }
  1118. $contactsModel = new \app\crm\model\Contacts();
  1119. //自定义字段
  1120. $fieldModel = new \app\admin\model\Field();
  1121. $fieldParam['types'] = $types;
  1122. $field_list = $fieldModel->getDataList($fieldParam);
  1123. $fieldArr = [];
  1124. $uniqueField = []; //验重字段
  1125. foreach ($field_list as $k => $v) {
  1126. $fieldArr[$v['name']]['field'] = $v['field'];
  1127. $fieldArr[$v['name']]['form_type'] = $v['form_type'];
  1128. if ($v['is_unique'] == 1) {
  1129. $uniqueField[] = $v['field'];
  1130. }
  1131. }
  1132. $field_num = count($field_list);
  1133. //客户导入联系人
  1134. if ($types == 'crm_customer') {
  1135. $contacts_field_list = $fieldModel->getDataList(['types' => 'crm_contacts', 'field' => array('neq', 'customer_id')]);
  1136. $contactsFieldArr = [];
  1137. foreach ($contacts_field_list as $k => $v) {
  1138. $contactsFieldArr[$v['name']]['field'] = $v['field'];
  1139. $contactsFieldArr[$v['name']]['form_type'] = $v['form_type'];
  1140. }
  1141. }
  1142. $defaultData = []; //默认数据
  1143. $defaultData['create_user_id'] = $param['create_user_id'];
  1144. $defaultData['owner_user_id'] = $param['owner_user_id'];
  1145. $defaultData['create_time'] = time();
  1146. $defaultData['update_time'] = time();
  1147. //产品类别
  1148. if ($types == 'crm_product') {
  1149. $productCategory = db('crm_product_category')->select();
  1150. $productCategoryArr = [];
  1151. foreach ($productCategory as $v) {
  1152. $productCategoryArr[$v['name']] = $v['category_id'];
  1153. }
  1154. }
  1155. // 表头行数
  1156. $keys = 2;
  1157. // 导入错误数据
  1158. $errorMessage = [];
  1159. // 每次导入条数
  1160. $forCount = 5;
  1161. // 当前页码
  1162. $page = $param['page'] ?: 1;
  1163. // 数据总数
  1164. $total = $sheetCount - $keys;
  1165. // 总页数
  1166. $max_page = ceil($total / $forCount);
  1167. if ($page > $max_page) {
  1168. $this->error = 'page参数错误';
  1169. $queue->dequeue();
  1170. return false;
  1171. }
  1172. $_sub = array_slice($sheetContent, ($page - 1) * $forCount, $forCount);
  1173. foreach ($_sub as $kk => $val) {
  1174. $data = '';
  1175. $contactsData = '';
  1176. $k = 0;
  1177. $contacts_k = $field_num;
  1178. $resNameIds = '';
  1179. $keys++;
  1180. $name = ''; //客户、线索、联系人等名称
  1181. $contactsName = '';
  1182. $data = $defaultData; //导入数据
  1183. $contacts_data = $defaultData; //导入数据
  1184. $resWhere = ''; //验重条件
  1185. $resWhereNum = 0; //验重数
  1186. $resContacts = false; //联系人是否有数据
  1187. $resInfo = false; //Excel列是否有数据
  1188. $resData = [];
  1189. $resContactsData = [];
  1190. $row_error = false;
  1191. foreach ($excelHeader as $aa => $header) {
  1192. if (empty($header)) break;
  1193. $fieldName = trim(str_replace('*', '', $header));
  1194. $info = '';
  1195. $info = trim($val[$k]);
  1196. if ($info) $resInfo = true;
  1197. if ($types == 'crm_product' && $fieldName == '产品类别') {
  1198. $data['category_id'] = $productCategoryArr[$info] ?: 0;
  1199. $data['category_str'] = $dataModel->getPidStr($productCategoryArr[$info], '', 1);
  1200. }
  1201. //联系人
  1202. if ($types == 'crm_contacts' && $fieldName == '客户名称') {
  1203. if (!$info) {
  1204. $error_data_func($val, '客户名称必填'); // 错误数据导出
  1205. $errorMessage[] = '第' . $keys . '行导入错误,失败原因:客户名称必填';
  1206. $row_error = true;
  1207. continue;
  1208. }
  1209. $customer_id = '';
  1210. $customer_id = db('crm_customer')->where(['name' => $info])->value('customer_id');
  1211. if (!$customer_id) {
  1212. $error_data_func($val, '客户名称不存在'); // 错误数据导出
  1213. $errorMessage[] = '第' . $keys . '行导入错误,失败原因:客户名称不存在';
  1214. $row_error = true;
  1215. continue;
  1216. }
  1217. $data['customer_id'] = $customer_id;
  1218. }
  1219. if ($aa < $field_num) {
  1220. if (empty($fieldArr[$fieldName]['field'])) continue;
  1221. // if ($fieldArr[$fieldName]['field'] == 'name') $name = $info;
  1222. if (in_array($fieldArr[$fieldName]['field'], $uniqueField) && $info) {
  1223. if ($resWhereNum > 0) $resWhere .= " OR ";
  1224. $resWhere .= " `" . $fieldArr[$fieldName]['field'] . "` = '" . $info . "'";
  1225. $resWhereNum += 1;
  1226. }
  1227. $resList = [];
  1228. $resList = $this->sheetData($k, $fieldArr, $fieldName, $info);
  1229. $resData[] = $resList['data'];
  1230. $k = $resList['k'];
  1231. } else {
  1232. //联系人
  1233. if ($types == 'crm_customer' && $aa == (int)$contacts_k) {
  1234. $contactsInfo = '';
  1235. $contactsInfo = $val[$contacts_k];
  1236. if ($contactsInfo) {
  1237. $resContacts = true;
  1238. }
  1239. // if ($contactsFieldArr[$fieldName]['field'] == 'name') $contactsName = $contactsInfo;
  1240. $resContactsList = [];
  1241. $resContactsList = $this->sheetData($contacts_k, $contactsFieldArr, $fieldName, $contactsInfo);
  1242. $resContactsData[] = $resContactsList['data'];
  1243. $contacts_k = $resContactsList['k'];
  1244. }
  1245. }
  1246. }
  1247. if ($row_error) {
  1248. continue;
  1249. }
  1250. $result = $this->changeArr($resData); //二维数组转一维数组
  1251. $data = $result ? array_merge($data, $result) : [];
  1252. if ($types == 'crm_customer' && $result) {
  1253. $resultContacts = $this->changeArr($resContactsData);
  1254. $contactsData = $resultContacts ? array_merge($contacts_data, $resultContacts) : []; //联系人
  1255. }
  1256. $resWhere = $resWhere ?: '';
  1257. // $ownerWhere['owner_user_id'] = $param['owner_user_id'];
  1258. if ($uniqueField && $resWhere) {
  1259. $resNameIds = db($db)->where($resWhere)->where($ownerWhere)->column($db_id);
  1260. }
  1261. if ($resInfo == false) {
  1262. continue;
  1263. }
  1264. if ($resNameIds && $data) {
  1265. if ($config == 1 && $resNameIds) {
  1266. $data['user_id'] = $param['create_user_id'];
  1267. $data['update_time'] = time();
  1268. //覆盖数据(以名称为查重规则,如存在则覆盖原数据)
  1269. foreach ($resNameIds as $nid) {
  1270. $upRes = $dataModel->updateDataById($data, $nid);
  1271. if (!$upRes) {
  1272. $error_data_func($val, $dataModel->getError()); // 错误数据导出
  1273. $errorMessage[] = '第' . $keys . '行导入错误,失败原因:' . $dataModel->getError();
  1274. continue;
  1275. }
  1276. if ($types == 'crm_customer' && $resContacts !== false) {
  1277. $contactsData['customer_id'] = $upRes['customer_id'];
  1278. if (!$contactsData['owner_user_id']) $contactsData['owner_user_id'] = $param['create_user_id'];
  1279. if (!$resData = $contactsModel->createData($contactsData)) {
  1280. $error_data_func($val, $contactsModel->getError()); // 错误数据导出
  1281. $errorMessage[] = '第' . $keys . '行导入错误,失败原因:' . $contactsModel->getError();
  1282. continue;
  1283. }
  1284. }
  1285. }
  1286. } else {
  1287. $error_data_func($val, '跳过');
  1288. }
  1289. } else {
  1290. if (!$resData = $dataModel->createData($data)) {
  1291. $error_data_func($val, $dataModel->getError()); // 错误数据导出
  1292. $errorMessage[] = '第' . $keys . '行导入错误,失败原因:' . $dataModel->getError();
  1293. continue;
  1294. }
  1295. if ($types == 'crm_customer' && $resContacts !== false) {
  1296. $contactsData['customer_id'] = $resData['customer_id'];
  1297. if (!$contactsData['owner_user_id']) $contactsData['owner_user_id'] = $param['create_user_id'];
  1298. if (!$resData = $contactsModel->createData($contactsData)) {
  1299. $error_data_func($val, $contactsModel->getError()); // 错误数据导出
  1300. $errorMessage[] = '第' . $keys . '行导入错误,失败原因:' . $contactsModel->getError();
  1301. continue;
  1302. }
  1303. }
  1304. }
  1305. }
  1306. // 完成数
  1307. $done = ($page - 1) * $forCount + count($_sub);
  1308. // 错误数
  1309. $error = $error_row - 3;
  1310. // 错误数据暂存
  1311. $objWriter = \PHPExcel_IOFactory::createWriter($err_PHPExcel, 'Excel5');
  1312. $objWriter->save($error_path);
  1313. $this->error = [
  1314. 'temp_file' => $save_name,
  1315. 'error_file' => $error_data_file_name,
  1316. // 每行错误信息提示
  1317. // 'error' => $errorMessage,
  1318. // 文件总计条数
  1319. 'total' => $total,
  1320. // 已完成条数
  1321. 'done' => $done,
  1322. // 错误数据写入行号
  1323. 'error' => $error,
  1324. // 下次页码
  1325. 'page' => $page + 1,
  1326. 'import_queue_index' => $import_queue_index
  1327. ];
  1328. // 执行完成
  1329. if ($done >= $total) {
  1330. $queue->dequeue();
  1331. $this->error['error_file_path'] = 'temp/' . $error_data_file_name;
  1332. Cache::set('item', 1);
  1333. Cache::set('excel_item', serialize($this->error));
  1334. } else {
  1335. $excelData['page']=$page+1;
  1336. $excelData['types']=$types;
  1337. $excelData['temp_file']=$save_name;
  1338. $excelData['error_file']=$error_data_file_name;
  1339. $excelData['create_user_id']=$param['create_user_id'];
  1340. $excelData['import_queue_index']=$import_queue_index;
  1341. $excelData['config']=$config;
  1342. $excelData['owner_user_id']=$user_id;
  1343. $excelData['base']='importExcel';
  1344. Cache::set('item', 0);
  1345. Cache::set('excel', $excelData);
  1346. }
  1347. return true;
  1348. } else {
  1349. $this->error = '请选择导入文件';
  1350. $queue->dequeue();
  1351. return false;
  1352. }
  1353. }
  1354. /**
  1355. * excel数据处理
  1356. * @param $k 需处理数据开始下标
  1357. * @return
  1358. * @author Michael_xu
  1359. */
  1360. public function sheetData($k = 0, $fieldArr, $fieldName, $info)
  1361. {
  1362. if ($info) {
  1363. if ($fieldArr[$fieldName]['form_type'] == 'address') {
  1364. $address = array();
  1365. for ($i = 0; $i < 4; $i++) {
  1366. $address[] = $val[$k];
  1367. $k++;
  1368. }
  1369. $data[$fieldArr[$fieldName]['field']] = implode(chr(10), $address);
  1370. // 地址信息转地理坐标(仅处理系统初始的地址字段)
  1371. if ($fieldArr[$fieldName]['field'] == 'address') {
  1372. $address_arr = $address;
  1373. if ($address_arr['3']) {
  1374. $address_str = implode('', $address_arr);
  1375. $ret = get_lng_lat($address_str);
  1376. $data['lng'] = $ret['lng'] ?: 0;
  1377. $data['lat'] = $ret['lat'] ?: 0;
  1378. }
  1379. }
  1380. } elseif ($fieldArr[$fieldName]['form_type'] == 'date') {
  1381. $data[$fieldArr[$fieldName]['field']] = $info ? date('Y-m-d', strtotime($info)) : '';
  1382. $k++;
  1383. } elseif ($fieldArr[$fieldName]['form_type'] == 'datetime') {
  1384. $data[$fieldArr[$fieldName]['field']] = $info ? strtotime($info) : '';
  1385. $k++;
  1386. } elseif ($fieldArr[$fieldName]['form_type'] == 'customer') {
  1387. $data[$fieldArr[$fieldName]['field']] = db('crm_customer')->where(['name' => $info])->value('customer_id') ?: '';
  1388. $k++;
  1389. } elseif ($fieldArr[$fieldName]['form_type'] == 'contacts') {
  1390. $data[$fieldArr[$fieldName]['field']] = db('crm_contacts')->where(['name' => $info])->value('contacts_id') ?: '';
  1391. $k++;
  1392. } elseif ($fieldArr[$fieldName]['form_type'] == 'business') {
  1393. $data[$fieldArr[$fieldName]['field']] = db('crm_business')->where(['name' => $info])->value('business_id') ?: '';
  1394. $k++;
  1395. } elseif ($fieldArr[$fieldName]['form_type'] == 'category') {
  1396. $data[$fieldArr[$fieldName]['field']] = db('crm_product_category')->where(['name' => $info])->value('category_id') ?: '';
  1397. $k++;
  1398. } elseif ($fieldArr[$fieldName]['form_type'] == 'business_type') {
  1399. $data[$fieldArr[$fieldName]['field']] = db('crm_business_type')->where(['name' => $info])->value('type_id') ?: '';
  1400. $k++;
  1401. } elseif ($fieldArr[$fieldName]['form_type'] == 'business_status') {
  1402. $data[$fieldArr[$fieldName]['field']] = db('crm_business_status')->where(['name' => $info])->value('status_id') ?: '';
  1403. $k++;
  1404. } else {
  1405. $data[$fieldArr[$fieldName]['field']] = $info ?: '';
  1406. $k++;
  1407. }
  1408. } else {
  1409. $data[$fieldArr[$fieldName]['field']] = '';
  1410. $k++;
  1411. }
  1412. $res['data'] = $data;
  1413. $res['k'] = $k;
  1414. return $res;
  1415. }
  1416. /**
  1417. * 导入数据处理
  1418. *
  1419. * @param string $value
  1420. * @param array $field
  1421. * @return string
  1422. * @author Ymob
  1423. */
  1424. public function handleData($value, $field)
  1425. {
  1426. switch ($field['form_type']) {
  1427. case 'address':
  1428. return $value;
  1429. case 'date':
  1430. return $value ? date('Y-m-d', strtotime($value)) : null;
  1431. case 'datetime':
  1432. return strtotime($value) ?: 0;
  1433. case 'customer':
  1434. case 'contacts':
  1435. case 'business':
  1436. $temp = db('crm_' . $field['form_type'])
  1437. ->where(['name' => $value])
  1438. ->value($field['form_type'] . '_id');
  1439. return $temp ?: 0;
  1440. case 'business_type':
  1441. $temp = db('crm_business_type')
  1442. ->where(['name' => $value])
  1443. ->value('type_id');
  1444. return $temp ?: 0;
  1445. case 'business_status':
  1446. $temp = db('crm_business_status')
  1447. ->where(['name' => $value])
  1448. ->value('status_id');
  1449. return $temp ?: 0;
  1450. default:
  1451. return $value;
  1452. }
  1453. }
  1454. //二维数组转一维数组
  1455. public function changeArr($arr)
  1456. {
  1457. $newArr = [];
  1458. foreach ($arr as $v) {
  1459. if ($v && is_array($v)) {
  1460. $newArr = array_merge($newArr, $v);
  1461. } else {
  1462. continue;
  1463. }
  1464. }
  1465. return $newArr;
  1466. }
  1467. /**
  1468. * excel参数配置(备份)
  1469. * @param
  1470. * @return
  1471. * @author Michael_xu
  1472. */
  1473. public function config()
  1474. {
  1475. vendor("PHPExcel.PHPExcel.PHPExcel");
  1476. vendor("PHPExcel.PHPExcel.Writer.Excel5");
  1477. vendor("PHPExcel.PHPExcel.Writer.Excel2007");
  1478. vendor("PHPExcel.PHPExcel.IOFactory");
  1479. //实例化
  1480. $objPHPExcel = new \PHPExcel();
  1481. $objWriter = new \PHPExcel_Writer_Excel5($objPHPExcel);
  1482. $objWriter = new \PHPExcel_Writer_Excel2007($objPHPExcel);
  1483. $objProps = $objPHPExcel->getProperties(); // 设置excel文档的属性
  1484. $objProps->setCreator("snowerp"); //创建人
  1485. $objProps->setLastModifiedBy("snowerp"); //最后修改人
  1486. $objProps->setTitle("snowerp"); //标题
  1487. $objProps->setSubject("snowerp"); //题目
  1488. $objProps->setDescription("snowerp"); //描述
  1489. $objProps->setKeywords("snowerp"); //关键字
  1490. $objProps->setCategory("snowerp"); //种类
  1491. $objPHPExcel->setActiveSheetIndex(0); //设置当前的sheet
  1492. $objActSheet = $objPHPExcel->getActiveSheet();
  1493. $objActSheet->setTitle('snowerp'); //设置sheet的标题
  1494. $objPHPExcel->getActiveSheet()->getColumnDimension('A')->setWidth(20); //设置单元格宽度
  1495. $objPHPExcel->getActiveSheet()->getRowDimension($i)->setRowHeight(40); //设置单元格高度
  1496. $objPHPExcel->getActiveSheet()->mergeCells('A18:E22'); //合并单元格
  1497. $objPHPExcel->getActiveSheet()->unmergeCells('A28:B28'); //拆分单元格
  1498. //设置保护cell,保护工作表
  1499. $objPHPExcel->getActiveSheet()->getProtection()->setSheet(true);
  1500. $objPHPExcel->getActiveSheet()->protectCells('A3:E13', 'PHPExcel');
  1501. //设置格式
  1502. $objPHPExcel->getActiveSheet()->getStyle('E4')->getNumberFormat()->setFormatCode(PHPExcel_Style_NumberFormat::FORMAT_CURRENCY_EUR_SIMPLE);
  1503. $objPHPExcel->getActiveSheet()->duplicateStyle($objPHPExcel->getActiveSheet()->getStyle('E4'), 'E5:E13');
  1504. //设置加粗
  1505. $objPHPExcel->getActiveSheet()->getStyle('B1')->getFont()->setBold(true);
  1506. //设置水平对齐方式(HORIZONTAL_RIGHT,HORIZONTAL_LEFT,HORIZONTAL_CENTER,HORIZONTAL_JUSTIFY)
  1507. $objPHPExcel->getActiveSheet()->getStyle('D11')->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_RIGHT);
  1508. //设置垂直居中
  1509. $objPHPExcel->getActiveSheet()->getStyle('A18')->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_CENTER);
  1510. //设置字号
  1511. $objPHPExcel->getActiveSheet()->getDefaultStyle()->getFont()->setSize(10);
  1512. //设置边框
  1513. $objPHPExcel->getActiveSheet()->getStyle('A1:I20')->getBorders()->getAllBorders()->setBorderStyle(\PHPExcel_Style_Border::BORDER_THIN);
  1514. //设置边框颜色
  1515. $objPHPExcel->getActiveSheet()->getStyle('D13')->getBorders()->getLeft()->getColor()->setARGB('FF993300');
  1516. $objPHPExcel->getActiveSheet()->getStyle('D13')->getBorders()->getTop()->getColor()->setARGB('FF993300');
  1517. $objPHPExcel->getActiveSheet()->getStyle('D13')->getBorders()->getBottom()->getColor()->setARGB('FF993300');
  1518. $objPHPExcel->getActiveSheet()->getStyle('E13')->getBorders()->getTop()->getColor()->setARGB('FF993300');
  1519. $objPHPExcel->getActiveSheet()->getStyle('E13')->getBorders()->getBottom()->getColor()->setARGB('FF993300');
  1520. $objPHPExcel->getActiveSheet()->getStyle('E13')->getBorders()->getRight()->getColor()->setARGB('FF993300');
  1521. //插入图像
  1522. $objDrawing = new PHPExcel_Worksheet_Drawing();
  1523. /*设置图片路径 切记:只能是本地图片*/
  1524. $objDrawing->setPath('图像地址');
  1525. /*设置图片高度*/
  1526. $objDrawing->setHeight(180);//照片高度
  1527. $objDrawing->setWidth(150); //照片宽度
  1528. /*设置图片要插入的单元格*/
  1529. $objDrawing->setCoordinates('E2');
  1530. /*设置图片所在单元格的格式*/
  1531. $objDrawing->setOffsetX(5);
  1532. $objDrawing->setRotation(5);
  1533. $objDrawing->getShadow()->setVisible(true);
  1534. $objDrawing->getShadow()->setDirection(50);
  1535. $objDrawing->setWorksheet($objPHPExcel->getActiveSheet());
  1536. //设置单元格背景色
  1537. $objPHPExcel->getActiveSheet(0)->getStyle('A1')->getFill()->setFillType(\PHPExcel_Style_Fill::FILL_SOLID);
  1538. $objPHPExcel->getActiveSheet(0)->getStyle('A1')->getFill()->getStartColor()->setARGB('FFCAE8EA');
  1539. //输入浏览器,导出Excel
  1540. $savename = '导出Excel示例';
  1541. $ua = $_SERVER["HTTP_USER_AGENT"];
  1542. $datetime = date('Y-m-d', time());
  1543. if (preg_match("/MSIE/", $ua)) {
  1544. $savename = urlencode($savename); //处理IE导出名称乱码
  1545. }
  1546. // excel头参数
  1547. header('Content-Type: application/vnd.ms-excel');
  1548. header('Content-Disposition: attachment;filename="' . $savename . '.xls"'); //日期为文件名后缀
  1549. header('Cache-Control: max-age=0');
  1550. $objWriter = \PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5'); //excel5为xls格式,excel2007为xlsx格式
  1551. $objWriter->save('php://output');
  1552. }
  1553. /**
  1554. * 非自定义字段模块导出csv
  1555. * @param $file_name 导出文件名称
  1556. * @param $field_list 导出字段列表
  1557. * @param $callback 回调函数,查询需要导出的数据
  1558. * @author
  1559. **/
  1560. public function dataExportCsv($file_name, $field_list, $callback)
  1561. {
  1562. ini_set('memory_limit', '128M');
  1563. set_time_limit(0);
  1564. //调试时,先把下面这个两个header注释即可
  1565. header("Access-Control-Expose-Headers: Content-Disposition");
  1566. header("Content-type:application/vnd.ms-excel;charset=UTF-8");
  1567. header("Content-Disposition:attachment;filename=" . $file_name . ".csv");
  1568. header('Expires: 0');
  1569. header('Cache-control: private');
  1570. header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
  1571. header('Content-Description: File Transfer');
  1572. header('Content-Encoding: UTF-8');
  1573. // 加上bom头,防止用office打开时乱码
  1574. echo "\xEF\xBB\xBF"; // UTF-8 BOM
  1575. // 打开PHP文件句柄,php://output 表示直接输出到浏览器
  1576. $fp = fopen('php://output', 'a');
  1577. // 将中文标题转换编码,否则乱码
  1578. foreach ($field_list as $i => $v) {
  1579. $title_cell[$i] = $v['name'];
  1580. }
  1581. // 将标题名称通过fputcsv写到文件句柄
  1582. fputcsv($fp, $title_cell);
  1583. // $export_data = $callback(0);
  1584. foreach ($callback as $item) {
  1585. $rows = [];
  1586. foreach ($field_list as $rule) {
  1587. $rows[] = $item[$rule['field']];
  1588. }
  1589. fputcsv($fp, $rows);
  1590. }
  1591. // 将已经写到csv中的数据存储变量销毁,释放内存占用
  1592. ob_flush();
  1593. flush();
  1594. fclose($fp);
  1595. exit();
  1596. }
  1597. /**
  1598. * 分批导入文件 项目任务导入
  1599. *
  1600. * @param null|array|\think\File $file
  1601. * @param array $param
  1602. * @param Controller $controller
  1603. * @return bool
  1604. *
  1605. * @author Ymob
  1606. */
  1607. public function batchTaskImportData($file, $param, $controller = null)
  1608. {
  1609. // 导入模块
  1610. $types = $param['types'];
  1611. if (!in_array($types, $this->types_arr)) {
  1612. $this->error = '参数错误!';
  1613. $queue->dequeue();
  1614. return false;
  1615. }
  1616. $user_id = $param['owner_user_id'];
  1617. // 采用伪队列 允许三人同时导入数据
  1618. $queue = new Queue(self::IMPORT_QUEUE, 3);
  1619. $import_queue_index = input('import_queue_index');
  1620. // 队列任务ID
  1621. if (!$import_queue_index) {
  1622. if (!$import_queue_index = $queue->makeTaskId()) {
  1623. $this->error = $queue->error;
  1624. $queue->dequeue();
  1625. return false;
  1626. }
  1627. } else {
  1628. if (!$queue->setTaskId($import_queue_index)) {
  1629. $this->error = $queue->error;
  1630. $queue->dequeue();
  1631. return false;
  1632. }
  1633. }
  1634. // 取消导入
  1635. if ($param['page'] == -1) {
  1636. @unlink(UPLOAD_PATH . $param['temp_file']);
  1637. $this->error = [
  1638. 'msg' => '导入已取消',
  1639. 'page' => -1
  1640. ];
  1641. if ($param['error']) {
  1642. $this->error['error_file_path'] = 'temp/' . $param['error_file'];
  1643. } else {
  1644. @unlink(TEMP_DIR . $param['error_file']);
  1645. }
  1646. $temp = $queue->cache('last_import_cache');
  1647. (new ImportRecord())->createData([
  1648. 'type' => $types,
  1649. 'total' => $temp['total'],
  1650. 'done' => $temp['done'],
  1651. 'cover' => $temp['cover'],
  1652. 'error' => $temp['error'],
  1653. 'user_id' => $user_id,
  1654. 'error_data_file_path' => $temp['error'] ? 'temp/' . $error_data_file_name : ''
  1655. ]);
  1656. $queue->dequeue();
  1657. return true;
  1658. }
  1659. if (!empty($file) || $param['temp_file']) {
  1660. // 导入初始化 上传文件
  1661. if (!empty($file)) {
  1662. $save_name = $this->upload($file);
  1663. if ($save_name === false) {
  1664. $queue->dequeue();
  1665. return false;
  1666. }
  1667. } else {
  1668. $save_name = $param['temp_file'];
  1669. }
  1670. // 文件类型
  1671. $ext = pathinfo($save_name, PATHINFO_EXTENSION);
  1672. // 文件路径
  1673. $save_path = UPLOAD_PATH . $save_name;
  1674. // 队列-判断是否需要排队
  1675. if (!$queue->canExec()) {
  1676. $this->error = [
  1677. 'temp_file' => $save_name,
  1678. 'page' => -2,
  1679. 'import_queue_index' => $import_queue_index,
  1680. 'info' => $queue->error
  1681. ];
  1682. return true;
  1683. }
  1684. // 加载类库
  1685. vendor("phpexcel.PHPExcel");
  1686. vendor("phpexcel.PHPExcel.Writer.Excel5");
  1687. vendor("phpexcel.PHPExcel.Writer.Excel2007");
  1688. vendor("phpexcel.PHPExcel.IOFactory");
  1689. // 错误数据临时文件路径 错误数据开始行数
  1690. if ($param['error_file']) {
  1691. $error_path = TEMP_DIR . $param['error_file'];
  1692. $error_row = $param['error'] + 3;
  1693. $cover = $param['cover'] ?: 0;
  1694. } else {
  1695. // 生成临时文件名称
  1696. $error_path = tempFileName($ext);
  1697. // 将导入模板保存至临时路径
  1698. $controller->excelDownload($error_path);
  1699. $error_row = 3;
  1700. $cover = 0;
  1701. }
  1702. // 错误数据临时文件名称 相对于临时目录
  1703. $error_data_file_name = \substr($error_path, strlen(TEMP_DIR));
  1704. // 加载错误数据文件
  1705. $err_PHPExcel = \PHPExcel_IOFactory::load($error_path);
  1706. $error_sheet = $err_PHPExcel->setActiveSheetIndex(0);
  1707. /**
  1708. * 添加错误数据到临时文件
  1709. *
  1710. * @param array $data 原数据
  1711. * @param string $error 错误原因
  1712. * @return void
  1713. */
  1714. $error_data_func = function ($data, $error) use ($error_sheet, &$error_row) {
  1715. foreach ($data as $key => $val) {
  1716. // 第一列为错误原因 所以+1
  1717. $error_col = \PHPExcel_Cell::stringFromColumnIndex($key + 1);
  1718. $error_sheet->setCellValue($error_col . $error_row, $val);
  1719. }
  1720. $error_sheet->setCellValue('A' . $error_row, $error);
  1721. $error_sheet->getStyle('A' . $error_row)->getFont()->getColor()->setARGB('FF000000');
  1722. $error_sheet->getStyle('A' . $error_row)->getFill()->setFillType(\PHPExcel_Style_Fill::FILL_SOLID)->getStartColor()->setARGB('FFFF0000');
  1723. $error_row++;
  1724. };
  1725. $field_list = [
  1726. '0' => ['name' => '任务名称', 'field' => 'name'],
  1727. '1' => ['name' => '任务描述', 'field' => 'description'],
  1728. '2' => ['name' => '开始时间', 'field' => 'start_time'],
  1729. '3' => ['name' => '结束时间', 'field' => 'stop_time'],
  1730. '4' => ['name' => '负责人', 'field' => 'create_user_id'],
  1731. '5' => ['name' => '参与人', 'field' => 'owner_user_id'],
  1732. '6' => ['name' => '所属任务列表', 'field' => 'class_id'],
  1733. ];
  1734. $field_key_name_list = array_column($field_list, 'name');
  1735. // 加载导入数据文件
  1736. $objRender = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xls');
  1737. $objRender->setReadDataOnly(true);
  1738. $ExcelObj = $objRender->load($save_path);
  1739. // 指定工作表
  1740. $sheet = $ExcelObj->getSheet(0);
  1741. // 总行数
  1742. $max_row = $sheet->getHighestRow();
  1743. // 最大列数
  1744. $max_col_num = count($field_list) - 1;
  1745. $max_col_num += 3 * array_count_values(array_column($field_list, 'form_type'))['map_address'];
  1746. $max_col = \PHPExcel_Cell::stringFromColumnIndex($max_col_num);
  1747. // 检测导入文件是否使用最新模板
  1748. $header = $sheet->rangeToArray("A2:{$max_col}2")[0];
  1749. $temp = 0;
  1750. for ($i = 0; $i < count($field_list); $i++) {
  1751. if (
  1752. $header[$i] == $field_list[$i]['name']
  1753. || $header[$i] == $field_list[$i]['name'] . '(*)'
  1754. ) {
  1755. $temp++;
  1756. // 字段为地址时,占四列
  1757. } elseif ($field_list[$i]['form_type'] == 'map_address') {
  1758. if (
  1759. $header[$i] == $this->map_address[0]
  1760. && $header[$i + 1] == $this->map_address[1]
  1761. && $header[$i + 2] == $this->map_address[2]
  1762. && $header[$i + 3] == $this->map_address[3]
  1763. ) {
  1764. $temp++;
  1765. }
  1766. }
  1767. }
  1768. if ($temp !== count($field_list)) {
  1769. $this->error = '请使用最新导入模板';
  1770. @unlink($save_path);
  1771. $queue->dequeue();
  1772. return false;
  1773. }
  1774. // 每次导入条数
  1775. $page_size = 100;
  1776. // 当前页码
  1777. $page = ((int)$param['page']) ?: 1;
  1778. // 数据总数
  1779. $total = $max_row - 2;
  1780. // 总页数
  1781. $max_page = ceil($total / $page_size);
  1782. if ($page > $max_page) {
  1783. // $this->error = 'page参数错误';
  1784. // @unlink($save_path);
  1785. // $queue->dequeue();
  1786. // return false;
  1787. }
  1788. // 开始行 +3 跳过表头
  1789. $start_row = ($page - 1) * $page_size + 3;
  1790. // 结束行
  1791. $end_row = $start_row + $page_size - 1;
  1792. if ($end_row > $max_row) {
  1793. $end_row = $max_row;
  1794. }
  1795. // 读取数据
  1796. $dataList = $sheet->rangeToArray("A{$start_row}:{$max_col}{$end_row}");
  1797. // 默认数据
  1798. $default_data = [
  1799. 'main_user_id' => $param['create_user_id'],
  1800. 'create_user_id' => $param['create_user_id'],
  1801. 'create_time' => time(),
  1802. 'update_time' => time(),
  1803. ];
  1804. // 开始导入数据
  1805. foreach ($dataList as $val) {
  1806. $data = [];
  1807. $fk = 0;
  1808. $classData = db('work_task_class')->where(['name' => $val[6], 'work_id' => $param['work_id']])->order('class_id', 'asc')->select();
  1809. $max_order_id = db('work_task_class')->where(['work_id' => $param['work_id'], 'status' => 1])->max('order_id');
  1810. if ($classData[0]['class_id'] != '') {
  1811. $data['class_id'] = $classData[0]['class_id'];
  1812. } else {
  1813. $item = [
  1814. 'name' => $val[6],
  1815. 'create_time' => time(),
  1816. 'create_user_id' => $param['create_user_id'],
  1817. 'order_id' => $max_order_id ? $max_order_id + 1 : 0,
  1818. 'status' => 1,
  1819. 'work_id' => $param['work_id'],
  1820. ];
  1821. $data['class_id'] = db('work_task_class')->insertGetId($item);
  1822. }
  1823. $dataModel = new \app\work\model\Task();
  1824. foreach ($field_list as $field) {
  1825. $temp_value = trim($val[$fk]);
  1826. // 特殊字段特殊处理
  1827. $temp_value = $this->handleData($temp_value, $field);
  1828. $data[$field['field']] = $temp_value;
  1829. $fk++;
  1830. }
  1831. // 数据重复时
  1832. $data = array_merge($data, $default_data);
  1833. if (!$resData = $dataModel->createTask($data)) {
  1834. $error_data_func($val, $dataModel->getError());
  1835. }
  1836. }
  1837. // 完成数(已导入数)
  1838. $done = ($page - 1) * $page_size + count($dataList);
  1839. if ($page == $max_page) {
  1840. $done = $total;
  1841. }
  1842. // 错误数
  1843. $error = $error_row - 3;
  1844. // 错误数据文件保存
  1845. $objWriter = \PHPExcel_IOFactory::createWriter($err_PHPExcel, 'Excel5');
  1846. $objWriter->save($error_path);
  1847. $this->error = [
  1848. // 数据导入文件临时路径
  1849. 'temp_file' => $save_name,
  1850. // 错误数据文件路径
  1851. 'error_file' => $error_data_file_name,
  1852. // 文件总计条数
  1853. 'total' => $total,
  1854. // 已完成条数
  1855. 'done' => $done,
  1856. // 覆盖
  1857. 'cover' => $cover,
  1858. // 错误数据写入行号
  1859. 'error' => $error,
  1860. // 下次页码
  1861. 'page' => $page + 1,
  1862. // 导入任务ID
  1863. 'import_queue_index' => $import_queue_index
  1864. ];
  1865. $queue->cache('last_import_cache', [
  1866. 'total' => $total,
  1867. 'done' => $done,
  1868. 'cover' => $cover,
  1869. 'error' => $error
  1870. ]);
  1871. // 执行完成
  1872. if ($done >= $total) {
  1873. // 出队
  1874. $queue->dequeue();
  1875. // 错误数据文件路径
  1876. $this->error['error_file_path'] = 'temp/' . $error_data_file_name;
  1877. // 删除导入文件
  1878. @unlink($save_path);
  1879. // 没有错误数据时,删除错误文件
  1880. if ($error == 0) {
  1881. @unlink($error_path);
  1882. }
  1883. (new ImportRecord())->createData([
  1884. 'type' => $types,
  1885. 'total' => $total,
  1886. 'done' => $done,
  1887. 'cover' => $cover,
  1888. 'error' => $error,
  1889. 'user_id' => $user_id,
  1890. 'error_data_file_path' => $error ? 'temp/' . $error_data_file_name : ''
  1891. ]);
  1892. Cache::set('item', 1);
  1893. Cache::set('excel_item', serialize($this->error));
  1894. } else {
  1895. $excelData['cover']=$cover;
  1896. $excelData['page']=$page+1;
  1897. $excelData['types']=$types;
  1898. $excelData['temp_file']=$save_name;
  1899. $excelData['error_file']=$error_data_file_name;
  1900. $excelData['create_user_id']=$param['create_user_id'];
  1901. $excelData['import_queue_index']=$import_queue_index;
  1902. $excelData['owner_user_id']=$user_id;
  1903. $excelData['base']='batchTaskImportData';
  1904. Cache::set('item', 0);
  1905. Cache::set('excel', $excelData);
  1906. }
  1907. return true;
  1908. } else {
  1909. $this->error = '请选择导入文件';
  1910. $queue->dequeue();
  1911. return false;
  1912. }
  1913. }
  1914. /**
  1915. * task模块导出csv
  1916. * @param $title 导出文件头
  1917. * @param $file_name 导出文件名称
  1918. * @param $field_list 导出字段列表
  1919. * @param $callback 回调函数,查询需要导出的数据
  1920. * @author
  1921. **/
  1922. public function taskExportCsv($file_name, $field_list, $title, $callback)
  1923. {
  1924. ini_set('memory_limit', '128M');
  1925. set_time_limit(0);
  1926. vendor("PHPExcel.PHPExcel.PHPExcel");
  1927. vendor("PHPExcel.PHPExcel.Writer.Excel5");
  1928. vendor("PHPExcel.PHPExcel.Writer.Excel2007");
  1929. vendor("PHPExcel.PHPExcel.IOFactory");
  1930. //实例化
  1931. $objPHPExcel = new \PHPExcel();
  1932. //定义配置
  1933. $topNumber = 2;//表头有几行占用
  1934. $xlsTitle = iconv('utf-8', 'gb2312', $title);//文件名称
  1935. $cellKey = array(
  1936. 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
  1937. 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
  1938. 'AA', 'AB', 'AC', 'AD', 'AE', 'AF', 'AG', 'AH', 'AI', 'AJ', 'AK', 'AL', 'AM',
  1939. 'AN', 'AO', 'AP', 'AQ', 'AR', 'AS', 'AT', 'AU', 'AV', 'AW', 'AX', 'AY', 'AZ'
  1940. );
  1941. //处理表头标题
  1942. $objActSheet = $objPHPExcel->getActiveSheet(0);
  1943. $objPHPExcel->getActiveSheet()->mergeCells('A1:' . $cellKey[count($field_list) - 1] . '1');//合并单元格(如果要拆分单元格是需要先合并再拆分的,否则程序会报错)
  1944. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('A1', $title);
  1945. $objPHPExcel->getActiveSheet()->getDefaultRowDimension()->setRowHeight(18);//所有单元格(行)默认高度
  1946. $objPHPExcel->getActiveSheet()->getDefaultColumnDimension()->setWidth(18);//所有单元格(列)默认宽度
  1947. $objPHPExcel->getActiveSheet()->getStyle('A1')->getFont()->setBold(true);
  1948. $objPHPExcel->getActiveSheet()->getStyle('A1')->getFont()->setSize(16);
  1949. $objPHPExcel->getActiveSheet()->getStyle('A1:' . $cellKey[count($field_list) - 1] . '1')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  1950. $objPHPExcel->getActiveSheet()->getStyle('A1:' . $cellKey[count($field_list) - 1] . '1')->getAlignment()->setVertical(\PHPExcel_Style_Alignment::VERTICAL_CENTER);
  1951. $objActSheet->getStyle('A1:' . $cellKey[count($field_list) - 1] . '1')->getFont()->getColor()->setARGB('FF000000');
  1952. $objActSheet->getStyle('A1:' . $cellKey[count($field_list) - 1] . '1')->getFill()->setFillType(\PHPExcel_Style_Fill::FILL_SOLID)->getStartColor()->setARGB('##00BFFF');
  1953. //处理表头
  1954. foreach ($field_list as $k => $v) {
  1955. $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellKey[$k] . $topNumber, $v['name']);//设置表头数据
  1956. $objPHPExcel->getActiveSheet()->freezePane($cellKey[$k] . ($topNumber + 1));//冻结窗口
  1957. $objPHPExcel->getActiveSheet()->getStyle($cellKey[$k] . $topNumber)->getFont()->setBold(true);//设置是否加粗
  1958. $objPHPExcel->getActiveSheet()->getStyle($cellKey[$k] . $topNumber)->getAlignment()->setVertical(\PHPExcel_Style_Alignment::VERTICAL_CENTER);//垂直居中
  1959. $objPHPExcel->getActiveSheet()->getStyle('A2:' . $cellKey[count($field_list) - 1] . '2')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  1960. $objPHPExcel->getActiveSheet()->getStyle('A2:' . $cellKey[count($field_list) - 1] . '2')->getAlignment()->setVertical(\PHPExcel_Style_Alignment::VERTICAL_CENTER);
  1961. if ($v[3] > 0)//大于0表示需要设置宽度
  1962. {
  1963. $objPHPExcel->getActiveSheet()->getColumnDimension($cellKey[$k])->setWidth($v[3]);//设置列宽度
  1964. }
  1965. }
  1966. $objActSheet->getStyle('A2:' . $cellKey[count($field_list) - 1] . '2')->getFont()->getColor()->setARGB('FF000000');
  1967. $objActSheet->getStyle('A2:' . $cellKey[count($field_list) - 1] . '2')->getFill()->setFillType(\PHPExcel_Style_Fill::FILL_SOLID)->getStartColor()->setARGB('##00BFFF');
  1968. $callCount = count($callback) + 2;
  1969. $style_array = array(
  1970. 'borders' => array(
  1971. 'allborders' => array(
  1972. 'style' => \PHPExcel_Style_Border::BORDER_THIN
  1973. )
  1974. ));
  1975. $objActSheet->getStyle('A1:' . $cellKey[count($field_list) - 1] . $callCount)->applyFromArray($style_array);
  1976. foreach ($callback as $k => $item) {
  1977. foreach ($field_list as $key => $rule) {
  1978. $objPHPExcel->getActiveSheet()->getStyle($cellKey[$key] . ($k + 1 + $topNumber) . ':' . $cellKey[count($field_list) - 1] . ($k + 1 + $topNumber))->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  1979. $objPHPExcel->getActiveSheet()->getStyle($cellKey[$key] . ($k + 1 + $topNumber) . ':' . $cellKey[count($field_list) - 1] . ($k + 1 + $topNumber))->getAlignment()->setVertical(\PHPExcel_Style_Alignment::VERTICAL_CENTER);
  1980. $objPHPExcel->getActiveSheet()->setCellValue($cellKey[$key] . ($k + 1 + $topNumber), $item[$rule['field']]);
  1981. }
  1982. }
  1983. // excel头参数
  1984. header('Content-Type: application/vnd.ms-excel');
  1985. header('Content-Disposition: attachment;filename="' . $file_name . '.xls"'); //日期为文件名后缀
  1986. header('Cache-Control: max-age=0');
  1987. $objWriter = \PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5'); //excel5为xls格式,excel2007为xlsx格式
  1988. $objWriter->save('php://output');
  1989. }
  1990. /**
  1991. * 商业智能模块导出csv
  1992. * @param $file_name 导出文件名称
  1993. * @param $field_list 导出字段列表
  1994. * @param $callback 回调函数,查询需要导出的数据
  1995. * @author
  1996. **/
  1997. public function biExportExcel($file_name, $field_list, $title, $callback)
  1998. {
  1999. ini_set('memory_limit', '128M');
  2000. set_time_limit(0);
  2001. // 加载类库
  2002. vendor("phpexcel.PHPExcel");
  2003. vendor("phpexcel.PHPExcel.Writer.Excel5");
  2004. vendor("phpexcel.PHPExcel.Writer.Excel2007");
  2005. vendor("phpexcel.PHPExcel.IOFactory");
  2006. $objPHPExcel = new \PHPExcel();
  2007. //定义配置
  2008. $topNumber = 2;//表头有几行占用
  2009. $xlsTitle = iconv('utf-8', 'gb2312', $title);//文件名称
  2010. $cellKey = array(
  2011. 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
  2012. 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
  2013. 'AA', 'AB', 'AC', 'AD', 'AE', 'AF', 'AG', 'AH', 'AI', 'AJ', 'AK', 'AL', 'AM',
  2014. 'AN', 'AO', 'AP', 'AQ', 'AR', 'AS', 'AT', 'AU', 'AV', 'AW', 'AX', 'AY', 'AZ'
  2015. );
  2016. //处理表头标题
  2017. $objActSheet = $objPHPExcel->getActiveSheet(0);
  2018. $objPHPExcel->getActiveSheet()->mergeCells('A1:' . $cellKey[count($field_list) - 1] . '1');//合并单元格(如果要拆分单元格是需要先合并再拆分的,否则程序会报错)
  2019. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('A1', $title);
  2020. $objPHPExcel->getActiveSheet()->getDefaultRowDimension()->setRowHeight(18);//所有单元格(行)默认高度
  2021. $objPHPExcel->getActiveSheet()->getDefaultColumnDimension()->setWidth(18);//所有单元格(列)默认宽度
  2022. $objPHPExcel->getActiveSheet()->getStyle('A1')->getFont()->setBold(true);
  2023. $objPHPExcel->getActiveSheet()->getStyle('A1')->getFont()->setSize(16);
  2024. $objPHPExcel->getActiveSheet()->getStyle('A1:' . $cellKey[count($field_list) - 1] . '1')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  2025. $objPHPExcel->getActiveSheet()->getStyle('A1:' . $cellKey[count($field_list) - 1] . '1')->getAlignment()->setVertical(\PHPExcel_Style_Alignment::VERTICAL_CENTER);
  2026. $objActSheet->getStyle('A1:' . $cellKey[count($field_list) - 1] . '1')->getFont()->getColor()->setARGB('FF000000');
  2027. $objActSheet->getStyle('A1:' . $cellKey[count($field_list) - 1] . '1')->getFill()->setFillType(\PHPExcel_Style_Fill::FILL_SOLID)->getStartColor()->setARGB('##00BFFF');
  2028. //处理表头
  2029. foreach ($field_list as $k => $v) {
  2030. $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellKey[$k] . $topNumber, $v['name']);//设置表头数据
  2031. $objPHPExcel->getActiveSheet()->freezePane($cellKey[$k] . ($topNumber + 1));//冻结窗口
  2032. $objPHPExcel->getActiveSheet()->getStyle($cellKey[$k] . $topNumber)->getFont()->setBold(true);//设置是否加粗
  2033. $objPHPExcel->getActiveSheet()->getStyle($cellKey[$k] . $topNumber)->getAlignment()->setVertical(\PHPExcel_Style_Alignment::VERTICAL_CENTER);//垂直居中
  2034. $objPHPExcel->getActiveSheet()->getStyle('A2:' . $cellKey[count($field_list) - 1] . '2')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  2035. $objPHPExcel->getActiveSheet()->getStyle('A2:' . $cellKey[count($field_list) - 1] . '2')->getAlignment()->setVertical(\PHPExcel_Style_Alignment::VERTICAL_CENTER);
  2036. if ($v[3] > 0)//大于0表示需要设置宽度
  2037. {
  2038. $objPHPExcel->getActiveSheet()->getColumnDimension($cellKey[$k])->setWidth($v[3]);//设置列宽度
  2039. }
  2040. }
  2041. $objActSheet->getStyle('A2:' . $cellKey[count($field_list) - 1] . '2')->getFont()->getColor()->setARGB('FF000000');
  2042. $objActSheet->getStyle('A2:' . $cellKey[count($field_list) - 1] . '2')->getFill()->setFillType(\PHPExcel_Style_Fill::FILL_SOLID)->getStartColor()->setARGB('##00BFFF');
  2043. $callCount = count($callback) + 2;
  2044. $style_array = array(
  2045. 'borders' => array(
  2046. 'allborders' => array(
  2047. 'style' => \PHPExcel_Style_Border::BORDER_THIN
  2048. )
  2049. ));
  2050. $objActSheet->getStyle('A1:' . $cellKey[count($field_list) - 1] . $callCount)->applyFromArray($style_array);
  2051. foreach ($callback as $k => $item) {
  2052. foreach ($field_list as $key => $rule) {
  2053. $objPHPExcel->getActiveSheet()->getStyle($cellKey[$key] . ($k + 1 + $topNumber) . ':' . $cellKey[count($field_list) - 1] . ($k + 1 + $topNumber))->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  2054. $objPHPExcel->getActiveSheet()->getStyle($cellKey[$key] . ($k + 1 + $topNumber) . ':' . $cellKey[count($field_list) - 1] . ($k + 1 + $topNumber))->getAlignment()->setVertical(\PHPExcel_Style_Alignment::VERTICAL_CENTER);
  2055. $objPHPExcel->getActiveSheet()->setCellValue($cellKey[$key] . ($k + 1 + $topNumber), $item[$rule['field']]);
  2056. }
  2057. }
  2058. // excel头参数
  2059. header('Content-Type: application/vnd.ms-excel');
  2060. header('Content-Disposition: attachment;filename="' . $file_name . '.xls"'); //日期为文件名后缀
  2061. header('Cache-Control: max-age=0');
  2062. $objWriter = \PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5'); //excel5为xls格式,excel2007为xlsx格式
  2063. $objWriter->save('php://output');
  2064. }
  2065. /**
  2066. * bi 员工业绩导出
  2067. * @param $file_name
  2068. * @param $field_list
  2069. * @param $param
  2070. * @param $title
  2071. * @throws \PHPExcel_Reader_Exception
  2072. * @throws \PHPExcel_Writer_Exception
  2073. */
  2074. public function template_download($file_name, $field_list, $title, $callback)
  2075. {
  2076. // 加载类库
  2077. vendor("phpexcel.PHPExcel");
  2078. vendor("phpexcel.PHPExcel.Writer.Excel5");
  2079. vendor("phpexcel.PHPExcel.Writer.Excel2007");
  2080. vendor("phpexcel.PHPExcel.IOFactory");
  2081. $objPHPExcel = new \PHPExcel();
  2082. //定义配置
  2083. $topNumber = 2;//表头有几行占用
  2084. $xlsTitle = iconv('utf-8', 'gb2312', $title);//文件名称
  2085. $cellKey = array(
  2086. 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
  2087. 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
  2088. 'AA', 'AB', 'AC', 'AD', 'AE', 'AF', 'AG', 'AH', 'AI', 'AJ', 'AK', 'AL', 'AM',
  2089. 'AN', 'AO', 'AP', 'AQ', 'AR', 'AS', 'AT', 'AU', 'AV', 'AW', 'AX', 'AY', 'AZ'
  2090. );
  2091. //处理表头标题
  2092. $objActSheet = $objPHPExcel->getActiveSheet(0);
  2093. $objPHPExcel->getActiveSheet()->mergeCells('A1:M1');//合并单元格(如果要拆分单元格是需要先合并再拆分的,否则程序会报错)
  2094. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('A1', $title);
  2095. $objPHPExcel->getActiveSheet()->getDefaultRowDimension()->setRowHeight(18);//所有单元格(行)默认高度
  2096. $objPHPExcel->getActiveSheet()->getDefaultColumnDimension()->setWidth(18);//所有单元格(列)默认宽度
  2097. $objPHPExcel->getActiveSheet()->getStyle('A1')->getFont()->setBold(true);
  2098. $objPHPExcel->getActiveSheet()->getStyle('A1')->getFont()->setSize(16);
  2099. $objPHPExcel->getActiveSheet()->getStyle('A1:M1')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  2100. $objPHPExcel->getActiveSheet()->getStyle('A1:M1')->getAlignment()->setVertical(\PHPExcel_Style_Alignment::VERTICAL_CENTER);
  2101. $objActSheet->getStyle('A1:M1')->getFont()->getColor()->setARGB('FF000000');
  2102. $objActSheet->getStyle('A1:M1')->getFill()->setFillType(\PHPExcel_Style_Fill::FILL_SOLID)->getStartColor()->setARGB('##00BFFF');
  2103. //处理表头
  2104. $objPHPExcel->getActiveSheet()->freezePane('A2');//冻结窗口
  2105. $objPHPExcel->getActiveSheet()->getStyle('A2')->getFont()->setBold(true);//设置是否加粗
  2106. $objPHPExcel->getActiveSheet()->getStyle('A2')->getAlignment()->setVertical(\PHPExcel_Style_Alignment::VERTICAL_CENTER);//垂直居中
  2107. $objPHPExcel->getActiveSheet()->getStyle('A2:M2')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  2108. $objPHPExcel->getActiveSheet()->getStyle('A2:M2')->getAlignment()->setVertical(\PHPExcel_Style_Alignment::VERTICAL_CENTER);
  2109. $objPHPExcel->getActiveSheet()->getStyle('A2:M2')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);//文字居中
  2110. $objActSheet->getStyle('A2:M2')->getFont()->getColor()->setARGB('FF000000');
  2111. $objActSheet->getStyle('A2:M2')->getFill()->setFillType(\PHPExcel_Style_Fill::FILL_SOLID)->getStartColor()->setARGB('##00BFFF');
  2112. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('A2', '日期');
  2113. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('A3', '当月回款金额(元)');
  2114. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('A4', '环比增长(%)');
  2115. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('A5', '同比增长(%)');
  2116. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('B2', '202001');
  2117. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('C2', '202002');
  2118. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('D2', '202003');
  2119. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('E2', '202004');
  2120. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('F2', '202005');
  2121. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('G2', '202006');
  2122. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('H2', '202007');
  2123. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('I2', '202008');
  2124. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('J2', '202009');
  2125. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('K2', '202010');
  2126. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('L2', '202011');
  2127. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('M2', '202012');
  2128. $baseRow = 3; //数据从N-1行开始往下输出 这里是避免头信息被覆盖
  2129. $callCount = count($callback) + 2;
  2130. $style_array = array(
  2131. 'borders' => array(
  2132. 'allborders' => array(
  2133. 'style' => \PHPExcel_Style_Border::BORDER_THIN
  2134. )
  2135. ));
  2136. $objActSheet->getStyle('A1:M5')->applyFromArray($style_array);
  2137. foreach ($callback as $key => $value) {
  2138. $k = $key + 1;
  2139. $objPHPExcel->getActiveSheet()->setCellValue($cellKey[$k] . '3', $value['thisMonth']);
  2140. $objPHPExcel->getActiveSheet()->setCellValue($cellKey[$k] . '4', $value['chain_ratio']);
  2141. $objPHPExcel->getActiveSheet()->setCellValue($cellKey[$k] . '5', $value['year_on_year']);
  2142. }
  2143. // excel头参数
  2144. header('Content-Type: application/vnd.ms-excel');
  2145. header('Content-Disposition: attachment;filename="' . $file_name . '.xls"'); //日期为文件名后缀
  2146. header('Cache-Control: max-age=0');
  2147. $objWriter = \PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5'); //excel5为xls格式,excel2007为xlsx格式
  2148. $objWriter->save('php://output');
  2149. }
  2150. /**
  2151. * 运行中
  2152. * @param $param
  2153. * @return int|null
  2154. */
  2155. public function importNum()
  2156. {
  2157. $param = Cache::get('item');
  2158. $excelData=Cache::get('excel');
  2159. $base=$excelData['base'];
  2160. if ($param == 0) {
  2161. $this->$base('',$excelData);
  2162. $data = 0;
  2163. } elseif ($param == 1) {
  2164. $data = '';
  2165. }
  2166. return $data;
  2167. }
  2168. /**
  2169. * 结果
  2170. * @param $param
  2171. * @return int|null
  2172. */
  2173. public function importInfo()
  2174. {
  2175. $param = Cache::get('excel_item');
  2176. $param = unserialize($param);
  2177. return $param;
  2178. }
  2179. /**
  2180. * 导入记录
  2181. * @param $param
  2182. * @return array
  2183. */
  2184. public function importList($param)
  2185. {
  2186. $list = db('admin_import_record')->alias('i')
  2187. ->join('admin_user user', 'i.user_id=user.id')
  2188. ->where(['i.type'=>$param['type'],'i.user_id'=>$param['user_id']])->page($param['page'], $param['limit'])
  2189. ->field('i.*,user.realname as user_name')->order('create_time desc')->select();
  2190. $dataCount = db('admin_import_record')->where('type', $param['type'])->count();
  2191. $week = strtotime(date("Y-m-d H:i:s", strtotime("+7 day")));
  2192. $time = time();
  2193. foreach ($list as $k => $v) {
  2194. $week = strtotime("+7 day",$v['create_time']);
  2195. if ($time > (int)$week) {
  2196. $list[$k]['valid'] = 0;
  2197. } else {
  2198. $list[$k]['valid'] = 1;
  2199. }
  2200. if($v['error_data_file_path']==''){
  2201. $list[$k]['valid'] = -1;
  2202. }
  2203. $list[$k]['create_time'] = date('Y-m-d', $v['create_time']);
  2204. }
  2205. $data = [];
  2206. $data['list'] = $list;
  2207. $data['dataCount'] = $dataCount ?: 0;
  2208. if ($param['page'] != 1 && ($param['page'] * $param['limit']) >= $dataCount) {
  2209. $data['firstPage'] = false;
  2210. $data['lastPage'] = true;
  2211. } else if ($param['page'] != 1 && (int)($param['page'] * $param['limit']) < $dataCount) {
  2212. $data['firstPage'] = false;
  2213. $data['lastPage'] = false;
  2214. } else if ($param['page'] == 1) {
  2215. $data['firstPage'] = true;
  2216. $data['lastPage'] = false;
  2217. }
  2218. return $data;
  2219. }
  2220. }