pptEnEditor.vue 58 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574
  1. <template>
  2. <div class="page-wrap">
  3. <div class="index-wrap ppt-page-wrap flex-column">
  4. <div class="cover-wrap" @click="openChooseCover">
  5. <div class="cover" :style="'background: no-repeat center/cover url('+firstPage.BackgroundImg+');background-color:#F2F6FA;background-size:100% 100%;'">
  6. <img src="~@/assets/img/ppt_m/add_first.png" />
  7. </div>
  8. <p class="hint-text">选择封面页</p>
  9. </div>
  10. <div class="hint-box"><div class="hint" @click="showHint"><span class="el-icon-info" style="margin-right:5px;"></span>操作说明</div></div>
  11. <div style="display:flex;justify-content: space-between;">
  12. <p style="text-align:start;color:#999999;">已添加{{pageList.length}}页</p>
  13. <p class="hint-text" style="cursor: pointer;" @click="saveCopyPages('cut')">剪切</p>
  14. <p class="hint-text" style="cursor: pointer;" @click="saveCopyPages('copy')">复制</p>
  15. <p style="color:#B72E18;cursor: pointer;" @click="openDeletePageDialog">批量删除</p>
  16. </div>
  17. <div class="index-list" ref="pptList" v-click-outside="resetCopyPages">
  18. <div class="empty" v-if="pageList.length===0">
  19. <img src="~@/assets/img/ppt_m/ppt-empty.png" alt="">
  20. <p>添加正文后,会在这里生成</p>
  21. </div>
  22. <transition-group name="flip-list">
  23. <index-item
  24. v-for="(item,index) in pageList" :key="item.id"
  25. :pageItem="item"
  26. :pageIndex="index"
  27. :currentItem="currentItem"
  28. :pageLength="pageList.length"
  29. :copyPagesMap="copyPagesMap"
  30. :ctrlKeyActive="ctrlKeyActive"
  31. :savePagesArrLength="savePagesArr.length"
  32. @deletePage="delPage"
  33. @click.native="handleClickItem(item)"
  34. @dragstart.native="(e)=>{dragstart(e,item)}"
  35. @dragenter.native="(e)=>{dragenter(e,item)}"
  36. @dragend.native="dragend(item)"
  37. @dragover.native="(e)=>{e.preventDefault()}"
  38. @changePage="changePageIndex"
  39. @operatePpt="handleOperate"
  40. />
  41. </transition-group>
  42. </div>
  43. </div>
  44. <div class="ppt-editor-wrap ppt-page-wrap" v-loading="isChartLoading" :element-loading-text="chartLoadingText">
  45. <div class="ppt-editor" ref="pptEditor" @paste="handlePasteOutSide">
  46. <!-- 显示全部ppt -->
  47. <template v-if="pageList.length">
  48. <div class="ppt-editor-item flex-column" v-for="(item,index) in pageList" :key="item.id">
  49. <AddFormat @addPage="addPage($event,index)" :chooseModalId="chooseModalId"/>
  50. <div class="ppt-item" :class="{'choose':currentItem.id===item.id}"
  51. v-loading="item.isUpdating" element-loading-text="正在更新图表..."
  52. @click="changeCurrentItem(item)">
  53. <!-- 标题 -->
  54. <div class="title-wrap" style="left:8%;width:62%;">
  55. <input type="text" placeholder="单击输入标题" v-model="item.title"/>
  56. </div>
  57. <!-- 内容 -->
  58. <component :is="getComponentName(item.modelId)"
  59. :ref="`pptPage_${index}`"
  60. :pageIndex="index"
  61. :pageItem="item"
  62. :key="item.key"
  63. :isEditLayer="isEditLayer"
  64. :choosedId="currentItem.id"
  65. :activeLayerEl="activeLayerEl"
  66. :isCopy="true"
  67. :isAdd="true"
  68. :amendatoryPositionInfo="item.positionInfo"
  69. @delChart="handleDelChart"
  70. @pasteImg="handlePasteImg"
  71. @changeText="handleChangeText"
  72. @changeActEl="changeActLayerEl"
  73. @copyShape="getCopyItem"
  74. @deleteLayer="deleteLayerEl"
  75. @reload="reloadPage"
  76. @addChart="handleAddChartToGallery"
  77. ></component>
  78. <!-- 删除按钮 -->
  79. <el-popconfirm
  80. @onConfirm="delPage(item)"
  81. confirmButtonText='删除'
  82. cancelButtonText='取消'
  83. confirmButtonType="text"
  84. icon="el-icon-info"
  85. iconColor="red"
  86. title="确定删除该页ppt吗">
  87. <span class="close-btn" @click.stop slot="reference"></span>
  88. </el-popconfirm>
  89. <!-- 页码 -->
  90. <span class="page-num">第{{index+1}}页</span>
  91. <!-- 更新图表 -->
  92. <span class="update-btn" v-show="item.modelId!==6&&item.elements.find((i)=>i.type==='chart')" @click.stop="updatePage(item)"><span class="update-ico"></span> 更新图表</span>
  93. </div>
  94. <AddFormat v-if="index===pageList.length-1" @addPage="addPage($event,index+1)" :chooseModalId="chooseModalId"/>
  95. </div>
  96. <!-- 自定义右键菜单 -->
  97. <context-menu :menu="[{id:1,text:'粘贴元素',eventName:'paste',canClick:Boolean(copyShape.id)}]" @paste="pasteLayerEl"/>
  98. </template>
  99. <template v-else>
  100. <div class="ppt-editor-item flex-column">
  101. <add-format @addPage="addPage($event,0)"/>
  102. </div>
  103. </template>
  104. </div>
  105. <div class="ppt-tool flex-column">
  106. <div class="tool-btn">
  107. <el-button v-permission="permissionBtn.enPPTPermission.pptEn_publish"
  108. type="primary" @click="handlePublish">去发布</el-button>
  109. <el-button @click="handleSave('save')">保存</el-button>
  110. <el-button type="text" @click="handleChangeEditModal"><i class="el-icon-sort" style="transform: rotate(90deg);margin-right:5px;"></i>{{isEditLayer?'ppt编辑':'图层编辑'}}</el-button>
  111. </div>
  112. <div class="richtext-tool"></div>
  113. <div class="addppt-right-box" v-if="!isEditLayer">
  114. <el-tabs v-model="tabsactive">
  115. <el-tab-pane :label="tab" :name="tab" v-for="tab in panelTabs" :key="tab"></el-tab-pane>
  116. </el-tabs>
  117. <div class="chart-tool flex-column" v-show="tabsactive == '图表'">
  118. <div class="chart-search">
  119. <el-input placeholder="关键字查找" v-model="key_word" size="medium" prefix-icon="el-icon-search" @input="() => {search_page=1;$refs.chartListRef.scrollTop = 0;getreportlist()}" style="max-width:420px;"></el-input>
  120. </div>
  121. <el-radio-group v-model="chart_source" @change="() => {search_page=1;$refs.chartListRef.scrollTop = 0;getreportlist()}" style="margin: 10px 0;">
  122. <el-radio :label="1" style="margin-bottom:5px">ETA图库</el-radio>
  123. <el-radio :label="2" style="margin-bottom:5px">商品价格曲线</el-radio>
  124. <el-radio :label="3" style="margin-bottom:5px">相关性图表</el-radio>
  125. <el-radio :label="6" style="margin-bottom:5px">拟合方程曲线</el-radio>
  126. <el-radio :label="7" style="margin-bottom:5px">统计特征</el-radio>
  127. <el-radio :label="10">跨品种分析</el-radio>
  128. </el-radio-group>
  129. <div style="margin: 10px 0">
  130. <el-checkbox v-model="isShowMe" @change="() => {search_page=1;$refs.chartListRef.scrollTop = 0;getreportlist()}">只看我的</el-checkbox>
  131. </div>
  132. <div class="chart-list" v-infinite-scroll="loadReportHandle" :infinite-scroll-immediate="false" ref="chartListRef">
  133. <template v-if="chartList.length">
  134. <div v-for="(item, index) in chartList" :key="index" @click="chooseChart(item,'chart')" class="chart-item" :style="item.Disabled && 'cursor: not-allowed;'">
  135. <div class="chartEn-mark" style="top: 0;left:0;" @click.stop="setEnHandle(item)">
  136. <span v-if="item.IsEnChart">En</span>
  137. <i class="el-icon-edit" v-else style="font-size:15px"/>
  138. </div>
  139. <p class="chart_tit">{{ chart_source===1 ? (item.ChartNameEn||item.ChartName) : item.ChartName }}</p>
  140. <img :src="item.ChartImage" ref="insert_img" />
  141. </div>
  142. </template>
  143. <tableNoData text="暂无数据" size="mini" v-else/>
  144. </div>
  145. </div>
  146. <div v-show="tabsactive == '沙盘'" class="chart-tool flex-column">
  147. <div class="chart-search">
  148. <el-input placeholder="沙盘名称/品种" v-model="sandTabelQuery.Keyword" size="medium" prefix-icon="el-icon-search" style="max-width:420px;"></el-input>
  149. </div>
  150. <div class="chart-list" id="sandTable" @scroll="sandTableHandleScroll">
  151. <template v-if="sandTableList.length">
  152. <div v-for="(item,index) in sandTableList" :key="index" class="sandTable-item" >
  153. <p class="chart_tit">{{item.Name}}</p>
  154. <img :src="item.PicUrl" style="width:100%" @click="chooseChart(item,'sandImage')"/>
  155. <!-- <p class="source-identification">来源:弘则研究</p> -->
  156. </div>
  157. <div v-loading = "sandTableLoading" class="loaded-text">{{loadedText}}</div>
  158. </template>
  159. <tableNoData text="暂无数据" size="mini" v-else/>
  160. </div>
  161. </div>
  162. <div v-show="tabsactive == '表格'" class="chart-tool flex-column">
  163. <div class="chart-search">
  164. <el-input placeholder="表格名称" v-model="sheetSearchObj.Keyword" size="medium" prefix-icon="el-icon-search" style="max-width:420px;" @input="getSheetList"></el-input>
  165. </div>
  166. <div class="chart-list" id="sandTable">
  167. <template v-if="sheetSearchList.length">
  168. <div v-for="(item,index) in sheetSearchList" :key="index" class="sandTable-item" >
  169. <p class="chart_tit">{{item.ExcelName}}</p>
  170. <img :src="item.ExcelImage" style="width:100%;object-fit: contain;height: 250px" @click="chooseChart(item,'sheet')"/>
  171. </div>
  172. </template>
  173. <tableNoData text="暂无数据" size="mini" v-else/>
  174. </div>
  175. </div>
  176. <div v-show="tabsactive == 'MyETA批量'" class="chart-tool flex-column">
  177. <insert-charts
  178. @handleImportMyChart="handleImportMyChart"
  179. :showEnMark="true"
  180. />
  181. </div>
  182. <div v-show="tabsactive == '语义分析插入'" class="chart-tool flex-column">
  183. <InsertSemantics />
  184. </div>
  185. </div>
  186. <!-- 图层编辑 -->
  187. <div class="layer-edit-box" v-else>
  188. <el-collapse v-model="activeNames" class="tool-list">
  189. <el-collapse-item title="图层元素" name="el">
  190. <div class="el-wrap">
  191. <div class="el-item"
  192. v-for="(shape,index) in layerElArr" :key="index" @click="addLayerEl(shape)">
  193. <el-tooltip class="item" effect="dark" :content="shape.name" placement="top">
  194. <ShapePreview :shape="shape" />
  195. </el-tooltip>
  196. </div>
  197. </div>
  198. </el-collapse-item>
  199. <el-collapse-item name="line"
  200. title="线条设置" >
  201. <LayerEditTool
  202. :isActiveEl="activeLayerEl.type==='line'?true:false"
  203. :elInfo="activeLayerEl.type==='line'?activeLayerEl:BaseLineShape"
  204. baseType="line"/>
  205. </el-collapse-item>
  206. <el-collapse-item name="shape"
  207. title="线框设置" >
  208. <LayerEditTool
  209. :isActiveEl="activeLayerEl.type==='shape'?true:false"
  210. :elInfo="activeLayerEl.type==='shape'?activeLayerEl:BaseRectShape"
  211. baseType="shape"/>
  212. </el-collapse-item>
  213. <el-collapse-item name="text"
  214. title="文本设置" >
  215. <LayerEditTool
  216. :isActiveEl="activeLayerEl.type==='text'?true:false"
  217. :elInfo="activeLayerEl.type==='text'?activeLayerEl:BaseTextShape"
  218. baseType="text"/>
  219. </el-collapse-item>
  220. </el-collapse>
  221. </div>
  222. </div>
  223. </div>
  224. <!-- 选择封面弹窗 -->
  225. <choose-cover v-if="isShowChooseCover"
  226. :firstPage="firstPage"
  227. :pptCoverList="pptCoverList"
  228. :PptId="pptId"
  229. @close="closeChooseCover"
  230. @save="saveCover"
  231. />
  232. <!-- 批量删除弹窗 -->
  233. <delete-page-dialog
  234. :deletePageShow="deletePageShow"
  235. :pageList="pageList"
  236. :optionMap="optionMap"
  237. ref="deletePage"
  238. @cancel="deletePageShow=false"
  239. @delete="handleDeletePages"
  240. />
  241. <!-- 一键改版弹窗 -->
  242. <change-format-dialog
  243. :changeFormatPageShow="changeFormatPageShow"
  244. :pptModelId="choosedItem?choosedItem.modelId:0"
  245. @cancel="changeFormatPageShow=false"
  246. @changeModel="changeFormatPage"
  247. />
  248. <!-- 插入PPT弹窗 -->
  249. <insert-page-dialog
  250. :insertPageShow="insertPageShow"
  251. @cancel="insertPageShow=false"
  252. @insert="handleInsert"
  253. />
  254. <!-- 设置英文信息弹窗 -->
  255. <set-en-name-dia
  256. :isOpenDialog="setEnName"
  257. :formData="formItemArray"
  258. @cancel="setEnName=false"
  259. @updateEnName="updateEnName"
  260. :chartType="enChartInfo.ChartType"
  261. :datainfo="enChartInfo.ChartType===10?JSON.parse(enChartInfo.ExtraConfig):null"
  262. :edblist="enChartInfo.ChartType===10?enEdblist:[]"
  263. />
  264. <!-- 添加到 我的图库 弹窗 -->
  265. <addMyClassifyDia
  266. :isAddMyDialog="addMyChartShow"
  267. :add_id="addChartInfoId"
  268. :add_ids="addChartClassifyIds"
  269. @cancel="addMyChartShow = false"
  270. @addSuccess="addChartToGallery"
  271. />
  272. </div>
  273. </template>
  274. <script>
  275. import {countComponentName,checkClipboardItems,createRandomCode,checkPPTpageElemant,getChartInfo} from './utils/untils';
  276. import {modelInfo,defaultPosition} from './utils/config'
  277. import { dataBaseInterface ,sandInterface } from "@/api/api.js";
  278. import futuresInterface from '@/api/modules/futuresBaseApi';
  279. import chartRelevanceApi from '@/api/modules/chartRelevanceApi';
  280. import { fittingEquationInterface,statisticFeatureInterface,crossVarietyInterface } from '@/api/modules/chartRelevanceApi';
  281. import pptmixin from '../mixins/pptMixins';
  282. import mixins from '../mixins/mixins';
  283. import layerMixins from '../mixins/layerMixins';
  284. import pptEditorMixins from '../mixins/pptEditorMixins';
  285. import Highcharts from "highcharts/highstock";
  286. import HighchartszhCN from '@/utils/highcahrts-zh_CN'
  287. HighchartszhCN(Highcharts)
  288. import IndexItem from './components/IndexItem.vue';
  289. import ChooseCover from './components/editor/ChooseCoverEn.vue';
  290. import AddFormat from './components/editor/AddFormat.vue';
  291. import {pptEnInterface} from '@/api/modules/pptEnApi.js';
  292. import * as sheetInterface from '@/api/modules/sheetApi.js';
  293. import {uploadFileDirect} from "@/utils/common.js"
  294. import ShapePreview from './components/layer/shapePreview.vue';
  295. import LayerEditTool from './components/layer/layerEditTool.vue';
  296. import DeletePageDialog from './components/editor/DeletePageDialog.vue';
  297. import ChangeFormatDialog from './components/editor/ChangeFormatDialog.vue';
  298. import InsertPageDialog from './components/editor/InsertPageDialog.vue';
  299. import addMyClassifyDia from '@/views/dataEntry_manage/components/addMyClassifyDia';
  300. import InsertCharts from './components/editor/InsertCharts.vue';
  301. import setEnNameDia from '@/views/dataEntry_manage/components/setEnNameDia.vue';
  302. import ContextMenu from './components/ContextMenu.vue';
  303. import InsertSemantics from './components/editor/InsertSemantics.vue';
  304. export default {
  305. mixins:[pptmixin,//ppt页面共同逻辑
  306. mixins,//图表加载逻辑
  307. layerMixins,//图层操作逻辑
  308. pptEditorMixins,//编辑页共同逻辑
  309. ],
  310. components: {
  311. IndexItem,
  312. ChooseCover,
  313. AddFormat,
  314. ShapePreview,
  315. LayerEditTool,
  316. DeletePageDialog,
  317. ChangeFormatDialog,
  318. InsertPageDialog,
  319. setEnNameDia,
  320. addMyClassifyDia,
  321. InsertCharts,
  322. ContextMenu,
  323. InsertSemantics
  324. },
  325. data() {
  326. return {
  327. pageList:[],//ppt数组
  328. currentItem:null,//当前活跃的page
  329. currentIndex:0,//当前活跃的pageIndex
  330. oldNum:null,//拖动相关
  331. newNum:null,//拖动相关
  332. chooseModalId:1,//上一次选择的版式Id
  333. firstPage:{
  334. Title:'',
  335. ReportType:'',
  336. BackgroundImg:'',
  337. PptDate:(new Date().getFullYear())+'.'+(new Date().getMonth()+1),
  338. BackIndex:0,
  339. TemplateType:1
  340. },//封面信息
  341. key_word:'',//搜索图表关键字
  342. chartList:[],//图表数组
  343. imgUrl:'',//黏贴图片上传后的地址
  344. isShowChooseCover:false,
  345. getDataLoading:null,//获取动态图表数据/上传图片的loading,
  346. isSave:false,//是否已手动保存过
  347. pptId:null,//新增后获得的pptid
  348. maxPageNum:0,//可以添加的最大页数
  349. maxPageChart:0,//可以添加的最大图表数
  350. chartNum:0,//当前PPT的总图表数
  351. tabsactive:'图表', //右侧区域显示内容 图表 | 沙盘
  352. // 沙盘图查询参数
  353. sandTabelQuery:{
  354. PageSize: 15,
  355. CurrentIndex: 1,
  356. Keyword: '',
  357. },
  358. sandTableLoading:false,
  359. sandTableTotal:0,//沙盘总共条数
  360. sandTableList:[],
  361. loadedText:'',
  362. // 沙盘图分页加载 是否在加载中
  363. isRequesting:false,
  364. catalogId:0,//ppt目录id,添加ppt时需要
  365. ReportId:0,//ppt对应的双周报id,如果没转过,则为0
  366. isChartLoading:false,//是否正在加载图表/图片
  367. panelTabs: [ '图表','MyETA批量','沙盘','表格','语义分析插入' ],
  368. sheetSearchList:[],
  369. sheetSearchObj: {
  370. Keyword:''
  371. },
  372. deletePageShow:false,//控制批量删除弹窗是否显示
  373. chartLoadingText:'拼命加载中...',
  374. loopTimer:null,//自动保存标识
  375. currentLang:'en',//语言标识
  376. search_page: 1,
  377. search_page_sizes: 20,
  378. search_have_more: true,
  379. chart_source: 1,//图表来源 1 eta 2 商品价格
  380. isShowMe:false
  381. };
  382. },
  383. created() {
  384. this.getSandTable()
  385. },
  386. mounted(){
  387. if(Highcharts.charts){
  388. Highcharts.charts.length=0
  389. }
  390. //获取图表数据
  391. this.getreportlist()
  392. //获取PPT限制
  393. this.getPPTLimit()
  394. this.getSheetList();
  395. this.init()
  396. window.addEventListener('message',this.reInitIframe)
  397. },
  398. watch:{
  399. 'sandTabelQuery.Keyword'(newval,oldval){
  400. $('#sandTable').animate({scrollTop:0},100);
  401. this.sandTabelQuery.CurrentIndex = 1
  402. this.getSandTable()
  403. }
  404. },
  405. methods: {
  406. getPPTLimit(){
  407. pptEnInterface.getConfig().then(res=>{
  408. if(res.Ret===200){
  409. this.maxPageNum = res.Data.ppt_num
  410. this.maxPageChart = res.Data.chart_num
  411. }
  412. })
  413. },
  414. async init(){
  415. this.dataLoading = this.$loading({
  416. lock: true,
  417. text: '正在获取ppt数据...',
  418. spinner: 'el-icon-loading',
  419. customClass:'loading',
  420. target:document.querySelector('.page-wrap'),
  421. background: 'rgba(255, 255, 255, 0.8)'
  422. });
  423. await this.getpptData()
  424. //遍历pageList,初始化图表,文字
  425. for(let i=0;i<this.pageList.length;i++){
  426. //计算图表总数
  427. const chartElements = this.pageList[i].elements.filter((item) => {
  428. return item.type === "chart";
  429. });
  430. this.chartNum+=chartElements.length
  431. await this.initPageElements(this.pageList[i])
  432. }
  433. //获取已加载图表的信息
  434. let chartInfoMap = {}
  435. for(let i=0;i<this.pageList.length;i++){
  436. this.pageList[i].elements.forEach(item=>{
  437. if(item.type==='chart'){
  438. let temp = getChartInfo(this.optionMap[item.chartId])
  439. chartInfoMap[item.chartId] = temp
  440. }
  441. })
  442. }
  443. this.$store.commit('SET_CHART_INFO_MAP',chartInfoMap)
  444. //添加ppt的情况:获取目录id
  445. if(!this.$route.query.id){
  446. this.catalogId = Number(sessionStorage.getItem('selectedCatalog'))
  447. }
  448. //防止自动保存时,有某一页处于更新图表的状态,其isUpdating为true
  449. this.pageList.forEach(i=>{
  450. i.isUpdating = false
  451. })
  452. this.dataLoading.close();
  453. $('.ppt-item').css('background-image',`url(${this.pptBgImage})`);
  454. },
  455. async getpptData(){
  456. const {id} = this.$route.query
  457. if(id){
  458. await this.getpptDataById(id)
  459. const {status} = this.result
  460. if(status===200){
  461. const {content,FirstPage,ReportId} = this.result
  462. this.pageList = content
  463. this.firstPage = FirstPage
  464. this.firstPage.BackIndex = FirstPage.TemplateType-1
  465. this.currentItem = this.pageList[0]
  466. this.ReportId=ReportId
  467. //开启自动保存
  468. this.autoSave()
  469. }else{
  470. this.$message.error('获取ppt数据失败!')
  471. this.dataLoading.close();
  472. window.close()
  473. }
  474. }else{
  475. this.pageList = []
  476. this.currentItem = null
  477. }
  478. this.currentIndex = 0
  479. },
  480. //打开选择封面页弹窗
  481. openChooseCover(){
  482. this.isShowChooseCover = true
  483. },
  484. //关闭选择封面页弹窗
  485. closeChooseCover(){
  486. this.isShowChooseCover = false
  487. },
  488. //保存修改的封面页
  489. saveCover(info){
  490. console.log(info)
  491. this.firstPage = info
  492. this.closeChooseCover()
  493. },
  494. //在指定位置添加一个有版式的空白页
  495. addPage(modelId,index) {
  496. let page = {
  497. id: createRandomCode(),
  498. key:0,
  499. /* isUpdating:false, */
  500. modelId: modelId,
  501. title:'',
  502. elements: [],
  503. }
  504. this.pageList.splice(index,0,page)
  505. this.changeCurrentItem(page)
  506. this.chooseModalId = modelId
  507. },
  508. //计算ppt的版式名称
  509. getComponentName(modelId){
  510. return countComponentName(modelId);
  511. },
  512. //删除一页
  513. delPage(item) {
  514. //找到index
  515. const index = this.pageList.findIndex((i) => i.id === item.id);
  516. //选中它
  517. this.changeCurrentItem(item)
  518. //计算出该页有多少个图表,chartNum-=num
  519. const chartElements = this.pageList[index].elements.filter((item) => {
  520. return item.type === "chart";
  521. });
  522. this.chartNum-=chartElements.length
  523. //从pageList移除这一页
  524. this.pageList.splice(index, 1);
  525. //更新活动页(currentItem)
  526. if (this.pageList.length === 0) {
  527. //删除的是仅有的一页
  528. this.currentIndex = 0;
  529. } else if (this.pageList.length === this.currentIndex) {
  530. //删除的是最后一页
  531. this.currentIndex = this.pageList.length - 1;
  532. }
  533. if(this.pageList.length===0){
  534. this.currentItem = null
  535. }else{
  536. this.changeCurrentItem(this.pageList[this.currentIndex])
  537. }
  538. this.$message.success('删除成功')
  539. },
  540. //删除活跃页的某一位置的图表/图片/其他组件
  541. handleDelChart(position) {
  542. this.currentItem.elements.map((i)=>{
  543. if(i.position===position){
  544. this.deleteFormatEl(i.type,position)
  545. }
  546. })
  547. //更新currentItem和pageList[currentIndex]
  548. this.currentItem.elements = this.currentItem.elements.filter(
  549. (i) => i.position !== position
  550. );
  551. this.pageList[this.currentIndex] = this.currentItem;
  552. //有已经初始化了的文本框的话,做个标记
  553. const textElements = this.currentItem.elements.filter((item)=>{
  554. return item.type === 'text'
  555. })
  556. if(textElements.length>0){
  557. this.pageList[this.currentIndex].isLoadText = true
  558. }
  559. },
  560. deleteFormatEl(type,position){
  561. if(type==='chart'){
  562. this.chartNum--
  563. $(`#${type}_${this.currentIndex}_${position}`).html(`<img id="img_${this.currentIndex}_${position}" />`)
  564. $(`#preview_${type}_${this.currentIndex}_${position}`).html(`<img id="preview_img_${this.currentIndex}_${position}" />`)
  565. }
  566. if(type==='image'){
  567. $(`#${type}_${this.currentIndex}_${position}`).parent().html(`<img id="${type}_${this.currentIndex}_${position}" />`)
  568. $(`#preview_${type}_${this.currentIndex}_${position}`).parent().html(`<img id="preview_${type}_${this.currentIndex}_${position}" />`)
  569. }
  570. if(type==='sheet'){
  571. this.$refs[`pptPage_${this.currentIndex}`][0].$refs[`sheet_${this.currentIndex}_${position}`].getSheetData('',[])
  572. this.$refs.deletePage.isLoaded&&this.$refs.deletePage.$refs[`preview_${this.currentIndex}`][0].$refs[`preview_sheet_${this.currentIndex}_${position}`].getSheetData('',[])
  573. }
  574. },
  575. handlePasteOutSide:_.throttle(async function(e){
  576. //e:粘贴板事件
  577. console.log(e)
  578. //如果是标题/文本编辑框,直接返回
  579. if(e.target.tagName=='INPUT'||e.target.id==='mcepastebin') {
  580. return
  581. }
  582. //如果是文本框内右键粘贴,也返回
  583. /* if(e.path.some((item)=>{if(item.className)return item.className.includes('mce-content-body')})){
  584. return
  585. } */
  586. if(e.target.tagName!=='DIV') return
  587. //算出currentItem还有哪些位置可以插入图表
  588. const {isAdd,addPositions}=this.checkElements(this.currentItem.modelId,this.currentItem.elements,'chart')
  589. if(!isAdd){
  590. this.$message("已无位置,请删除现有图表/图片")
  591. return
  592. }
  593. //从clipboardData中获取图片
  594. if(!e.clipboardData) return
  595. const clipboardDataItems = e.clipboardData.items
  596. const clipboardDataFirstItem = clipboardDataItems[0]
  597. if(clipboardDataFirstItem){
  598. for (const item of clipboardDataItems) {
  599. if (item.kind === 'file' && item.type.indexOf('image') !== -1) {
  600. const imageFile = item.getAsFile()
  601. if (imageFile) this.$refs[`pptPage_${this.currentIndex}`][0].handlePaste(imageFile,addPositions[0])
  602. return
  603. }
  604. }
  605. }
  606. //clipboardData中没有图片,从navigator.clipboard.read里获取图片
  607. let clipboardItems = null
  608. try{
  609. clipboardItems = await navigator.clipboard.read()
  610. }catch(error){
  611. this.$message.error("剪贴板读取不到文件!")
  612. return
  613. }
  614. const blob = await checkClipboardItems(clipboardItems)
  615. this.$refs[`pptPage_${this.currentIndex}`][0].handlePaste(blob,addPositions[0])
  616. },300),
  617. //黏贴图片
  618. handlePasteImg:_.throttle(async function($event){
  619. /** $event:
  620. * position:1
  621. * src:base64格式的
  622. */
  623. //这里进行上传操作
  624. if(this.isChartLoading) return
  625. this.chartLoadingText = "上传图片中..."
  626. this.isChartLoading = true
  627. // const params = new FormData();
  628. // params.append('Image',$event.src)
  629. // const { Data } = await dataBaseInterface.uploadImg(params);
  630. // this.imgUrl = Data.ResourceUrl
  631. //直接走oss不走接口
  632. let clientType = this.$setting.dynamicOutLinks.ObjectStorageClient
  633. || this.$store.state.dynamicOutLinks.ObjectStorageClient
  634. || JSON.parse(localStorage.getItem('dynamicOutLinks')).ObjectStorageClient;
  635. let temName = `ppt/image/${new Date().getTime()}`
  636. const res = await uploadFileDirect(clientType,$event.src,temName)
  637. this.imgUrl = res;
  638. //console.log('OK',$event)
  639. let temp_elements = this.addElement(
  640. this.currentItem.elements,
  641. [$event.position],
  642. "image",
  643. -1,
  644. ""
  645. );
  646. this.refleshElements(temp_elements)
  647. this.initImage(`#image_${this.currentIndex}_${$event.position}`,this.imgUrl)
  648. this.initPreviewPageEl()
  649. this.isChartLoading = false
  650. },300),
  651. //改变活跃页某一位置的文字
  652. handleChangeText({ position, text, richContent,pageItemId }) {
  653. //处理初始化的场景
  654. if(this.currentItem.id!==pageItemId) return
  655. //如果该位置元素为空,说明文字编辑器没有初始化,添加一个元素
  656. if (
  657. this.currentItem.elements.findIndex((i) => i.position === position) ===
  658. -1
  659. ) {
  660. let tempEls = this.addElement(
  661. this.currentItem.elements,
  662. [position],
  663. "text",
  664. -1,
  665. { text, richContent }
  666. );
  667. this.refleshElements(tempEls);
  668. }
  669. this.currentItem.elements.map((i) => {
  670. if (i.position === position) {
  671. i.content = text;
  672. i.richContent = richContent;
  673. }
  674. });
  675. this.pageList[this.currentIndex] = this.currentItem;
  676. },
  677. //切换活跃页(currentItem)
  678. changeCurrentItem(item){
  679. const {id} = item
  680. //切换到其他活跃页的时候,需退出图层编辑模式
  681. if(this.currentItem&&this.currentItem.id!==id){
  682. this.isEditLayer = false
  683. this.activeLayerEl = {}
  684. }
  685. this.pageList.map((item,index)=>{
  686. if(item.id===id){
  687. this.currentItem = item
  688. this.currentIndex = index
  689. }
  690. })
  691. this.$nextTick(()=>{
  692. let height = $('.ppt-editor-item')[0].offsetHeight
  693. //pptEditor的滚动条动画
  694. this.$refs.pptEditor.scrollTo({
  695. top:height*this.currentIndex,
  696. left:0,
  697. behavior: 'smooth'
  698. })
  699. let listDomArr = $('.index-item')
  700. let heightSum = 0
  701. for(let i=0;i<this.currentIndex;i++){
  702. heightSum+=listDomArr[i].offsetHeight+5
  703. }
  704. //pptList的滚动条动画
  705. this.$refs.pptList.scrollTo({
  706. top:heightSum,
  707. left:0,
  708. behavior: 'smooth'
  709. })
  710. })
  711. },
  712. //获取图表列表
  713. async getreportlist(){
  714. let params = {
  715. Keyword: this.key_word,
  716. CurrentIndex: this.search_page,
  717. PageSize: this.search_page_sizes,
  718. IsShowMe: this.isShowMe
  719. }
  720. // let res=null
  721. // if(this.chart_source === 1){
  722. // res=await dataBaseInterface.chartSearchByEs(params)
  723. // }else if(this.chart_source === 2){
  724. // res=await futuresInterface.searchChart(params)
  725. // }else if(this.chart_source === 3){
  726. // res=await chartRelevanceApi.searchChart(params)
  727. // }else if(this.chart_source === 6){
  728. // res=await fittingEquationInterface.searchChart(params)
  729. // }else if(this.chart_source === 7){
  730. // res=await statisticFeatureInterface.searchChart(params)
  731. // }
  732. const apiMap = {
  733. 1: dataBaseInterface.chartSearchByEs,
  734. 2: futuresInterface.searchChart,
  735. 3: chartRelevanceApi.searchChart,
  736. 6: fittingEquationInterface.searchChart,
  737. 7: statisticFeatureInterface.searchChart,
  738. 10: crossVarietyInterface.searchChart,
  739. }
  740. let res = await apiMap[this.chart_source](params)
  741. if (res.Ret !== 200) return;
  742. res.Data.List = res.Data.List || [];
  743. this.chartList = this.search_page > 1 ? [...this.chartList,...res.Data.List] : res.Data.List;
  744. this.search_have_more = this.search_page <= res.Data.Paging.Pages;
  745. },
  746. loadReportHandle() {
  747. if(!this.search_have_more) return
  748. this.search_page++;
  749. this.getreportlist(this.key_word);
  750. },
  751. // 获取沙盘图列表 分页
  752. getSandTable(){
  753. this.sandTableLoading = true
  754. this.isRequesting = true
  755. sandInterface.sandlistByQuote(this.sandTabelQuery).then(({Data:{List,Paging}})=>{
  756. if(this.sandTabelQuery.CurrentIndex>1){
  757. this.sandTableList = [...this.sandTableList,...List]
  758. }else{
  759. this.sandTableList = List
  760. this.sandTableTotal = Paging.Totals
  761. console.log(this.sandTableList.length,this.sandTableTotal);
  762. if(this.sandTableList.length>=this.sandTableTotal){
  763. this.loadedText = '已全部加载完'
  764. }else{
  765. this.loadedText=''
  766. }
  767. }
  768. })
  769. .finally(()=>{
  770. this.sandTableLoading = false
  771. this.isRequesting = false
  772. })
  773. },
  774. // 沙盘列表滚动事件,触底加载下一页数据
  775. sandTableHandleScroll:_.debounce(function (e) {
  776. if(this.sandTableList.length>=this.sandTableTotal){
  777. this.loadedText = '已全部加载完'
  778. return
  779. }
  780. if(e.target.scrollHeight-e.target.clientHeight-e.target.scrollTop<10 && !this.isRequesting){
  781. this.sandTabelQuery.CurrentIndex++
  782. this.getSandTable()
  783. }
  784. },200),
  785. //点击右侧图表或者沙盘图
  786. chooseChart:_.throttle(async function(item,type){
  787. if(this.pageList.length===0||!this.currentItem){
  788. this.$message.warning("请先添加页面");
  789. return;
  790. }
  791. if(item.Disabled&&type==='chart') return this.$message.warning('内部图表,不允许插入ppt')
  792. if(this.isChartLoading) return
  793. //获取当前活动页
  794. const { elements, modelId } = this.currentItem;
  795. //判断element是否能再加入图表
  796. const { isAdd, addPositions } = this.checkElements(
  797. modelId,
  798. elements,
  799. "chart"
  800. );
  801. if (!isAdd) {
  802. this.$message.warning("已无位置,请删除现有图表/图片/表格");
  803. return;
  804. }
  805. let temp_elements = null;
  806. this.chartLoadingText = '拼命加载中...'
  807. // type:sandImage 沙盘图 chart 图表 sheet
  808. if(type=='chart'){
  809. this.isChartLoading = true
  810. await this.getchartData(item.UniqueCode,'en');
  811. //加入图表
  812. temp_elements = this.addElement(
  813. elements,
  814. addPositions,
  815. "chart",
  816. item.UniqueCode,
  817. ""
  818. );
  819. this.chartNum++
  820. this.refleshElements(temp_elements);
  821. /* const page = this.currentItem
  822. const pageElements = page.elements.filter(i=>i.type==='chart') */
  823. //优化一下
  824. const tempEls = [{type:'chart',position:addPositions[0],chartId:item.UniqueCode}]
  825. const tempPage = {id:this.currentItem.id,elements:tempEls}
  826. //这里不直接用initChart是因为initCharts里可以判断该图表有没有数据,然后显示图表或显示“该图表已被删除”
  827. this.initCharts(/* pageElements,page */tempEls,tempPage)
  828. this.isChartLoading = false
  829. //更新图表数据
  830. let chartInfoMap = _.cloneDeep(this.$store.state.ppt.chartInfoMap)
  831. let chartInfo = getChartInfo(this.optionMap[item.UniqueCode])
  832. chartInfoMap[item.UniqueCode] = chartInfo
  833. this.$store.commit('SET_CHART_INFO_MAP',chartInfoMap)
  834. //需要重新渲染一次缩略图
  835. this.initPreviewPageEl()
  836. }else if(type === 'sandImage'){
  837. // 插入沙盘图
  838. this.imgUrl = item.PicUrl
  839. this.isChartLoading = true
  840. //加入沙盘图
  841. temp_elements = this.addElement(
  842. elements,
  843. addPositions,
  844. "image",
  845. -1,
  846. ""
  847. );
  848. this.refleshElements(temp_elements);
  849. this.initImage(`#image_${this.currentIndex}_${addPositions[0]}`,this.imgUrl)
  850. this.isChartLoading = false
  851. this.initPreviewPageEl()
  852. }
  853. type === 'sheet' && this.insertSheet(item,elements,addPositions,temp_elements);
  854. },300),
  855. //检查是否还能再加入元素,返回{isAdd:bool,addPosition:[]},isAdd:是否能插入,addPositions:可以插入的位置
  856. //type:[chart,text] chart包含除text外的全部元素
  857. checkElements(modelId, elements, type) {
  858. const info = modelInfo[modelId];
  859. let addPositions = [],
  860. isAdd = false;
  861. const elPositions = elements.map((i) => i.position);
  862. let typeNum = 0
  863. elements.forEach(i=>{
  864. if(i.type!=='text') typeNum++
  865. })
  866. //先判断长度
  867. if (elements.length >= info.elNum) return { isAdd, addPositions };
  868. //去重:info.positions和el
  869. let tempArr = info.positions.filter((i) => elPositions.indexOf(i) < 0);
  870. if (!tempArr.length) return { isAdd, addPositions };
  871. //同一版式下的位置支持互换,位置和类型根据 当前版式.positionInfo决定
  872. //判断数量
  873. if(typeNum>=info.elChartNum){
  874. return { isAdd, addPositions };
  875. }
  876. const positionInfo = this.$refs[`pptPage_${this.currentIndex}`][0].positionInfo
  877. //判断类型
  878. tempArr.forEach((i)=>{
  879. if(positionInfo[i].type!=='text'){
  880. addPositions.push(i)
  881. }
  882. })
  883. if (!addPositions.length) {
  884. return { isAdd, addPositions };
  885. } else {
  886. isAdd = true;
  887. return { isAdd, addPositions };
  888. }
  889. },
  890. //添加一个元素进els,type:[chart,text,image],chart时有chartId,text为空;text/image时,chartId为-1
  891. addElement(
  892. el,
  893. positions,
  894. type,
  895. UniqueCode,
  896. { text, richContent } = { text: "", richContent: "" }
  897. ) {
  898. let temp = null;
  899. if (type === "chart") {
  900. temp = {
  901. type: "chart",
  902. position: positions[0],
  903. chartId: UniqueCode,
  904. };
  905. } else if (type === "text") {
  906. temp = {
  907. type: "text",
  908. position: positions[0],
  909. content: text,
  910. richContent: richContent,
  911. };
  912. }else if (type==='image'){
  913. temp = {
  914. type:'image',
  915. position:positions[0],
  916. src:this.imgUrl
  917. }
  918. }else if(type==='sheet'){
  919. temp={
  920. type:'sheet',
  921. position:positions[0],
  922. sheetId:UniqueCode
  923. }
  924. }
  925. el.push(temp);
  926. return el;
  927. },
  928. //更新ppt页元素(数据)
  929. refleshElements(els){
  930. this.currentItem.elements = els;
  931. this.pageList[this.currentIndex] = this.currentItem;
  932. this.$refs[`pptPage_${this.currentIndex}`][0].initPositionInfo()
  933. },
  934. //手动保存PPT
  935. handleSave(type){
  936. //保存走save_checkPPT,发布走checkPPT
  937. let checkResult = null
  938. if(type==='save'){
  939. checkResult = this.save_checkPPT()
  940. }else{
  941. checkResult = this.checkPPT()
  942. }
  943. if(!checkResult.result){
  944. this.$message.warning(checkResult.hintText)
  945. return
  946. }
  947. let Content = JSON.stringify(this.pageList)
  948. const {Title,ReportType,PptDate,BackgroundImg,BackIndex} = this.firstPage
  949. const FirstPage = {
  950. Title,ReportType,PptDate,BackIndex,
  951. ImgUrl:BackgroundImg,
  952. TemplateType:BackIndex+1
  953. }
  954. if(this.$route.query.id||this.pptId){
  955. this.editPPT(FirstPage,Content,type)
  956. }else{
  957. this.addPPT(FirstPage,Content)
  958. }
  959. },
  960. addPPT(FirstPage,Content){
  961. pptEnInterface.addppt({
  962. FirstPage:FirstPage,
  963. Content:Content,
  964. GroupId:this.catalogId
  965. }).then(res=>{
  966. if(res.Ret===200){
  967. this.$message.success('新增成功')
  968. this.isSave = true
  969. this.pptId = res.Data.PptId
  970. sessionStorage.removeItem('selectedCatalog')
  971. //开启自动保存
  972. this.autoSave()
  973. }
  974. })
  975. },
  976. editPPT(FirstPage,Content,type){
  977. const ppt_id = this.$route.query.id||this.pptId
  978. pptEnInterface.editppt({
  979. PptId:parseInt(ppt_id),
  980. FirstPage:FirstPage,
  981. Content:Content
  982. }).then(res=>{
  983. if(res.Ret===200){
  984. if(type==='save'){
  985. this.$message.success('编辑成功')
  986. }
  987. this.isSave = true
  988. this.pptId = res.Data.PptId
  989. }
  990. })
  991. },
  992. //自动保存PPT
  993. autoSave(){
  994. if(this.loopTimer) return
  995. this.loopTimer = setInterval(()=>{
  996. const ppt_id = this.$route.query.id||this.pptId
  997. const {Title,ReportType,PptDate,BackgroundImg,BackIndex} = this.firstPage
  998. const FirstPage = {
  999. Title,ReportType,PptDate,BackIndex,
  1000. ImgUrl:BackgroundImg,
  1001. TemplateType:BackIndex+1
  1002. }
  1003. //防止自动保存时,有某一页处于更新图表的状态,其isUpdating为true
  1004. let pageList = this.pageList.map(i=>{
  1005. i.isUpdating = false
  1006. return i
  1007. })
  1008. let Content = JSON.stringify(pageList)
  1009. pptEnInterface.saveLog({
  1010. PptId:parseInt(ppt_id),
  1011. FirstPage:FirstPage,
  1012. Content:Content
  1013. }).then((res)=>{})
  1014. },10000)
  1015. },
  1016. //保存时的校验规则:封面信息,至少一页
  1017. save_checkPPT(){
  1018. if(!this.firstPage.Title){
  1019. return {result:false,hintText:'请输入封面标题!'}
  1020. }else if(!this.firstPage.ReportType){
  1021. return {result:false,hintText:'请输入ppt类型!'}
  1022. }
  1023. if(this.pageList.length===0){
  1024. return {result:false,hintText:'请至少添加一张PPT!'}
  1025. }
  1026. return {result:true,hintText:''}
  1027. },
  1028. //发布时的校验规则:封面信息,每一页标题及内容
  1029. checkPPT(){
  1030. //检验首页
  1031. if(!this.firstPage.Title){
  1032. return {result:false,hintText:'请输入封面标题!'}
  1033. }else if(!this.firstPage.ReportType){
  1034. return {result:false,hintText:'请输入ppt类型!'}
  1035. }
  1036. if(this.pageList.length===0){
  1037. return {result:false,hintText:'请至少添加一张PPT!'}
  1038. }
  1039. //检验每一页
  1040. for(let i=0;i<this.pageList.length;i++){
  1041. if(!this.pageList[i].title){
  1042. return {result:false,hintText:`请输入第${i+1}张PPT的标题!`}
  1043. }
  1044. //无内容
  1045. if(this.pageList[i].elements.length===0){
  1046. return {result:false,hintText:`请填写第${i+1}张PPT的内容!`}
  1047. }
  1048. }
  1049. return {result:true,hintText:''}
  1050. },
  1051. async handlePublish(){
  1052. if(!this.isSave){
  1053. this.$message.warning("请先保存!")
  1054. return
  1055. }else{
  1056. const {result,hintText} = this.checkPPT()
  1057. if(!result){
  1058. this.$message.warning(hintText)
  1059. return
  1060. }
  1061. }
  1062. await this.handleSave('pub')
  1063. //ppt4.0后,合并后的PPT可能会超出页数or图表限制,在这里做个校验
  1064. if(this.pageList.length>this.maxPageNum){
  1065. this.$message.warning(`最多可添加${this.maxPageNum}页PPT,请修改后再试!`)
  1066. return
  1067. }
  1068. if(this.chartNum>this.maxPageChart){
  1069. this.$message.warning(`最多可添加${this.maxPageChart}图表,请修改后再试!`);
  1070. return;
  1071. }
  1072. this.$router.push({path:'/pptenpublish',query:{id:this.pptId}})
  1073. },
  1074. //拖动相关
  1075. dragstart(e,value) {
  1076. e.dataTransfer.effectAllowed = "move";
  1077. this.oldNum = value;
  1078. },
  1079. dragend() {
  1080. if (this.oldNum != this.newNum) {
  1081. let oldIndex = this.pageList.indexOf(this.oldNum);
  1082. let newIndex = this.pageList.indexOf(this.newNum);
  1083. let newItems = [...this.pageList];
  1084. // 删除老的节点
  1085. newItems.splice(oldIndex, 1);
  1086. // 在列表中目标位置增加新的节点
  1087. newItems.splice(newIndex, 0, this.oldNum);
  1088. this.pageList = [...newItems];
  1089. this.$message.success('移动成功')
  1090. //如果拖动的是当前选中的item,就定位到该item
  1091. if(this.oldNum.id===this.currentItem.id){
  1092. this.changeCurrentItem(this.currentItem)
  1093. }
  1094. }
  1095. },
  1096. dragenter(e,value) {
  1097. e.preventDefault()
  1098. this.newNum = value;
  1099. },
  1100. changePageIndex($event){
  1101. this.oldNum = this.pageList[$event.oldPos-1]
  1102. this.newNum = this.pageList[$event.newPos-1]
  1103. this.dragend()
  1104. },
  1105. /* 搜索表格 */
  1106. getSheetList() {
  1107. sheetInterface
  1108. .sheetList({
  1109. Keyword: this.sheetSearchObj.Keyword,
  1110. CurrentIndex: 1,
  1111. PageSize: 10000
  1112. })
  1113. .then((res) => {
  1114. if (res.Ret !== 200) return
  1115. this.sheetSearchList = res.Data.List || [];
  1116. });
  1117. },
  1118. /* 插入表格 */
  1119. async insertSheet({ExcelImage,UniqueCode},elements,addPositions,temp_elements) {
  1120. // 插入沙盘图
  1121. this.imgUrl = ExcelImage;
  1122. this.isChartLoading = true
  1123. const idName = `sheet_${this.currentIndex}_${addPositions[0]}`
  1124. const res = await this.getsheetData(UniqueCode)
  1125. if(!res) {
  1126. this.isChartLoading = false
  1127. return
  1128. }
  1129. //加入表格
  1130. temp_elements = this.addElement(
  1131. elements,
  1132. addPositions,
  1133. "sheet",
  1134. UniqueCode,
  1135. ""
  1136. );
  1137. this.refleshElements(temp_elements);
  1138. this.initSheet(this.$refs[`pptPage_${this.currentIndex}`][0],idName,UniqueCode,'insert')
  1139. this.isChartLoading = false
  1140. this.initPreviewPageEl()
  1141. },
  1142. //打开批量删除弹窗
  1143. openDeletePageDialog(){
  1144. //添加一页才能打开弹窗
  1145. if(this.pageList.length===0){
  1146. this.$message.warning('请至少添加一页!')
  1147. return
  1148. }
  1149. this.deletePageShow = true
  1150. },
  1151. async handleDeletePages(list){
  1152. this.$refs.deletePage.loadingText="删除中..."
  1153. this.$refs.deletePage.dataLoading=true
  1154. this.deletePages(list)
  1155. },
  1156. //批量删除
  1157. deletePages(list){
  1158. let temp = this.pageList.filter((item,index)=>{
  1159. if(!list.includes(index)){
  1160. return item
  1161. }
  1162. })
  1163. this.pageList = temp
  1164. this.currentIndex=0
  1165. if(this.pageList.length>0){
  1166. this.changeCurrentItem(this.pageList[0])
  1167. }else{
  1168. this.currentItem=null
  1169. }
  1170. //删除后,需要重新计算图表数
  1171. let newChartNum = 0
  1172. for(let i=0;i<this.pageList.length;i++){
  1173. const page = this.pageList[i]
  1174. page.elements.forEach(item=>{
  1175. if(item.type==='chart'){
  1176. newChartNum++
  1177. }
  1178. })
  1179. }
  1180. this.chartNum = newChartNum
  1181. this.$nextTick(()=>{
  1182. this.$refs.deletePage.dataLoading = false
  1183. this.deletePageShow = false
  1184. this.$message.success('批量删除成功')
  1185. })
  1186. },
  1187. initPreviewPageEl(type,page){
  1188. const currentItem = page||this.currentItem
  1189. const isLoaded = this.$refs.deletePage.isLoaded
  1190. if(isLoaded){
  1191. type!=='delete'&&this.$refs.deletePage.$refs[`preview_${this.currentIndex}`][0].initPositionInfo()
  1192. this.$refs.deletePage.initPreviewEl(currentItem)
  1193. }
  1194. },
  1195. reloadPage({id,positionInfo}){
  1196. const index = this.pageList.findIndex(i=>i.id===id)
  1197. this.pageList[index].key++
  1198. this.pageList[index].positionInfo = positionInfo
  1199. this.initPageElements(this.pageList[index])
  1200. this.initPreviewPageEl()
  1201. },
  1202. },
  1203. updated(){
  1204. $('.ppt-item').css('height',$('.ppt-item').width()*0.7);
  1205. $('.ppt-item').css('background-image',`url(${this.pptBgImage})`);
  1206. window.onresize = ()=>{
  1207. $('.ppt-item').css('height',$('.ppt-item').width()*0.7);
  1208. }
  1209. },
  1210. destroyed(){
  1211. sessionStorage.removeItem('selectedCatalog')
  1212. window.removeEventListener('message',this.reInitIframe)
  1213. window.onresize=null
  1214. if(this.loopTimer) clearInterval(this.loopTimer)
  1215. }
  1216. };
  1217. </script>
  1218. <style lang="scss">
  1219. @import './css/common.scss';
  1220. @import './css/format.scss';
  1221. .page-wrap{
  1222. .ppt-editor-wrap{
  1223. .ppt-tool{
  1224. .layer-edit-box{
  1225. .tool-list{
  1226. .el-collapse-item__header{
  1227. margin-bottom: 0;
  1228. }
  1229. .el-collapse-item__wrap{
  1230. overflow: visible;
  1231. }
  1232. }
  1233. }
  1234. }
  1235. }
  1236. }
  1237. </style>
  1238. <style scoped lang="scss">
  1239. $titleColor:#333333;
  1240. .page-wrap{
  1241. display: flex;
  1242. width: 100%;
  1243. overflow-x: scroll;
  1244. &::-webkit-scrollbar-track{
  1245. display: none;
  1246. }
  1247. div::-webkit-scrollbar-track{
  1248. display: none;
  1249. }
  1250. .index-wrap{
  1251. min-width: 280px;
  1252. margin-right: 10px;
  1253. padding:10px;
  1254. .cover-wrap{
  1255. cursor: pointer;
  1256. width: 100%;
  1257. margin-bottom: 10px;
  1258. .cover{
  1259. //高:宽 0.75
  1260. height: 195px;
  1261. display: flex;
  1262. justify-content: center;
  1263. align-items: center;
  1264. background-size: 100% 100% !important;
  1265. /* background: no-repeat center/cover url(~@/assets/img/ppt_m/bg3.jpg); */
  1266. }
  1267. }
  1268. .hint-box{
  1269. color:#409EFF;
  1270. display: flex;
  1271. justify-content: flex-end;
  1272. align-items: center;
  1273. margin-bottom:10px;
  1274. }
  1275. .hint{
  1276. border: 1px solid transparent;
  1277. cursor: pointer;
  1278. padding:5px;
  1279. &:hover{
  1280. border: 1px solid #409EFF;
  1281. border-radius: 4px;
  1282. background: #ECF5FF;
  1283. }
  1284. }
  1285. .index-list{
  1286. //margin-top: 10px;
  1287. flex: 1;
  1288. overflow-y: scroll;
  1289. .empty{
  1290. margin-top:120px;
  1291. text-align: center;
  1292. img{
  1293. width:76px;
  1294. height:76px;
  1295. display: inline-block;
  1296. }
  1297. p{
  1298. font-size: 14px;
  1299. color: $titleColor;
  1300. }
  1301. }
  1302. }
  1303. }
  1304. .ppt-editor-wrap{
  1305. flex:1;
  1306. /* min-width: 1370px; */
  1307. padding:20px;
  1308. display: flex;
  1309. .ppt-editor{
  1310. flex:1;
  1311. /* max-width: 980px;
  1312. min-width:776px; */
  1313. max-width: 980px;
  1314. min-width: 980px;
  1315. height:100%;
  1316. /* background-color: pink; */
  1317. background-color: #fff;
  1318. border:2px solid #EBEBEB;
  1319. border-radius: 4px;
  1320. margin-right: 30px;
  1321. overflow-y: scroll;
  1322. position:relative;
  1323. .ppt-editor-item {
  1324. position: relative;
  1325. align-items: center;
  1326. .ppt-item {
  1327. /* width: 835px;
  1328. height: 580px; */
  1329. //padding 两边 40 减掉边框两边 8 max-width:100% - 32px
  1330. width:calc(100% - 64px);
  1331. /* height: 0;
  1332. padding-bottom: calc(69.408% - 64px); */
  1333. background: url('~@/assets/img/ppt_en_bg.png') no-repeat top;
  1334. /* background-size: 898px 642px; */
  1335. background-size:100% 100%;
  1336. /* margin-bottom: 30px; */
  1337. position: relative;
  1338. border: 4px solid transparent;
  1339. cursor: pointer;
  1340. &.choose {
  1341. // box-shadow: 1px 1px 2px rgba($color: #409eff, $alpha: 0.6);
  1342. border: 4px solid rgba($color: #4B8DFF, $alpha: 0.7);
  1343. }
  1344. .close-btn{
  1345. color:#BDBDBD;
  1346. width:20px;
  1347. height:20px;
  1348. position:absolute;
  1349. cursor: pointer;
  1350. top:-10px;
  1351. right:-10px;
  1352. background: url('~@/assets/img/ppt_m/ppt-del.png') no-repeat center/cover;
  1353. background-color: white;
  1354. border-radius: 50%;
  1355. }
  1356. .page-num{
  1357. color:#666666;
  1358. position:absolute;
  1359. bottom:10px;
  1360. right:20px;
  1361. }
  1362. .update-btn{
  1363. position:absolute;
  1364. top:16px;
  1365. left:12px;
  1366. color:#409EFF;
  1367. cursor: pointer;
  1368. display: flex;
  1369. justify-content: center;
  1370. .update-ico{
  1371. width:20px;
  1372. height:20px;
  1373. background:url('~@/assets/img/ppt_m/update-ico.png') no-repeat center/cover;
  1374. margin-right: 4px;
  1375. }
  1376. }
  1377. .loading-cover{
  1378. position: absolute;
  1379. top:20px;
  1380. left:0;
  1381. height:20px;
  1382. background-color: #409EFF;
  1383. }
  1384. }
  1385. }
  1386. }
  1387. .ppt-tool{
  1388. min-width:320px;
  1389. width: 320px;
  1390. height:100%;
  1391. .tool-btn{
  1392. margin-bottom: 12px;
  1393. }
  1394. .addppt-right-box{
  1395. flex: 1;
  1396. height: calc(100% - 182px);
  1397. padding: 0 10px;
  1398. margin-top: 12px;
  1399. box-sizing: border-box;
  1400. border:1px solid #B2B9C3;
  1401. min-width: 320px;
  1402. #tabs {
  1403. padding: 0px 40px;
  1404. box-sizing: border-box;
  1405. margin: 15px 0;
  1406. overflow: hidden;
  1407. > p {
  1408. float: left;
  1409. width: 50%;
  1410. font-size: 18px;
  1411. cursor: pointer;
  1412. color: #1f2e4d;
  1413. padding: 10px 0;
  1414. text-align: center;
  1415. }
  1416. > p.active {
  1417. border-bottom: 2px solid #3464e0;
  1418. color: #3464e0;
  1419. }
  1420. }
  1421. .chart-tool{
  1422. //btn:40+20,wrapPadding:40 -> 120+100
  1423. //height: calc(100vh - 220px);
  1424. flex: 1;
  1425. margin-top: 10px;
  1426. // height:calc(100% - 230px);
  1427. height: calc(100% - 80px);
  1428. .chart-search{
  1429. margin-bottom: 10px;
  1430. }
  1431. .chart-list{
  1432. /* background-color: rgb(82, 106, 106); */
  1433. flex: 1;
  1434. border:2px solid #EBEBEB;
  1435. border-radius: 4px;
  1436. overflow-y: scroll;
  1437. text-align: center;
  1438. .chart-item {
  1439. /* border: 0.5px solid rgba(116, 129, 141,0.1); */
  1440. border-top: 1px solid #EBEBEB;
  1441. cursor: pointer;
  1442. text-align: center;
  1443. color: #74818d;
  1444. font-size: 18px;
  1445. margin-bottom: 10px;
  1446. padding:10px;
  1447. position: relative;
  1448. &:first-child{
  1449. border-top: none;
  1450. }
  1451. .chartEn-mark{
  1452. border-right: 15px solid transparent;
  1453. border-top: 15px solid #409EFF;
  1454. border-bottom: 15px solid transparent;
  1455. border-left: 15px solid #409EFF;
  1456. font-size: 12px;
  1457. }
  1458. img {
  1459. width: 100%;
  1460. }
  1461. }
  1462. .sandTable-item{
  1463. border-bottom: solid 1px #eeeeee;
  1464. padding: 20px 0 10px 0;
  1465. p{
  1466. font-size: 16px;
  1467. color: #3464e0;
  1468. text-align: center;
  1469. }
  1470. img{
  1471. cursor: pointer;
  1472. }
  1473. // .source-identification{
  1474. // color: #000;
  1475. // text-align: left;
  1476. // padding-left: 12px;
  1477. // }
  1478. }
  1479. .loaded-text{
  1480. height: 20px;
  1481. text-align: center;
  1482. color: #666;
  1483. font-size: 14px;
  1484. }
  1485. }
  1486. }
  1487. }
  1488. .layer-edit-box{
  1489. flex: 1;
  1490. height: calc(100% - 182px);
  1491. padding: 0 20px;
  1492. margin-top: 12px;
  1493. box-sizing: border-box;
  1494. border: 2px solid #EBEBEB;
  1495. border-radius: 4px;
  1496. min-width: 320px;
  1497. overflow-y: scroll;
  1498. .tool-list{
  1499. .el-wrap{
  1500. display: flex;
  1501. justify-content: space-between;
  1502. flex-wrap: wrap;
  1503. .el-item{
  1504. border: 1px solid #DCDFE6;
  1505. border-radius: 4px;
  1506. width:55px;
  1507. height:55px;
  1508. margin-bottom: 20px;
  1509. display: flex;
  1510. justify-content: center;
  1511. align-items: center;
  1512. cursor: pointer;
  1513. &:hover,&.active{
  1514. border: 1px solid #409EFF;
  1515. }
  1516. }
  1517. }
  1518. .el-collapse-item__wrap{
  1519. overflow: visible;
  1520. }
  1521. }
  1522. }
  1523. }
  1524. }
  1525. }
  1526. </style>
  1527. <style lang="scss">
  1528. .page-wrap {
  1529. .el-tabs__nav-wrap::after {
  1530. height: 0;
  1531. }
  1532. .el-tabs__item { font-size: 16px; }
  1533. .layer-edit-box{
  1534. .tool-list{
  1535. .el-collapse-item:first-child{
  1536. .el-collapse-item__content{
  1537. padding-bottom: 5px;
  1538. }
  1539. }
  1540. }
  1541. }
  1542. }
  1543. </style>