pptEditor.vue 66 KB

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