Excel.php 130KB

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