index.vue 59 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778
  1. <template>
  2. <div v-if="showReportData" style="width: 100%; height: calc(100% - 70px);">
  3. <div class="cy-report-content">
  4. <div class="cy-div0">
  5. <div class="cy-report-ruler-unit">mm</div>
  6. </div>
  7. <div class="cy-div1">
  8. <div class="cy-ruler-top">
  9. <ruler-view ruler-position="horizontal"></ruler-view>
  10. </div>
  11. <div class="cy-box">
  12. <div class="cy-ruler-left">
  13. <ruler-view ruler-position="vertical"></ruler-view>
  14. </div>
  15. <div class="cy-box-main">
  16. <el-input type="text" placeholder="请输入报表模板标题" v-model="templateName" maxlength="20" size="mini" show-word-limit></el-input>
  17. <div id="luckysheet"
  18. style="margin:0px;padding:0px;position:relative;height:calc(100% - 40px);width:100%;left: 0px;top: 0px;bottom:0px;">
  19. </div>
  20. </div>
  21. </div>
  22. </div>
  23. <div class="cy-div2">
  24. <input ref="importFileNode" class="import-file-node" type="file" @change="loadExcel" style="display: none;"></input>
  25. <div class="cy-chart-div" @click="newReportTemplate" v-if="false">
  26. <svg-icon icon-class="report_new" style="width: 35px; height: 35px; margin-bottom: 6px;"/>
  27. <span>新建</span>
  28. </div>
  29. <div class="cy-chart-div" @click="saveReportTemplate">
  30. <svg-icon icon-class="report_save" style="width: 35px; height: 35px; margin-bottom: 6px;"/>
  31. <span>保存</span>
  32. </div>
  33. <div class="cy-chart-div" @click="resetReport">
  34. <svg-icon icon-class="report_cancel" style="width: 30px; height: 30px; margin-bottom: 8px;"/>
  35. <span>重置</span>
  36. </div>
  37. <el-divider></el-divider>
  38. <div class="cy-chart-div" @click="templateEvent()">
  39. <svg-icon icon-class="report_template" style="width: 35px;"/>
  40. <span>模板库</span>
  41. </div>
  42. <div class="cy-chart-div" @click="dataEvent(1)">
  43. <svg-icon icon-class="report_basedata" style="width: 35px;"/>
  44. <span>基础数据项</span>
  45. </div>
  46. <div class="cy-chart-div" @click="dataEvent()">
  47. <svg-icon icon-class="report_data" style="width: 35px;"/>
  48. <span>数据项</span>
  49. </div>
  50. <div class="cy-chart-div" @click="barEvent('bar')">
  51. <svg-icon icon-class="bar_chart"/>
  52. <span>柱状图</span>
  53. </div>
  54. <div class="cy-chart-div" @click="barEvent('line')">
  55. <svg-icon icon-class="line_chart"/>
  56. <span>折线图</span>
  57. </div>
  58. <div class="cy-chart-div" @click="barEvent('pie')">
  59. <svg-icon icon-class="pie_chart"/>
  60. <span>饼状图</span>
  61. </div>
  62. </div>
  63. </div>
  64. <!-- 图表右键操作 -->
  65. <ul v-show="visibleChartMenu"
  66. :style="{ left: menuLeft + 'px', top: menuTop + 'px' }"
  67. class="contextmenu contextmenu-black">
  68. <li @click="updateChartItem">修改</li>
  69. <li @click="delChartItem">删除</li>
  70. </ul>
  71. <!-- 弹出层:图配置 -->
  72. <el-drawer
  73. :title="chartConfigTitle"
  74. :visible.sync="dialogBarChartVisible"
  75. :show-close="false"
  76. :close-on-press-escape="false"
  77. :wrapperClosable="false"
  78. :before-close="dialogClose">
  79. <el-form ref="barChartForm" :model="barChartForm" label-position="top" size="mini" style="margin: 0 20px;">
  80. <el-form-item label="图表标题:" prop="title" class="label-title1">
  81. <el-input type="text" placeholder="请输入标题" v-model="barChartForm.title" maxlength="80" show-word-limit>
  82. </el-input>
  83. </el-form-item>
  84. <el-form-item label="图表副标题:" prop="subtitle" class="label-title1">
  85. <el-input type="text" placeholder="请输入副标题" v-model="barChartForm.subtitle" maxlength="60" show-word-limit>
  86. </el-input>
  87. </el-form-item>
  88. <el-form-item label="是否显示图表标题:" prop="showTitle">
  89. <el-radio v-model="barChartForm.showTitle" label="1">是</el-radio>
  90. <el-radio v-model="barChartForm.showTitle" label="0">否</el-radio>
  91. </el-form-item>
  92. <el-form-item label="是否显示图例:" prop="showLegend">
  93. <el-radio v-model="barChartForm.showLegend" label="1">是</el-radio>
  94. <el-radio v-model="barChartForm.showLegend" label="0">否</el-radio>
  95. </el-form-item>
  96. <!-- <el-form-item label="图例:" prop="legendType" v-if="barChartForm.type != 'pie'">-->
  97. <!-- <el-radio v-model="barChartForm.legendType" label="1">数据项</el-radio>-->
  98. <!-- <el-radio v-model="barChartForm.legendType" label="0">值</el-radio>-->
  99. <!-- </el-form-item>-->
  100. <el-form-item label="数据组:">
  101. <el-select filterable
  102. v-model="chooseDataGroup"
  103. placeholder="请选择数据组"
  104. @change="dataSourceChange"
  105. style="width: calc(100% - 110px);">
  106. <el-option
  107. v-for="dict in dataGroupList"
  108. :key="dict.id"
  109. :label="dict.groupName"
  110. :value="dict.id"
  111. ></el-option>
  112. </el-select>
  113. <el-button size="mini" @click="addDataItem" style="float: right;width: 100px;">选择数据项</el-button>
  114. <el-tag size="mini" v-if="chooseItemData.length > 0"
  115. style="margin-top: 10px;"
  116. class="cy-item-tag">已选择({{ chooseItemData.length }})项</el-tag>
  117. </el-form-item>
  118. <el-button type="primary" size="mini" @click="insertBarChartForm">保存</el-button>
  119. <el-button size="mini" @click="cancelBarChartForm">取消</el-button>
  120. </el-form>
  121. </el-drawer>
  122. <!-- 数据项列表信息 -->
  123. <el-dialog
  124. title="数据表配置"
  125. width="80%"
  126. top="10vh"
  127. center
  128. :before-close="dialogClose"
  129. :visible.sync="dialogDataVisible"
  130. :close-on-click-modal="false"
  131. :append-to-body="true">
  132. <div style="text-align: center">
  133. <el-transfer v-model="transferValue"
  134. style="text-align: left; display: inline-block"
  135. :titles="['数据字段列表', '已选择数据字段']"
  136. target-order="push"
  137. :data="transferData"></el-transfer>
  138. <el-divider content-position="left">请选择数据组</el-divider>
  139. <div class="cy-line" style="height: 100px;">
  140. <div>
  141. <el-select filterable
  142. v-model="chooseDataGroup"
  143. placeholder="请选择数据组"
  144. @change="dataSourceChange"
  145. style="width: calc(100% - 110px);">
  146. <el-option
  147. v-for="dict in dataGroupList"
  148. :key="dict.id"
  149. :label="dict.groupName"
  150. :value="dict.id"
  151. ></el-option>
  152. </el-select>
  153. <el-button size="mini" @click="addDataItem" style="float: right;width: 100px;margin-top: 4px;">选择数据项</el-button>
  154. </div>
  155. <el-tag size="mini" v-if="chooseItemData.length > 0"
  156. style="margin-top: 10px;"
  157. class="cy-item-tag">已选择({{ chooseItemData.length }})项</el-tag>
  158. </div>
  159. <el-button type="primary" @click="transferEvent" style="margin-top: 20px;">确定</el-button>
  160. </div>
  161. </el-dialog>
  162. <!-- 数据组选择 -->
  163. <el-dialog
  164. title="选择数据项"
  165. width="80%"
  166. top="10vh"
  167. center
  168. :before-close="dialogItemClose"
  169. :visible.sync="dialogDataItemVisible"
  170. :close-on-click-modal="false"
  171. :append-to-body="true">
  172. <div>
  173. <div style="margin-bottom: 10px;">
  174. <span>组名称:{{ itemDataListByGroupName }}(共 {{ itemDataListByTree.length }} 项)</span>
  175. <el-checkbox v-model="isSelectAllItem" @change="selectAllItem" style="float: right;">全选</el-checkbox>
  176. </div>
  177. <el-input placeholder="请输入关键字进行过滤" v-model="filterItemData"></el-input>
  178. <div style="height: 50vh; margin-top: 10px; overflow: auto;">
  179. <el-tree class="cy-item-tree"
  180. ref="itemTree"
  181. :data="itemDataListByTree"
  182. :indent="10"
  183. node-key="id"
  184. show-checkbox
  185. :check-on-click-node="true"
  186. :filter-node-method="filterItemDataNode"
  187. :highlight-current="true"
  188. :default-expand-all="true"
  189. @check-change="handleItemCheckChange">
  190. <span class="custom-tree-node" slot-scope="{ node, data }">
  191. <svg-icon v-if="!data.children || data.children.length == 0" icon-class="file"/>
  192. <svg-icon v-else-if="node.expanded" icon-class="folder-open"/>
  193. <svg-icon v-else icon-class="folder"/>
  194. <span :title='data.itemName || "-"' style="margin-left: 2px;">
  195. {{ data.describe
  196. ? ((data.itemName ? data.itemName : '') + '(' + data.describe + ')')
  197. : data.itemName ? data.itemName : '' }}
  198. </span>
  199. </span>
  200. </el-tree>
  201. </div>
  202. </div>
  203. <div style="width: 100%;text-align: center;">
  204. <el-button type="primary"
  205. @click="chooseItemTreeEvent"
  206. style="margin-top: 20px;">确定</el-button>
  207. </div>
  208. </el-dialog>
  209. <!-- 选择基础数据项 -->
  210. <el-dialog
  211. title="选择基础数据项"
  212. width="500px"
  213. center
  214. :before-close="dialogBaseDataClose"
  215. :visible.sync="dialogBaseDataVisible"
  216. :close-on-click-modal="false"
  217. :append-to-body="true">
  218. <el-select v-model="chooseBaseData" filterable placeholder="请选择数据项" style="width: 100%;">
  219. <el-option
  220. v-for="item in this.baseDataList"
  221. :key="item.id"
  222. :label="item.dictKey"
  223. :value="item.dictValue">
  224. </el-option>
  225. </el-select>
  226. <div style="width: 100%; text-align: center; margin-top: 20px;">
  227. <el-button type="primary" @click="baseItemEvent">确定</el-button>
  228. <el-button @click="dialogBaseDataClose">取消</el-button>
  229. </div>
  230. </el-dialog>
  231. <el-dialog
  232. title="选择四则运算表达式"
  233. width="500px"
  234. top="10vh"
  235. center
  236. :before-close="dialogDataModelClose"
  237. :visible.sync="dialogDataModelVisible"
  238. :close-on-click-modal="false"
  239. :append-to-body="true">
  240. <el-select v-model="chooseDataModel" filterable placeholder="请选择表达式" style="width: 100%;">
  241. <el-option
  242. v-for="item in this.dataModelList"
  243. :key="item.id"
  244. :label="item.operationRule"
  245. :value="item.id">
  246. </el-option>
  247. </el-select>
  248. <div style="width: 100%; text-align: center; margin-top: 20px;">
  249. <el-button type="primary" @click="saveDataModelEvent">确定</el-button>
  250. <el-button @click="dialogDataModelClose">取消</el-button>
  251. </div>
  252. </el-dialog>
  253. <!-- 公共模板报表类型选择 -->
  254. <el-dialog
  255. title="选择模板报表"
  256. width="60vw"
  257. center
  258. :before-close="dialogClose"
  259. :visible.sync="dialogCommReportVisible"
  260. :close-on-click-modal="false"
  261. :append-to-body="true">
  262. <div style="height: 60vh; overflow: auto;">
  263. <div style="height: 50px; display: flex;">
  264. <div style="width: 50%;">
  265. <label style="margin-right: 15px;">模板名称</label>
  266. <el-input placeholder="请输入报表模板名称进行过滤"
  267. v-model="searchReportTxt"
  268. @input="handleReportTypeInputChange"
  269. style="width: calc(100% - 100px); margin-bottom: 10px;">
  270. </el-input>
  271. </div>
  272. <div style="width: 50%;">
  273. <label style="margin-right: 15px;">所属行业</label>
  274. <el-input placeholder="请选择所属行业"
  275. @click.native="clickCascader"
  276. v-model="reportTypeInputName"
  277. readonly
  278. style="width: calc(100% - 100px); z-index: 1;">
  279. </el-input>
  280. <el-cascader v-model="reportType"
  281. ref="reportType"
  282. :options="reportTypes"
  283. style="width: calc(100% - 100px); width: 40px; margin-left: calc(-100% + 100px);"
  284. :props="{ value: 'id', label: 'dictKey', emitPath: true, multiple: false }"
  285. clearable
  286. @change="handleReportTypeChange"
  287. @expand-change="handleReportTypeNodeChange"></el-cascader>
  288. </div>
  289. </div>
  290. <div style="height: calc(100% - 50px); overflow: auto;">
  291. <el-empty v-if="commTemplateList.length == 0" description="暂无数据"></el-empty>
  292. <div v-else style="margin-top: 10px;">
  293. <div v-for="item in commTemplateList"
  294. style="float: left;width: 30%;display: flex;flex-direction: column;align-items: center;margin-top: 10px;">
  295. <el-image v-if="item.logo == 'zsxt.png'" :src="require('@/assets/images/template/zsxt.png')" fit="contain" style="height: 80px;"></el-image>
  296. <el-image v-else-if="item.logo == 'jcscs.png'" :src="require('@/assets/images/template/jcscs.png')" fit="contain" style="height: 80px;"></el-image>
  297. <el-image v-else-if="item.logo == 'ncs.png'" :src="require('@/assets/images/template/ncs.png')" fit="contain" style="height: 80px;"></el-image>
  298. <el-image v-else :src="require('@/assets/images/template/muban.png')" fit="contain" style="height: 80px;"></el-image>
  299. <el-radio v-model="commTemplate" :label="item"
  300. style="margin-top: 5px;width: 100%;text-overflow: ellipsis;white-space: nowrap;overflow: hidden;text-align: center;">
  301. {{ item.templateName }}
  302. </el-radio>
  303. </div>
  304. </div>
  305. </div>
  306. </div>
  307. <div style="text-align: center; margin-top: 40px;">
  308. <el-button type="primary" @click="commReportEvent">确定</el-button>
  309. <el-button @click="dialogClose">取消</el-button>
  310. </div>
  311. </el-dialog>
  312. <!-- 选择数据组数据 -->
  313. <el-dialog
  314. title="数据项配置"
  315. width="80%"
  316. top="10vh"
  317. center
  318. :before-close="dialogClose"
  319. :visible.sync="dialogGroupItemVisible"
  320. :close-on-click-modal="false"
  321. :append-to-body="true">
  322. <div>
  323. <div>
  324. <el-select filterable
  325. v-model="chooseDataGroup"
  326. placeholder="请选择数据组"
  327. style="width: calc(100% - 110px);">
  328. <el-option
  329. v-for="dict in dataGroupList"
  330. :key="dict.id"
  331. :label="dict.groupName"
  332. :value="dict.id"
  333. ></el-option>
  334. </el-select>
  335. <el-button size="mini" @click="addDataItem" style="float: right;width: 100px;margin-top: 4px;">选择数据项</el-button>
  336. </div>
  337. <div style="height: 30vh; margin-top: 10px; overflow: auto;">
  338. <el-tree class="cy-item-tree"
  339. ref="itemTree"
  340. :data="chooseGroupItemList"
  341. :indent="10"
  342. node-key="id"
  343. :check-on-click-node="true"
  344. :filter-node-method="filterItemDataNode"
  345. :highlight-current="true"
  346. :default-expand-all="true">
  347. <span class="custom-tree-node" slot-scope="{ node, data }">
  348. <svg-icon v-if="!data.children || data.children.length == 0" icon-class="file"/>
  349. <svg-icon v-else-if="node.expanded" icon-class="folder-open"/>
  350. <svg-icon v-else icon-class="folder"/>
  351. <span v-if="!data.children || data.children.length == 0" :title='data.itemName || "-"' style="margin-left: 2px;">
  352. <!-- {{ data.describe-->
  353. <!-- ? ((data.itemName ? data.itemName : '') + '【' + data.describe + '】')-->
  354. <!-- : data.itemName ? data.itemName : '' }}-->
  355. {{ '【' + data.groupName + '】' + data.itemName + (data.describe ? ('(' + data.describe + ')') : '') }}
  356. </span>
  357. <span v-else :title='data.groupName || "-"' style="margin-left: 2px;">
  358. {{ data.groupName }}
  359. </span>
  360. </span>
  361. </el-tree>
  362. </div>
  363. <div v-if="chooseGroupItemList.length > 0">
  364. <template>
  365. <el-divider content-position="left">数据显示</el-divider>
  366. <div class="cy-line custom-tree">
  367. <label>显示方式:</label>
  368. <el-radio-group v-model="itemShowParams.fieldType" @input="showDataTypeEvent">
  369. <el-radio label="1">横向</el-radio>
  370. <el-radio label="2">纵向</el-radio>
  371. </el-radio-group>
  372. </div>
  373. </template>
  374. <template>
  375. <el-divider content-position="left">值显示</el-divider>
  376. <div class="cy-line custom-tree" style="display: flex; align-items: center;">
  377. <div style="width: 50%;display: none;">
  378. <label>显示方式:</label>
  379. <el-radio v-model="itemShowParams.valType" label="1">横向</el-radio>
  380. <el-radio v-model="itemShowParams.valType" label="2">纵向</el-radio>
  381. </div>
  382. <div style="width: 50%;">
  383. <label>值显示数量:</label>
  384. <el-input-number label="请输入显示数量"
  385. v-model="itemShowParams.valLine"
  386. :min="1"
  387. :max="50"></el-input-number>
  388. </div>
  389. </div>
  390. </template>
  391. </div>
  392. </div>
  393. <div style="width: 100%;text-align: center;">
  394. <el-button type="primary" style="margin-top: 20px;" @click="itemEvent">确定</el-button>
  395. <el-button style="margin-top: 20px;" @click="dialogClose">取消</el-button>
  396. </div>
  397. </el-dialog>
  398. </div>
  399. <div v-else></div>
  400. </template>
  401. <script>
  402. import RulerView from '@/components/RulerView'
  403. import LuckyExcel from 'luckyexcel'
  404. import {
  405. getAllDataModel,
  406. getAllItemGroup, getSysTableTemplate, getSysTableTemplateById,
  407. getTableItemGroupById,
  408. getTableTemplateById,
  409. saveReportTemplate,
  410. updateReportTemplate
  411. } from "@/api/datasource";
  412. import {handleTree, showAlertWin, showLoading, traverseNode, traverseVisible} from "@/utils/cqcy";
  413. import {getDictByKey} from "@/api/basic";
  414. import {exportExcel} from "@/utils/export";
  415. import {insertLuckysheetEChart} from "@/utils/luckysheettool";
  416. export default {
  417. name: 'index',
  418. components: {
  419. RulerView
  420. },
  421. data() {
  422. return {
  423. showReportData: true,
  424. dialogBarChartVisible: false,
  425. dialogDataVisible: false,
  426. dialogDataItemVisible: false,
  427. dialogDataModelVisible: false,
  428. dialogCommReportVisible: false,
  429. dialogGroupItemVisible: false,
  430. dialogBaseDataVisible: false,
  431. isSelectAllItem: false,
  432. menuLeft: 0,
  433. menuTop: 0,
  434. visibleChartMenu: false,
  435. chooseChartClz: '',
  436. chartConfigTitle: '',
  437. barChartForm: {
  438. title: '',
  439. type: '',
  440. subtitle: '',
  441. showTitle: '1',
  442. showLegend: '1',
  443. legendType: '1',
  444. legendData: ['模板数据1', '模板数据2'],
  445. xAxisData: ['数据1', '数据2', '数据3']
  446. },
  447. barChartOption: {
  448. },
  449. itemShowParams: {
  450. fieldType: '1',
  451. valType: '2',
  452. valLine: 1,
  453. dataId: new Date().getTime()
  454. },
  455. baseDataList: [],
  456. chooseBaseData: null,
  457. dataGroupList: [],
  458. itemDataListByTree: [],
  459. itemDataListByGroupName: '',
  460. itemDataListByCalc: [],
  461. chooseGroupItemList: [],
  462. dataModelList: [],
  463. chooseDataModel: null,
  464. chooseDataItemIndex: null,
  465. bomCheckKey: 0,
  466. chooseDataGroup: null,
  467. chooseItemData: [],
  468. filterItemData: '',
  469. searchReportTxt: '',
  470. keyType: 'report_template',
  471. reportTypes: [],
  472. reportType: null,
  473. reportTypeInputName: '所有',
  474. reportTypeInputId: [],
  475. transferSingleData: [
  476. { key: 'groupName', label: 'groupName(组名称)', sort: 1 },
  477. { key: 'groupDescribe', label: 'groupDescribe(组描述)', sort: 11 },
  478. { key: 'dataSourceName', label: 'dataSourceName(数据源名称)', sort: 21 }
  479. ],
  480. transferData: [
  481. { key: 'itemName', label: 'itemName(数据项名称)', sort: 31 },
  482. { key: 'describe', label: 'describe(数据标签/描述)', sort: 41 },
  483. { key: 'dataType', label: 'dataType(数据类型)', sort: 51 },
  484. { key: 'dataValue', label: 'dataValue(数据值)', sort: 61 },
  485. { key: 'calcDataValue', label: 'calcDataValue(数据计算值)', sort: 71 }
  486. ],
  487. transferValue: [],
  488. commTemplate: null,
  489. commTemplateList: [],
  490. templateName: '',
  491. templateId: '',
  492. templateVersion: '',
  493. reportTemplateItem: 'REPORT_TEMPLATE_ITEM',
  494. reportInterval: null,
  495. toolChart: [],
  496. toolTable: [],
  497. luckysheetOption: {
  498. container: 'luckysheet', // 设定 DOM 容器的 id
  499. title: '报表模板', // 设定表格名称
  500. lang: 'zh', // 设定表格语言
  501. showinfobar: false, // 是否显示顶部信息栏
  502. showtoolbar: false, // 是否显示工具栏
  503. showtoolbarConfig: {
  504. paintFormat: true, //格式刷
  505. moreFormats: true, // 单元格格式
  506. font: true, // 字体
  507. fontSize: true, // 字号大小
  508. bold: true, // 粗体 (Ctrl+B)
  509. italic: true, // 斜体 (Ctrl+I)
  510. strikethrough: true, // 删除线 (Alt+Shift+5)
  511. underline: true, // 下划线 (Alt+Shift+6)
  512. textColor: true, // 文本颜色
  513. fillColor: true, // 单元格颜色
  514. border: true, // 边框
  515. mergeCell: true, // 合并单元格
  516. horizontalAlignMode: true, // 水平对齐方式
  517. verticalAlignMode: true, // 垂直对齐方式
  518. function: true, // 公式
  519. // image: true
  520. // chart: true
  521. },
  522. showsheetbar: false, // 是否显示底部 sheet 页按钮
  523. sheetFormulaBar: false, // 是否显示公式
  524. row: 100, // 是否显示底部 sheet 页按钮
  525. data: [{
  526. "name": "统计报表", //工作表名称
  527. }],
  528. cellRightClickConfig: { // 自定义配置单元格右击菜单
  529. copy: true, // 复制
  530. copyAs: false, // 复制为
  531. paste: true, // 粘贴
  532. insertRow: true, // 插入行
  533. insertColumn: true, // 插入列
  534. deleteRow: true, // 删除选中行
  535. deleteColumn: true, // 删除选中列
  536. deleteCell: false, // 删除单元格
  537. hideRow: false, // 隐藏选中行和显示选中行
  538. hideColumn: false, // 隐藏选中列和显示选中列
  539. rowHeight: true, // 行高
  540. columnWidth: true, // 列宽
  541. clear: false, // 清除内容
  542. matrix: false, // 矩阵操作选区
  543. sort: false, // 排序选区
  544. filter: false, // 筛选选区
  545. chart: true, // 图表生成
  546. image: false, // 插入图片
  547. link: false, // 插入链接
  548. data: false, // 数据验证
  549. cellFormat: false // 设置单元格格式
  550. },
  551. plugins: ['chart']
  552. }
  553. }
  554. },
  555. watch: {
  556. '$route.query.t': {
  557. handler(now, old) {
  558. this.getTableTemplate(this.$route.query.id, this.$route.query.type)
  559. }
  560. },
  561. filterItemData(val) {
  562. this.$refs.itemTree.filter(val)
  563. },
  564. visibleChartMenu(value) {
  565. if (value) {
  566. document.body.addEventListener('click', this.closeMenu)
  567. } else {
  568. document.body.removeEventListener('click', this.closeMenu)
  569. }
  570. }
  571. },
  572. mounted() {
  573. let _this = this
  574. $(function () {
  575. if (_this.showReportData) {
  576. let tempId = _this.$route.query.id
  577. let type = _this.$route.query.type
  578. if (tempId) {
  579. _this.getTableTemplate(tempId, type)
  580. return
  581. }
  582. luckysheet.destroy()
  583. let locItem = localStorage.getItem(_this.reportTemplateItem)
  584. if (locItem && JSON.parse(locItem).data && Array.isArray(JSON.parse(locItem).data)) {
  585. let option = JSON.parse(JSON.stringify(_this.luckysheetOption))
  586. option.data = JSON.parse(locItem).data
  587. _this.toolChart = JSON.parse(locItem).charts
  588. _this.toolTable = JSON.parse(locItem).tables
  589. option.hook = {
  590. workbookCreateAfter() {
  591. for (let i in _this.toolChart) {
  592. _this.insertEChartTool(_this.toolChart[i].info, false)
  593. }
  594. },
  595. cellUpdated(r, c, newV, oldV) {
  596. if (!(r === 49 && c === 0)) {
  597. _this.forceRefreshLuckysheet()
  598. }
  599. }
  600. }
  601. luckysheet.create(option)
  602. } else {
  603. let option = JSON.parse(JSON.stringify(_this.luckysheetOption))
  604. option.hook = {
  605. cellUpdated(r, c, newV, oldV) {
  606. if (!(r === 49 && c === 0)) {
  607. _this.forceRefreshLuckysheet()
  608. }
  609. }
  610. }
  611. luckysheet.create(option)
  612. }
  613. _this.autoSaveReportInfo()
  614. }
  615. })
  616. document.onkeyup = function (event) {
  617. let e = event || window.event || arguments.callee.caller.arguments[0]
  618. if (!e) return
  619. const {key, keyCode} = e
  620. if (keyCode === 46 || keyCode === 8) {
  621. _this.forceRefreshLuckysheet()
  622. }
  623. }
  624. },
  625. created() {
  626. this.transferData = this.transferData.concat(this.transferSingleData)
  627. let compare = (obj1, obj2) => {
  628. if (obj1.sort > obj2.sort) return 1
  629. else if (obj1.sort < obj2.sort) return -1
  630. else return 0
  631. }
  632. this.transferData.sort(compare)
  633. },
  634. destroyed() {
  635. luckysheet.destroy()
  636. if (this.reportInterval) {
  637. clearInterval(this.reportInterval)
  638. }
  639. },
  640. methods: {
  641. /** 实现类似刷新效果 */
  642. forceRefreshLuckysheet() {
  643. if (luckysheet) {
  644. luckysheet.setCellValue(49, 0, luckysheet.getCellValue(49, 0))
  645. }
  646. },
  647. /** 数据项全选事件 */
  648. selectAllItem() {
  649. const data = traverseNode(this.$refs.itemTree.store.root.childNodes)
  650. if (this.isSelectAllItem) {
  651. // this.$refs.itemTree.setCheckedNodes(this.itemDataListByTree)
  652. let arr = traverseVisible(data)
  653. let ids = Array.from(arr, ({ id }) => id)
  654. this.$refs.itemTree.setCheckedKeys(ids)
  655. } else {
  656. this.$refs.itemTree.setCheckedNodes([])
  657. }
  658. },
  659. /**数据项单项选择判断 */
  660. handleItemCheckChange(data, checked, indeterminate) {
  661. const tdata = traverseNode(this.$refs.itemTree.store.root.childNodes)
  662. let arr = traverseVisible(tdata)
  663. if (arr.length == this.$refs.itemTree.getCheckedNodes().length) {
  664. this.isSelectAllItem = true
  665. } else {
  666. this.isSelectAllItem = false
  667. }
  668. },
  669. /** 保存模版信息 */
  670. saveReportTemplate() {
  671. this.$prompt('请输入/确认报表模板名称', '保存', {
  672. confirmButtonText: '确定',
  673. cancelButtonText: '取消',
  674. customClass: 'close_confirm',
  675. closeOnClickModal: false,
  676. inputValue: this.templateName,
  677. inputValidator: (val) => {
  678. if (!val) {
  679. return '模板名称不能为空'
  680. }
  681. if (val.length > 20) {
  682. return '模板名称必须在20字以内'
  683. }
  684. }
  685. }).then(({ value }) => {
  686. const loading = showLoading(this, '保存中,请稍候···')
  687. let option = JSON.parse(JSON.stringify(this.luckysheetOption))
  688. let result = {
  689. 'charts': this.toolChart,
  690. 'tables': this.toolTable,
  691. 'option': option,
  692. 'data': JSON.parse(this.getLuckysheetConfig())
  693. }
  694. let data = {
  695. 'templateName': value,
  696. 'templateData': JSON.stringify(result)
  697. }
  698. if (this.templateId) {
  699. data.id = this.templateId
  700. data.version = this.templateVersion
  701. updateReportTemplate(data).then(res => {
  702. loading.close()
  703. let msg = res.data ? '修改成功!' : '修改失败!'
  704. let msgType = res.data ? 'success' : 'error'
  705. this.$message({
  706. message: msg,
  707. type: msgType
  708. })
  709. this.clearReportTemplate()
  710. this.getTableTemplate(this.$route.query.id, this.$route.query.type)
  711. this.$emit('refreshReportTemplateData', new Date().getTime())
  712. }).catch((e) => {
  713. loading.close()
  714. showAlertWin(this, e)
  715. })
  716. } else {
  717. saveReportTemplate(data).then(res => {
  718. loading.close()
  719. let msg = res.data ? '保存成功!' : '保存失败!'
  720. let msgType = res.data ? 'success' : 'error'
  721. this.$message({
  722. message: msg,
  723. type: msgType
  724. })
  725. this.clearReportTemplate()
  726. this.$emit('refreshReportTemplateData', new Date().getTime())
  727. }).catch((e) => {
  728. loading.close()
  729. showAlertWin(this, e)
  730. })
  731. }
  732. }).catch(() => {
  733. })
  734. },
  735. /** 新建报表内容 */
  736. newReportTemplate(type) {
  737. this.templateId = null
  738. this.templateVersion = null
  739. this.templateName = ''
  740. this.clearReportTemplate(type)
  741. // this.$emit('refreshReportTemplateData', true)
  742. },
  743. resetReport() {
  744. let _this = this
  745. localStorage.removeItem(this.reportTemplateItem)
  746. this.toolChart = []
  747. this.toolTable = []
  748. luckysheet.destroy()
  749. let option = JSON.parse(JSON.stringify(this.luckysheetOption))
  750. luckysheet.create(option)
  751. },
  752. /** 取消报表内容 */
  753. clearReportTemplate(type) {
  754. let _this = this
  755. localStorage.removeItem(this.reportTemplateItem)
  756. this.toolChart = []
  757. this.toolTable = []
  758. this.templateName = ''
  759. // 报表模板导入
  760. if (type === 'import') {
  761. console.log('导入报表')
  762. this.$refs.importFileNode.dispatchEvent(new MouseEvent('click'))
  763. } else {
  764. luckysheet.destroy()
  765. let option = JSON.parse(JSON.stringify(this.luckysheetOption))
  766. option.hook = {
  767. cellUpdated(r, c, newV, oldV) {
  768. if (!(r === 49 && c === 0)) {
  769. _this.forceRefreshLuckysheet()
  770. }
  771. }
  772. }
  773. luckysheet.create(option)
  774. }
  775. },
  776. /** 导入报表 */
  777. loadExcel(evt) {
  778. console.log(evt)
  779. let _this = this
  780. const files = evt.target.files
  781. if (files == null || files.length == 0) {
  782. _this.$message({
  783. message: '请选择文件',
  784. type: 'warning'
  785. })
  786. return
  787. }
  788. console.log(files)
  789. // 获取文件名
  790. let name = files[0].name
  791. // 获取文件后缀
  792. let suffixArr = name.split("."),
  793. suffix = suffixArr[suffixArr.length - 1]
  794. if (suffix != 'xlsx') {
  795. _this.$message({
  796. message: '只能导入xlsx格式文件!',
  797. type: 'warning'
  798. })
  799. return
  800. }
  801. const loading = showLoading(this, '文件导入中,请稍候···')
  802. // 转换导入的excel
  803. LuckyExcel.transformExcelToLucky(files[0], function (exportJson, luckysheetfile) {
  804. if (exportJson.sheets == null || exportJson.sheets.length == 0) {
  805. loading.close()
  806. _this.$message({
  807. message: '无法读取excel文件的内容,当前不支持xls文件!',
  808. type: 'warning'
  809. })
  810. return
  811. }
  812. console.log('exportJson', exportJson)
  813. // 销毁之前的表格
  814. window.luckysheet.destroy()
  815. // 创建新的表格
  816. let option = JSON.parse(JSON.stringify(_this.luckysheetOption))
  817. option.data = exportJson.sheets
  818. option.title = exportJson.info.name
  819. option.data = exportJson.sheets
  820. option.hook = {
  821. cellUpdated(r, c, newV, oldV) {
  822. if (!(r === 49 && c === 0)) {
  823. _this.forceRefreshLuckysheet()
  824. }
  825. }
  826. }
  827. window.luckysheet.create(option)
  828. document.getElementsByClassName('import-file-node')[0].value = ''
  829. loading.close()
  830. })
  831. },
  832. /** 保存数据格式转换 */
  833. getLuckysheetConfig() {
  834. let ls = luckysheet.getLuckysheetfile()
  835. ls.forEach((item, index) => {
  836. if(item.chart) {
  837. item.chart.forEach((chart, i) => {
  838. ls[index].chart[i] = {
  839. ...ls[index].chart[i],
  840. chartOptions: {...chartmix.default.getChartJson(chart.chart_id)}
  841. }
  842. let div = document.getElementById(chart.chart_id + '_c');
  843. if(div.style) {
  844. ls[index].chart[i].left = parseInt(div.style.left)
  845. ls[index].chart[i].top = parseInt(div.style.top)
  846. ls[index].chart[i].width = parseInt(div.style.width)
  847. ls[index].chart[i].height = parseInt(div.style.height)
  848. }
  849. })
  850. }
  851. })
  852. return JSON.stringify(ls)
  853. },
  854. /** 生成唯一 ID 字符串 */
  855. generateRandomKey(prefix) {
  856. if (prefix == null) {
  857. prefix = 'chart'
  858. }
  859. let userAgent = window.navigator.userAgent
  860. .replace(/[^a-zA-Z0-9]/g, '')
  861. .split('')
  862. let mid = ''
  863. for (let i = 0; i < 12; i++) {
  864. mid += userAgent[Math.round(Math.random() * (userAgent.length - 1))]
  865. }
  866. let time = new Date().getTime()
  867. return prefix + '_' + mid + '_' + time
  868. },
  869. /** 向 Excel 插入图表 */
  870. insertEChartTool(info, flag, item, legendType) {
  871. let _this = this
  872. let _self = this
  873. setTimeout(() => {
  874. const sheet = luckysheet.getLuckysheetfile()[0]
  875. let optionData = sheet.data
  876. try {
  877. insertLuckysheetEChart({
  878. selector: '#luckysheet',
  879. info,
  880. sheet,
  881. optionData,
  882. echarts,
  883. luckysheet,
  884. $,
  885. _self
  886. })
  887. } catch (e) {
  888. console.error(e)
  889. }
  890. // 保存图表信息
  891. if (flag) {
  892. let chart = {
  893. 'info': info,
  894. 'legendType': legendType,
  895. 'item': item
  896. }
  897. _this.toolChart.push(chart)
  898. }
  899. // 图表事件监听
  900. $('.' + info.className).mousedown(function(e) {
  901. if (3 == e.which) {
  902. _this.closeMenu()
  903. _this.menuTop = e.pageY
  904. _this.menuLeft = e.pageX / 2
  905. _this.visibleChartMenu = true
  906. _this.chooseChartClz = info.className
  907. }
  908. })
  909. }, 200)
  910. },
  911. /** 获取右侧数据组列表 */
  912. getDataGroupList() {
  913. getAllItemGroup().then(res => {
  914. this.dataGroupList = res.data
  915. }).catch((e) => {
  916. showAlertWin(this, e)
  917. })
  918. },
  919. /** 选择数据源值改变事件 */
  920. dataSourceChange(val) {
  921. this.chooseDataGroup = val
  922. this.chooseItemData = []
  923. },
  924. /** 数据项搜索过滤 */
  925. filterItemDataNode(value, data) {
  926. if (!value) return true
  927. return data.itemName.indexOf(value) !== -1
  928. },
  929. /** 添加数据项 */
  930. addDataItem() {
  931. let id = this.chooseDataGroup
  932. if (!id) {
  933. this.$message({
  934. message: '请选择数据组!',
  935. type: 'warning'
  936. })
  937. return
  938. }
  939. this.isSelectAllItem = false
  940. this.itemDataListByTree = []
  941. const loading = showLoading(this, '加载中,请稍候···')
  942. getTableItemGroupById(id).then(res => {
  943. loading.close()
  944. this.itemDataListByGroupName = res.data.groupName
  945. this.itemDataListByTree = res.data.itemList
  946. let tempList = this.chooseGroupItemList.length == 0 ? this.chooseItemData : this.chooseGroupItemList
  947. if (this.$refs.itemTree) this.$refs.itemTree.setCheckedKeys([])
  948. if (tempList.length > 0) {
  949. let idList = Array.from(tempList, ({ id }) => id)
  950. if (this.$refs.itemTree) this.$refs.itemTree.setCheckedKeys(idList)
  951. if (this.itemDataListByTree.length == idList.length) {
  952. this.isSelectAllItem = true
  953. } else {
  954. this.isSelectAllItem = false
  955. }
  956. }
  957. this.dialogDataItemVisible = true
  958. }).catch((e) => {
  959. loading.close()
  960. showAlertWin(this, e)
  961. })
  962. },
  963. /** 选择的数据项:第一步 */
  964. chooseItemTreeEvent() {
  965. let checkedNodes = this.$refs.itemTree.getCheckedNodes(false, true)
  966. if (checkedNodes.length == 0) {
  967. this.$message({
  968. message: '请选择数据项!',
  969. type: 'warning'
  970. })
  971. return
  972. }
  973. let chooseList = []
  974. for (let i in checkedNodes) {
  975. let checkedNode = checkedNodes[i]
  976. if (checkedNode.children && checkedNode.children.length > 0) {
  977. continue
  978. }
  979. chooseList.push(checkedNode)
  980. }
  981. this.chooseItemData = chooseList
  982. this.dialogDataItemVisible = false
  983. if (this.dialogGroupItemVisible) {
  984. this.initGroupItem()
  985. }
  986. this.filterItemData = ''
  987. // this.getAllDataModel(chooseList)
  988. },
  989. initGroupItem() {
  990. let group = null
  991. for (let i = 0; i < this.dataGroupList.length; i ++) {
  992. if (this.dataGroupList[i].id == this.chooseDataGroup) {
  993. group = this.dataGroupList[i]
  994. break
  995. }
  996. }
  997. this.chooseGroupItemList = this.chooseGroupItemList.filter(function (item, index) {
  998. return item.itemGroupId != group.id;
  999. })
  1000. for (let j = 0; j < this.chooseItemData.length; j ++) {
  1001. this.chooseItemData[j].groupName = group.groupName
  1002. this.chooseGroupItemList.push(this.chooseItemData[j])
  1003. }
  1004. let obj = {};
  1005. this.chooseGroupItemList = this.chooseGroupItemList.reduce(function(item, next) {
  1006. obj[next.id] ? '' : obj[next.id] = true && item.push(next)
  1007. return item;
  1008. }, [])
  1009. },
  1010. /** 查询所有数据模型 */
  1011. getAllDataModel(itemList) {
  1012. let loading = showLoading(this, '数据加载中,请稍候···')
  1013. let params = {
  1014. 'page': 1,
  1015. 'limit': 1000
  1016. }
  1017. getAllDataModel(params).then(res => {
  1018. loading.close()
  1019. if (!res.data) {
  1020. this.chooseItemData = itemList
  1021. return
  1022. }
  1023. this.dataModelList = res.data.dataModelList
  1024. this.itemDataListByCalc = JSON.parse(JSON.stringify(itemList))
  1025. for (let i in this.itemDataListByCalc) {
  1026. let temp = this.itemDataListByCalc[i]
  1027. temp.rule = {
  1028. 'operationRule': '默认值'
  1029. }
  1030. }
  1031. }).catch((e) => {
  1032. loading.close()
  1033. showAlertWin(this, e)
  1034. })
  1035. },
  1036. /** 选择数据模型 */
  1037. handleChoose(index, row) {
  1038. this.chooseDataItemIndex = index
  1039. this.chooseDataModel = null
  1040. this.dialogDataModelVisible = true
  1041. },
  1042. /** 保存数据模型事件 */
  1043. saveDataModelEvent() {
  1044. if (!this.chooseDataModel) {
  1045. this.$message({
  1046. message: '请选择表达式!',
  1047. type: 'warning'
  1048. })
  1049. return
  1050. }
  1051. let item = {}
  1052. for (let i in this.dataModelList) {
  1053. if (this.chooseDataModel == this.dataModelList[i].id) {
  1054. item = this.dataModelList[i]
  1055. }
  1056. }
  1057. this.$nextTick(() => {
  1058. this.itemDataListByCalc[this.chooseDataItemIndex].rule = item
  1059. this.bomCheckKey ++
  1060. })
  1061. this.dialogDataModelVisible = false
  1062. },
  1063. /** 报表模板库 */
  1064. templateEvent() {
  1065. this.commTemplateList = []
  1066. this.reportTypes = []
  1067. this.reportType = null
  1068. // 获取报表类别
  1069. getDictByKey({
  1070. 'keyType': 'report_template'
  1071. }).then(res => {
  1072. this.reportTypes.push({
  1073. 'id': -1,
  1074. 'dictKey': '所有',
  1075. 'dictValue': ''
  1076. })
  1077. let t = handleTree(res.data, 'id', 'parentId', 'children')
  1078. this.reportTypes = this.reportTypes.concat(t)
  1079. this.dialogCommReportVisible = true
  1080. this.handleReportTypeChange([-1])
  1081. }).catch((e) => {
  1082. showAlertWin(this, e)
  1083. })
  1084. },
  1085. clickCascader() {
  1086. this.$refs.reportType.$el.click()
  1087. this.reportType = null
  1088. },
  1089. searchDictTree(nodes, searchKey) {
  1090. for (let _i = 0; _i < nodes.length; _i++) {
  1091. if (nodes[_i].id === searchKey) {
  1092. return nodes[_i].dictKey
  1093. } else {
  1094. if (nodes[_i].children && nodes[_i].children.length > 0) {
  1095. let res = this.searchDictTree(nodes[_i].children, searchKey)
  1096. if (res) {
  1097. return res
  1098. }
  1099. }
  1100. }
  1101. }
  1102. return null
  1103. },
  1104. handleReportTypeInputChange() {
  1105. let arr = this.reportTypeInputId.length == 0 ? [-1] : this.reportTypeInputId
  1106. this.handleReportTypeChange(arr)
  1107. },
  1108. /** 报表行业选择 */
  1109. handleReportTypeNodeChange(val) {
  1110. this.handleReportTypeChange(val)
  1111. },
  1112. /** 报表行业选择 */
  1113. handleReportTypeChange(val) {
  1114. if (!val || val.length == 0) {
  1115. return
  1116. }
  1117. let nameArr = []
  1118. val.forEach(v => {
  1119. let name = this.searchDictTree(this.reportTypes, v)
  1120. nameArr.push(name)
  1121. })
  1122. this.reportTypeInputName = nameArr.join(' / ')
  1123. this.reportTypeInputId = val
  1124. let id = val[val.length - 1]
  1125. if (id === -1) {
  1126. this.reportTypeInputName = '所有'
  1127. this.reportTypeInputId = []
  1128. id = null
  1129. }
  1130. let params = {
  1131. 'keyType': this.keyType,
  1132. 'dictId': id
  1133. }
  1134. if (this.searchReportTxt && this.searchReportTxt.trim()) {
  1135. params.templateName = this.searchReportTxt.trim()
  1136. }
  1137. getSysTableTemplate(params).then(res => {
  1138. if (!res.data) {
  1139. return
  1140. }
  1141. this.commTemplateList = res.data
  1142. }).catch((e) => {
  1143. showAlertWin(this, e)
  1144. })
  1145. },
  1146. /** 选择模板报表 */
  1147. commReportEvent() {
  1148. let _this = this
  1149. if (!_this.commTemplate) {
  1150. _this.$message({
  1151. message: '请选择模板!',
  1152. type: 'warning'
  1153. })
  1154. return
  1155. }
  1156. const loading = showLoading(_this, '加载中,请稍候···')
  1157. getSysTableTemplateById(_this.commTemplate.id).then(res => {
  1158. if (!res || !res.data) {
  1159. loading.close()
  1160. return
  1161. }
  1162. let locItem = res.data.templateData
  1163. luckysheet.destroy()
  1164. let option = JSON.parse(JSON.stringify(_this.luckysheetOption))
  1165. option.data = JSON.parse(locItem).data
  1166. this.toolChart = JSON.parse(locItem).charts
  1167. this.toolTable = JSON.parse(locItem).tables
  1168. option.hook = {
  1169. workbookCreateAfter() {
  1170. loading.close()
  1171. _this.dialogClose()
  1172. },
  1173. cellUpdated(r, c, newV, oldV) {
  1174. if (!(r === 49 && c === 0)) {
  1175. _this.forceRefreshLuckysheet()
  1176. }
  1177. }
  1178. }
  1179. luckysheet.create(option)
  1180. }).catch((e) => {
  1181. loading.close()
  1182. showAlertWin(_this, e)
  1183. })
  1184. },
  1185. /** 数据项 */
  1186. dataEvent(type) {
  1187. let rangeWithFlatten = luckysheet.getRangeWithFlatten()
  1188. if (!rangeWithFlatten || rangeWithFlatten.length != 1) {
  1189. this.$message({
  1190. message: '请选择一项单元格!',
  1191. type: 'warning'
  1192. })
  1193. return
  1194. }
  1195. // 基础数据项
  1196. if (type == 1) {
  1197. this.getBaseDataItem()
  1198. return
  1199. }
  1200. // 初始化数据显示方式
  1201. this.itemShowParams = {
  1202. fieldType: '1',
  1203. valType: '2',
  1204. valLine: 1,
  1205. dataId: new Date().getTime()
  1206. }
  1207. this.chooseDataGroup = null
  1208. this.getDataGroupList()
  1209. this.dialogGroupItemVisible = true
  1210. },
  1211. /** 查询字典表:基础数据项 */
  1212. getBaseDataItem() {
  1213. getDictByKey({
  1214. 'keyType': 'base_data_item'
  1215. }).then(res => {
  1216. this.dialogBaseDataVisible = true
  1217. this.baseDataList = res.data
  1218. }).catch((e) => {
  1219. showAlertWin(this, e)
  1220. })
  1221. },
  1222. /** 基础数据项插入 */
  1223. baseItemEvent() {
  1224. console.log(this.chooseBaseData)
  1225. if (!this.chooseBaseData) {
  1226. this.$message({
  1227. message: '请选择基础数据项!',
  1228. type: 'warning'
  1229. })
  1230. return
  1231. }
  1232. let rangeWithFlatten = luckysheet.getRangeWithFlatten()
  1233. let c = rangeWithFlatten[0].c
  1234. let r = rangeWithFlatten[0].r
  1235. luckysheet.setCellValue(r, c, this.chooseBaseData)
  1236. this.dialogBaseDataClose()
  1237. },
  1238. /** 动态数据项插入 */
  1239. itemEvent() {
  1240. if (this.chooseGroupItemList.length == 0) {
  1241. this.$message({
  1242. message: '请选择数据组项!',
  1243. type: 'warning'
  1244. })
  1245. return
  1246. }
  1247. let rangeWithFlatten = luckysheet.getRangeWithFlatten()
  1248. let c = rangeWithFlatten[0].c
  1249. let r = rangeWithFlatten[0].r
  1250. let fieldList = []
  1251. for (let n = 0; n < this.itemShowParams.valLine; n ++) {
  1252. for (let i in this.chooseGroupItemList) {
  1253. let groupId = this.chooseGroupItemList[i].itemGroupId
  1254. let name = this.chooseGroupItemList[i].itemName
  1255. name = name.substring(name.lastIndexOf('.') + 1)
  1256. let isCopy = ''
  1257. if (n > 0) isCopy = '.copy'
  1258. let v = '${' + groupId + '.' + name + '.' + this.itemShowParams.dataId + isCopy + '}'
  1259. let p_c = parseInt(c)
  1260. let p_r = parseInt(r)
  1261. // fieldType:1 横向 2 纵向
  1262. if (this.itemShowParams.fieldType == '2') {
  1263. p_r += parseInt(i)
  1264. p_c += n
  1265. } else {
  1266. p_c += parseInt(i)
  1267. p_r += n
  1268. }
  1269. if (n === 0) {
  1270. fieldList.push({
  1271. 'name': v,
  1272. 'r': p_r,
  1273. 'c': p_c
  1274. })
  1275. }
  1276. luckysheet.setCellValue(p_r, p_c, v)
  1277. }
  1278. }
  1279. let tableTem = {
  1280. 'item': this.chooseGroupItemList,
  1281. 'field': fieldList,
  1282. 'showType': this.itemShowParams
  1283. }
  1284. for (let i = this.toolTable.length - 1; i >= 0; i --) {
  1285. let info = this.toolTable[i]
  1286. if (info.field && info.field[0]) {
  1287. let v = luckysheet.getCellValue(info.field[0].r, info.field[0].c)
  1288. if (v != info.field[0].name) {
  1289. this.toolTable.splice(i, 1)
  1290. }
  1291. } else {
  1292. this.toolTable.splice(i, 1)
  1293. }
  1294. }
  1295. this.toolTable.push(tableTem)
  1296. this.chooseGroupItemList = []
  1297. this.chooseDataGroup = null
  1298. this.chooseItemData = []
  1299. this.dialogGroupItemVisible = false
  1300. },
  1301. /** 数据字段添加 */
  1302. transferEvent() {
  1303. if (this.transferValue.length == 0) {
  1304. this.$message({
  1305. message: '请选择数据字段!',
  1306. type: 'warning'
  1307. })
  1308. return
  1309. }
  1310. if (!this.chooseDataGroup) {
  1311. this.$message({
  1312. message: '请选择数据组!',
  1313. type: 'warning'
  1314. })
  1315. return
  1316. }
  1317. if (this.chooseItemData.length == 0) {
  1318. this.$message({
  1319. message: '请选择数据组项!',
  1320. type: 'warning'
  1321. })
  1322. return
  1323. }
  1324. let rangeWithFlatten = luckysheet.getRangeWithFlatten()
  1325. let c = rangeWithFlatten[0].c
  1326. let r = rangeWithFlatten[0].r
  1327. let fieldList = []
  1328. for (let i in this.transferValue) {
  1329. let v = '#{' + this.transferValue[i] + '}'
  1330. let p_c = parseInt(c) + parseInt(i)
  1331. fieldList.push({
  1332. 'name': v,
  1333. 'r': r,
  1334. 'c': p_c
  1335. })
  1336. luckysheet.setCellValue(r, p_c, v)
  1337. }
  1338. let tableTem = {
  1339. 'item': this.chooseItemData,
  1340. 'field': fieldList
  1341. }
  1342. this.toolTable.push(tableTem)
  1343. this.transferValue = []
  1344. this.chooseDataGroup = null
  1345. this.chooseItemData = []
  1346. this.dialogDataVisible = false
  1347. },
  1348. /** 柱状图 */
  1349. barEvent(type) {
  1350. let _this = this
  1351. // 当前选择范围
  1352. let rangeWithFlatten = luckysheet.getRangeWithFlatten()
  1353. if (!rangeWithFlatten || rangeWithFlatten.length <= 1) {
  1354. _this.$message({
  1355. message: '请选择多项单元格!',
  1356. type: 'warning'
  1357. })
  1358. return
  1359. }
  1360. this.chooseDataGroup = null
  1361. this.getDataGroupList()
  1362. if (!type) {
  1363. type = 'bar'
  1364. }
  1365. if (type == 'bar') {
  1366. this.chartConfigTitle = '柱状图配置'
  1367. } else if (type == 'line') {
  1368. this.chartConfigTitle = '折线图配置'
  1369. } else if (type == 'pie') {
  1370. this.chartConfigTitle = '饼状图配置'
  1371. }
  1372. this.barChartForm.type = type
  1373. // 显示柱状图配置信息
  1374. this.dialogBarChartVisible = true
  1375. },
  1376. /** 柱状图图表插入 */
  1377. insertBarChartForm() {
  1378. if (this.barChartForm.showTitle == '1' && !this.barChartForm.title) {
  1379. this.$message({
  1380. message: '标题不能为空!',
  1381. type: 'warning'
  1382. })
  1383. return
  1384. }
  1385. if (!this.chooseDataGroup) {
  1386. this.$message({
  1387. message: '请选择数据组!',
  1388. type: 'warning'
  1389. })
  1390. return
  1391. }
  1392. if (this.chooseItemData.length == 0) {
  1393. this.$message({
  1394. message: '请选择数据组项!',
  1395. type: 'warning'
  1396. })
  1397. return
  1398. }
  1399. if ((this.barChartForm.legendType == '0' && this.chooseItemData.length > 1)
  1400. || (this.barChartForm.type == 'pie' && this.chooseItemData.length > 1)) {
  1401. this.$message({
  1402. message: '请选择一项数据项!',
  1403. type: 'warning'
  1404. })
  1405. return
  1406. }
  1407. // 图表参数
  1408. this.barChartOption = {
  1409. title: {
  1410. show: this.barChartForm.showTitle == '1',
  1411. text: this.barChartForm.title,
  1412. subtext: this.barChartForm.subtitle,
  1413. left: 'center'
  1414. },
  1415. tooltip: {},
  1416. legend: {
  1417. show: this.barChartForm.showLegend == '1',
  1418. orient: 'vertical',
  1419. left: 'left',
  1420. data: []
  1421. },
  1422. series: []
  1423. }
  1424. if (this.barChartForm.type != 'pie') {
  1425. this.barChartOption.xAxis = {
  1426. data: this.barChartForm.xAxisData
  1427. }
  1428. this.barChartOption.yAxis = {
  1429. }
  1430. // 图表图例类型:1 数据项 0 值
  1431. if (this.barChartForm.legendType == '1') {
  1432. let legendNameList = []
  1433. for (let i in this.chooseItemData) {
  1434. let name = this.chooseItemData[i].describe
  1435. name = name ? name : this.chooseItemData[i].itemName
  1436. legendNameList.push(name)
  1437. }
  1438. this.barChartOption.legend.data = legendNameList
  1439. } else {
  1440. this.barChartOption.legend.data = ['值', '运算值']
  1441. }
  1442. // this.barChartOption.legend.data = this.barChartForm.legendData
  1443. for (let i in this.barChartOption.legend.data) {
  1444. let seriesName = this.barChartOption.legend.data[i]
  1445. let dataList = []
  1446. for (let j in this.barChartForm.xAxisData) {
  1447. dataList.push(Math.ceil(Math.random() * 10))
  1448. }
  1449. let series = {
  1450. name: seriesName,
  1451. type: this.barChartForm.type,
  1452. data: dataList
  1453. }
  1454. this.barChartOption.series.push(series)
  1455. }
  1456. } else {
  1457. this.barChartOption.legend.data = null
  1458. let seriesName = this.barChartForm.legendData[0]
  1459. let dataList = []
  1460. for (let i in this.barChartForm.xAxisData) {
  1461. dataList.push({
  1462. value: Math.ceil(Math.random() * 10),
  1463. name: this.barChartForm.xAxisData[i]
  1464. })
  1465. }
  1466. let series = {
  1467. name: seriesName,
  1468. type: this.barChartForm.type,
  1469. radius: '50%',
  1470. data: dataList
  1471. }
  1472. this.barChartOption.series.push(series)
  1473. }
  1474. if (this.barChartForm.uid) {
  1475. this.delChartItem()
  1476. }
  1477. // 合并当前选择范围
  1478. luckysheet.setRangeMerge('all')
  1479. // 图表插入的 x、y 轴坐标获取
  1480. let range = luckysheet.getRange()
  1481. let x = range[0].column[0]
  1482. let y = range[0].row[0]
  1483. // 随机类名称
  1484. let clz = this.generateRandomKey('chart_clz')
  1485. let info = {
  1486. id: this.generateRandomKey('chart'), // 唯一 id
  1487. pos: [y, x], // eCharts 插入的单元格横、纵坐标
  1488. className: clz, // 给定eCharts容器的 className,自定义
  1489. chart: null, // 创建的 eCharts 实例
  1490. option: this.barChartOption // eCharts 图形配置选项
  1491. }
  1492. this.insertEChartTool(info, true, this.chooseItemData, this.barChartForm.legendType)
  1493. this.$refs.barChartForm.resetFields()
  1494. this.chooseDataGroup = null
  1495. this.chooseItemData = []
  1496. this.dialogBarChartVisible = false
  1497. },
  1498. /** 取消图表插入 */
  1499. cancelBarChartForm() {
  1500. this.$refs.barChartForm.resetFields()
  1501. this.chooseDataGroup = null
  1502. this.chooseItemData = []
  1503. this.dialogBarChartVisible = false
  1504. },
  1505. /** 图表修改 */
  1506. updateChartItem() {
  1507. this.getDataGroupList()
  1508. let chartInfo = {}
  1509. for (let i in this.toolChart) {
  1510. if (this.toolChart[i].info.className == this.chooseChartClz) {
  1511. chartInfo = this.toolChart[i]
  1512. }
  1513. }
  1514. let temp = chartInfo.info.option
  1515. this.barChartForm.uid = new Date().getTime()
  1516. this.barChartForm.title = temp.title.text
  1517. this.barChartForm.subtitle = temp.title.subtext
  1518. this.barChartForm.showTitle = temp.title.show ? '1' : '0'
  1519. this.barChartForm.showLegend = temp.legend.show ? '1' : '0'
  1520. this.barChartForm.type = temp.series[0].type
  1521. this.barChartForm.legendType = chartInfo.legendType
  1522. this.chooseItemData = chartInfo.item
  1523. this.chooseDataGroup = chartInfo.item[0].itemGroupId
  1524. this.dialogBarChartVisible = true
  1525. },
  1526. /** 图表删除 */
  1527. delChartItem() {
  1528. $('.' + this.chooseChartClz).remove()
  1529. luckysheet.cancelRangeMerge()
  1530. for (let i in this.toolChart) {
  1531. if (this.toolChart[i].info.className == this.chooseChartClz) {
  1532. this.toolChart.splice(i, 1)
  1533. }
  1534. }
  1535. },
  1536. /** 自动存储报表信息 */
  1537. autoSaveReportInfo() {
  1538. let _this = this
  1539. let time = setInterval(() => {
  1540. if (!this.templateId) {
  1541. let result = {
  1542. 'charts': _this.toolChart,
  1543. 'tables': _this.toolTable,
  1544. 'data': JSON.parse(_this.getLuckysheetConfig())
  1545. }
  1546. localStorage.setItem(_this.reportTemplateItem, JSON.stringify(result))
  1547. console.log(new Date().getTime(), 'auto save report template success.')
  1548. }
  1549. }, 30 * 1000)
  1550. this.reportInterval = time
  1551. },
  1552. /** 获取模板报表信息 */
  1553. getTableTemplate(id, type) {
  1554. let _this = this
  1555. if (!id || id == -999) {
  1556. this.newReportTemplate(type)
  1557. return
  1558. }
  1559. getTableTemplateById(id).then(res => {
  1560. if (!res.data) {
  1561. return
  1562. }
  1563. luckysheet.destroy()
  1564. this.templateName = res.data.templateName
  1565. this.templateId = res.data.id
  1566. this.templateVersion = res.data.version
  1567. let templateData = res.data.templateData
  1568. let option = JSON.parse(JSON.stringify(this.luckysheetOption))
  1569. option.data = JSON.parse(templateData).data
  1570. this.toolChart = JSON.parse(templateData).charts
  1571. this.toolTable = JSON.parse(templateData).tables
  1572. option.hook = {
  1573. workbookCreateAfter() {
  1574. for (let i in _this.toolChart) {
  1575. _this.insertEChartTool(_this.toolChart[i].info, false)
  1576. }
  1577. // 报表模板导出
  1578. if (type === 'export') {
  1579. exportExcel(luckysheet.getAllSheets(), _this.templateName)
  1580. }
  1581. },
  1582. cellUpdated(r, c, newV, oldV) {
  1583. if (!(r === 49 && c === 0)) {
  1584. _this.forceRefreshLuckysheet()
  1585. }
  1586. }
  1587. }
  1588. luckysheet.create(option)
  1589. }).catch((e) => {
  1590. showAlertWin(this, e)
  1591. })
  1592. },
  1593. /** 数据值显示类型事件 */
  1594. showDataTypeEvent(val) {
  1595. if (val == 1) {
  1596. this.itemShowParams.valType = '2'
  1597. } else {
  1598. this.itemShowParams.valType = '1'
  1599. }
  1600. },
  1601. clearRt() {
  1602. this.reportTypeInputName = '所有'
  1603. this.reportTypeInputId = []
  1604. this.reportType = null
  1605. this.searchReportTxt = ''
  1606. },
  1607. /** 弹出层关闭事件 */
  1608. dialogClose(done) {
  1609. this.chooseDataGroup = null
  1610. this.chooseItemData = []
  1611. this.chooseGroupItemList = []
  1612. this.filterItemData = ''
  1613. this.clearRt()
  1614. this.commTemplate = null
  1615. if (typeof(done) === 'function') {
  1616. done()
  1617. } else {
  1618. this.dialogBarChartVisible = false
  1619. this.dialogDataVisible = false
  1620. this.dialogDataItemVisible = false
  1621. this.dialogDataModelVisible = false
  1622. this.dialogCommReportVisible = false
  1623. this.dialogGroupItemVisible = false
  1624. }
  1625. },
  1626. /** 弹出层关闭事件 */
  1627. dialogItemClose(done) {
  1628. this.itemDataListByTree = []
  1629. this.filterItemData = ''
  1630. if (typeof(done) === 'function') {
  1631. done()
  1632. } else {
  1633. this.dialogDataItemVisible = false
  1634. }
  1635. },
  1636. /** 弹出层关闭事件 */
  1637. dialogDataModelClose() {
  1638. if (typeof(done) === 'function') {
  1639. done()
  1640. } else {
  1641. this.dialogDataModelVisible = false
  1642. }
  1643. },
  1644. /** 弹出层关闭事件 */
  1645. dialogBaseDataClose() {
  1646. this.chooseBaseData = null
  1647. if (typeof(done) === 'function') {
  1648. done()
  1649. } else {
  1650. this.dialogBaseDataVisible = false
  1651. }
  1652. },
  1653. /** 关闭右键弹出层 */
  1654. closeMenu() {
  1655. this.visibleChartMenu = false
  1656. }
  1657. }
  1658. }
  1659. </script>
  1660. <style rel="stylesheet/scss" lang="scss">
  1661. .cy-report-content {
  1662. width: 100%;
  1663. height: 100%;
  1664. padding-left: 6px;
  1665. color: #FFFFFF;
  1666. font-size: 14px;
  1667. background-color: #2c3e50;
  1668. display: flex;
  1669. flex-direction: row;
  1670. flex-wrap: nowrap;
  1671. }
  1672. .cy-div0 {
  1673. width: 30px;
  1674. height: 30px;
  1675. background: #767676;
  1676. }
  1677. .cy-div1 {
  1678. width: calc(100% - 90px);
  1679. height: 100%;
  1680. }
  1681. .cy-div2 {
  1682. width: 60px;
  1683. height: 100%;
  1684. z-index: 1;
  1685. background: #2c3e50;
  1686. overflow: auto;
  1687. }
  1688. .cy-report-ruler-unit {
  1689. width: 30px;
  1690. height: 30px;
  1691. display: flex;
  1692. justify-content: center;
  1693. align-items: center;
  1694. float: left;
  1695. border-right: 1px solid #2c3e50;
  1696. border-bottom: 1px solid #2c3e50;
  1697. }
  1698. .cy-ruler-top {
  1699. background: #767676;
  1700. width: 100%;
  1701. height: 30px;
  1702. }
  1703. .cy-box {
  1704. height: calc(100% - 50px);
  1705. width: calc(100% + 30px);
  1706. display: flex;
  1707. margin-left: -30px;
  1708. overflow: hidden;
  1709. .cy-ruler-left {
  1710. background: #767676;
  1711. width: 30px;
  1712. height: 100%;
  1713. }
  1714. .cy-box-main {
  1715. width: 100%;
  1716. height: 100%;
  1717. margin: 10px;
  1718. background: #FFFFFF;
  1719. }
  1720. }
  1721. .cy-chart-div {
  1722. display: flex;
  1723. flex-direction: column;
  1724. align-items: center;
  1725. cursor: pointer;
  1726. margin-bottom: 15px;
  1727. .svg-icon {
  1728. width: 50px;
  1729. height: 50px;
  1730. }
  1731. span {
  1732. font-size: 12px;
  1733. margin-top: -5px;
  1734. }
  1735. }
  1736. .label-title {
  1737. display: block !important;
  1738. .el-form-item__content {
  1739. width: calc(100% - 100px);
  1740. }
  1741. }
  1742. .el-transfer-panel {
  1743. width: 300px !important;
  1744. }
  1745. </style>