Excel.php 101KB

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