index.vue 51 KB

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