xm-attachment-ex.vue 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590
  1. <template>
  2. <div>
  3. <portal
  4. class="sd-attachment-ex-contain"
  5. :to="`sd-attachment-ex${groupId}`"
  6. :disabled="!enablePreview"
  7. >
  8. <div :class="{ [$style.contain]: true, [$style.attachmentextab]: enablePreview }">
  9. <a-spin v-if="init === -1">
  10. <div class="spin-content" />
  11. </a-spin>
  12. <a-spin
  13. v-if="
  14. computedZwList.length &&
  15. enablePreview &&
  16. (computedZwList[0].busy || !computedZwList[0].Completed)
  17. "
  18. size="small"
  19. :class="$style.spin"
  20. />
  21. <div v-if="init === 1" :class="$style.buttons">
  22. <a-button
  23. v-if="!computedZwList.length && _zwUploadable && !formReadOnly"
  24. :type="enablePreview ? 'link' : 'primary'"
  25. icon="plus"
  26. :loading="copyLoading"
  27. @click="
  28. () => {
  29. copyLoading = true
  30. // 如有传了自定义方法,则走自己的
  31. if (customSelectTemplateFun) {
  32. customSelectTemplateFun()
  33. return
  34. }
  35. uploadByTemplateCode().then(() => {
  36. copyLoading = false
  37. getFileList()
  38. })
  39. }
  40. "
  41. >
  42. 模板创建</a-button
  43. >
  44. <a-upload
  45. v-if="!computedZwList.length && _zwUploadable && !formReadOnly"
  46. name="zw"
  47. :accept="acceptZwType"
  48. :multiple="false"
  49. :custom-request="(file) => customRequestZw(file)"
  50. :file-list="[]"
  51. >
  52. <a-button :type="enablePreview ? 'link' : ''" icon="plus" @click="beforeUpload">
  53. 手动上传</a-button
  54. >
  55. </a-upload>
  56. <a-button
  57. v-if="
  58. pluginName === 'WPSDOCX' && !computedZwList.length && _zwUploadable && !formReadOnly
  59. "
  60. :type="enablePreview ? 'link' : ''"
  61. icon="form"
  62. :loading="createZwLoading"
  63. @click="createZw"
  64. >在线新建正文</a-button
  65. >
  66. <UploadFj v-if="uploadfj && !enablePreview && !formReadOnly" :context="_self" />
  67. <PlDownload v-if="uploadfj && !enablePreview" :context="_self" />
  68. <a-tooltip
  69. v-if="computedZwList[0] && enablePreview"
  70. placement="top"
  71. title="点击进入编辑页面"
  72. :overlay-class-name="computedZwList[0].editable ? '' : 'hide'"
  73. >
  74. <a
  75. :class="$style.filename"
  76. :disabled="!computedZwList[0].editable || !!busy[computedZwList[0].FileName]"
  77. @click="OpenMyFile(computedZwList[0].FileName)"
  78. >
  79. <a-spin v-if="!!busy[computedZwList[0].FileName]" size="small" :class="$style.spin" />
  80. {{ computedZwList[0].name }}
  81. </a>
  82. </a-tooltip>
  83. <ActionButtons
  84. v-if="enablePreview && computedZwList[0]"
  85. :context="_self"
  86. :item="computedZwList[0]"
  87. />
  88. <a-modal
  89. v-if="enablePreview && computedZwList[0]"
  90. :visible="computedZwList[0].doReName"
  91. title="重命名"
  92. @ok="UpdateName(computedZwList[0])"
  93. @cancel="ReName(computedZwList[0], false)"
  94. >
  95. <a-input
  96. v-model="newname"
  97. :addon-after="getFileType(computedZwList[0].FileName)"
  98. :max-length="parseInt(filenameMaxLength)"
  99. />
  100. </a-modal>
  101. </div>
  102. <a-button
  103. v-if="!enablePreview && versionVisiable"
  104. icon="sd-desktop"
  105. type="link"
  106. title="本电脑相关信息"
  107. :class="$style.controlinfor"
  108. @click="showVersionInfo"
  109. />
  110. <component
  111. :is="readerPlugin"
  112. v-if="enablePreview && computedZwList.length && computedZwList[0].code"
  113. :key="computedZwList[0].lastModifiedTime"
  114. :class="$style.reader"
  115. :filetype="computedZwList[0].suffix"
  116. :code="computedZwList[0].code"
  117. />
  118. <a-empty v-if="computedZwList.length === 0 && enablePreview" :class="$style.wpsEmpty" />
  119. <div
  120. v-for="{ list, label, id } in fileClasses.filter((item) => !enablePreview)"
  121. :key="id"
  122. class="ant-list ant-list-sm ant-list-split"
  123. >
  124. <div v-if="list.length" :class="$style.divider">{{ label }}</div>
  125. <sd-draggable
  126. v-if="list.length"
  127. tag="ul"
  128. class="ant-list-items"
  129. handle=".handle"
  130. :value="list"
  131. @input="(values) => resort(values, id)"
  132. >
  133. <FileListItem
  134. v-for="item in list"
  135. :key="item.code"
  136. :context="_self"
  137. :item="item"
  138. :busy="busy"
  139. />
  140. </sd-draggable>
  141. </div>
  142. <a-tabs
  143. v-if="
  144. enablePreview && (attachmentsList.find((item) => item.CatNum !== '-1') || _uploadable)
  145. "
  146. size="small"
  147. :class="$style.tab"
  148. @tabClick="showTab = true"
  149. >
  150. <a-tab-pane
  151. v-for="{ list, label, id } in fileClasses.filter(
  152. (item) => item.id !== '_inner' && item.list.length
  153. )"
  154. v-show="showTab || !computedZwList.length"
  155. :key="id"
  156. :tab="`${label}(${list.length})`"
  157. >
  158. <sd-draggable
  159. tag="ul"
  160. class="ant-list-items ant-list-sm"
  161. handle=".handle"
  162. :value="list"
  163. @input="(values) => resort(values, id)"
  164. >
  165. <FileListItem
  166. v-for="item in list"
  167. :key="item.code"
  168. :context="_self"
  169. :item="item"
  170. :busy="busy"
  171. />
  172. </sd-draggable>
  173. </a-tab-pane>
  174. <a-button
  175. v-if="versionVisiable"
  176. slot="tabBarExtraContent"
  177. icon="sd-desktop"
  178. type="link"
  179. title="本电脑相关信息"
  180. :class="$style.controlinfo"
  181. @click="showVersionInfo"
  182. />
  183. <UploadFj v-show="!formReadOnly" slot="tabBarExtraContent" :context="_self" />
  184. <PlDownload slot="tabBarExtraContent" :context="_self" />
  185. <a-button
  186. v-if="computedZwList.length"
  187. slot="tabBarExtraContent"
  188. type="link"
  189. :title="showTab ? '折叠附件列表' : '打开附件列表'"
  190. :icon="showTab ? 'up' : 'down'"
  191. @click="showTab = !showTab"
  192. />
  193. </a-tabs>
  194. </div>
  195. </portal>
  196. <SdAttachmentDisplay :enable-preview="enablePreview" :value="value" />
  197. </div>
  198. </template>
  199. <script>
  200. import cubeServices from '@product/iam/cube/cube-services'
  201. import loginService from '@/login/login-service'
  202. import storeMixin from '@/common/store-mixin'
  203. import axios from '@/common/services/axios-instance'
  204. import { Modal, Message } from 'ant-design-vue'
  205. import attrAccept from 'ant-design-vue/es/vc-upload/src/attr-accept'
  206. import asyncComponent from '@/common/services/async-component'
  207. import systemManage from '@/system-manage/system-manage'
  208. import Vue from 'vue'
  209. import pickValues from '@/common/services/pick-values'
  210. import download from '@/common/services/download'
  211. import sdAttachmentVue from '@/common/components/sd-attachment.vue'
  212. import sdListPickerVue from '@/common/components/sd-list-picker.vue'
  213. import SdAttachmentDisplay from '@/common/components/sd-form/sd-attachment-display.vue'
  214. import { sdLocalStorage } from '@/common/services/storage-service'
  215. import components from './_import-components/xm-attachment-ex-import'
  216. window.fnGetFileLink = (code) => {
  217. return `${location.origin}${location.pathname}api/framework/v1/task-form-process/download-attachments/${code}`
  218. }
  219. const ActionButtons = {
  220. props: ['context', 'item'],
  221. render() {
  222. return (
  223. <div class={['ant-list-item-action', this.context.$style.actionbutton]}>
  224. {this.context.$scopedSlots.actions?.({
  225. item: this.item,
  226. waitUntil: (promise) => this.context.waitUntil(this.item, promise),
  227. })}
  228. {this.item.Completed && this.context.intelligentReview ? (
  229. <a-tooltip placement='top' title='智能核稿'>
  230. <a-button
  231. icon='sd-eye'
  232. type='link'
  233. vOn:click={() => this.context.reviewFile(this.item)}
  234. />
  235. </a-tooltip>
  236. ) : (
  237. ''
  238. )}
  239. {this.item.Completed && this.item.replacable && !this.context.formReadOnly ? (
  240. <a-tooltip placement='top' title='替换'>
  241. <a-upload
  242. class={this.context.$style.replacefj}
  243. accept={this.item.CatNum === '-1' ? this.context.acceptZwType : this.context.accept}
  244. custom-request={(file) => this.context.customRequestZw(file, this.item)}
  245. show-upload-list={false}
  246. >
  247. <a-button icon='retweet' type='link' />
  248. </a-upload>
  249. </a-tooltip>
  250. ) : (
  251. ''
  252. )}
  253. {this.context.onlineWordZw(this.item) &&
  254. this.context.cleanCopy &&
  255. !this.context.formReadOnly ? (
  256. <a-tooltip placement='top' title='清稿'>
  257. <a-button
  258. icon='sd-eraser'
  259. type='link'
  260. vOn:click={() => this.context.QingGaoFile(this.item.FileName)}
  261. />
  262. </a-tooltip>
  263. ) : (
  264. ''
  265. )}
  266. {this.context.onlineWordZw(this.item) &&
  267. this.context.redHeader &&
  268. !this.context.formReadOnly ? (
  269. <a-tooltip placement='top' title='套打'>
  270. <a-button
  271. icon='sd-tile'
  272. type='link'
  273. vOn:click={() => this.context.TaoDaFile(this.item.FileName)}
  274. />
  275. </a-tooltip>
  276. ) : (
  277. ''
  278. )}
  279. {this.context.onlineWordZw(this.item) &&
  280. this.context.putSealOn &&
  281. !this.context.formReadOnly ? (
  282. <a-tooltip placement='top' title='盖章'>
  283. <a-button
  284. icon='sd-stamp'
  285. type='link'
  286. vOn:click={() => this.context.GaiZhangFile(this.item.FileName)}
  287. />
  288. </a-tooltip>
  289. ) : (
  290. ''
  291. )}
  292. {!this.sdPlatform.isXC &&
  293. this.context.onlineWordZw(this.item) &&
  294. this.context.changeFileFormat &&
  295. !this.context.formReadOnly ? (
  296. <a-tooltip placement='top' title='转pdf'>
  297. <a-button
  298. icon='file-pdf'
  299. type='link'
  300. vOn:click={() => this.context.ConvertPDFFile(this.item.FileName)}
  301. />
  302. </a-tooltip>
  303. ) : (
  304. ''
  305. )}
  306. {this.sdPlatform.isXC &&
  307. this.context.onlineWordZw(this.item) &&
  308. this.context.changeFileFormat &&
  309. !this.context.formReadOnly ? (
  310. <a-tooltip placement='top' title='转ofd'>
  311. <a-button
  312. icon='file-pdf'
  313. type='link'
  314. vOn:click={() => this.context.ConvertOFDFile(this.item.FileName)}
  315. />
  316. </a-tooltip>
  317. ) : (
  318. ''
  319. )}
  320. {this.item.Completed && this.item.deletable && !this.context.formReadOnly ? (
  321. <a-tooltip placement='top' title='删除'>
  322. <a-button
  323. icon='sd-trash2'
  324. type='link'
  325. vOn:click={() => this.context.DelFile(this.item)}
  326. />
  327. </a-tooltip>
  328. ) : (
  329. ''
  330. )}
  331. {this.item.Completed && this.item.renamable && !this.context.formReadOnly ? (
  332. <a-tooltip placement='top' title='重命名'>
  333. <a-button
  334. icon='sd-edit'
  335. type='link'
  336. vOn:click={() => this.context.ReName(this.item, true)}
  337. />
  338. </a-tooltip>
  339. ) : (
  340. ''
  341. )}
  342. {this.context.webflow.FlowData &&
  343. this.context.webflow.FlowData.attrs['we-flow$attachmentRevert'] &&
  344. this.item.CatNum === '-4' &&
  345. !this.context.formReadOnly ? (
  346. <a-tooltip placement='top' title='还原正文'>
  347. <a-button
  348. icon='rollback'
  349. type='link'
  350. vOn:click={() => this.context.revertZw(this.item.FileName)}
  351. />
  352. </a-tooltip>
  353. ) : (
  354. ''
  355. )}
  356. {this.item.Completed ? (
  357. <a-tooltip placement='top' title='下载'>
  358. <a-button
  359. icon='sd-download'
  360. type='link'
  361. vOn:click={() => this.context.SaveFile(this.item.FileName)}
  362. />
  363. </a-tooltip>
  364. ) : (
  365. ''
  366. )}
  367. {this.item.CatNum !== '-1' && this.context._sortable && !this.context.formReadOnly ? (
  368. <a-tooltip placement='right' title='拖拽排序'>
  369. <a class='handle ant-btn ant-btn-link ant-btn-icon-only' title={null}>
  370. <a-icon type='sd-swap' />
  371. </a>
  372. </a-tooltip>
  373. ) : (
  374. ''
  375. )}
  376. </div>
  377. )
  378. },
  379. }
  380. /**
  381. * 集成了indidoc和wps控件的组件
  382. * @displayName SdAttachmentEx 控件附件
  383. */
  384. export default {
  385. name: 'XmAttachmentEx',
  386. components: {
  387. SdAttachmentDisplay,
  388. ...components,
  389. ActionButtons,
  390. UploadFj: {
  391. props: ['context'],
  392. render(h) {
  393. if (this.context.attachmentsList.length < this.context.max && this.context._uploadable) {
  394. const createFj =
  395. this.context.pluginName === 'WPSDOCX' ? (
  396. <a-button
  397. type={this.context.enablePreview ? 'link' : ''}
  398. icon='form'
  399. loading={this.context.createFjLoading}
  400. on={{
  401. click: this.context.createFj,
  402. }}
  403. >
  404. 在线新建附件
  405. </a-button>
  406. ) : (
  407. ''
  408. )
  409. return (
  410. <div class={this.context.$style.multiButton}>
  411. <a-upload
  412. multiple={this.context.max > 1}
  413. custom-request={(file) => this.context.customRequest(file, 0)}
  414. file-list={[]}
  415. accept={this.context.accept}
  416. >
  417. <a-button type={this.context.enablePreview ? 'link' : ''}>
  418. <a-icon type='paper-clip' />
  419. 上传附件
  420. </a-button>
  421. </a-upload>
  422. {createFj}
  423. </div>
  424. )
  425. }
  426. },
  427. },
  428. PlDownload: {
  429. props: ['context'],
  430. render(h) {
  431. if (this.context.attachmentsList.length > 1) {
  432. return (
  433. <a-button
  434. type={this.context.enablePreview ? 'link' : ''}
  435. vOn:click={this.context.plsave}
  436. >
  437. <a-icon type='download' />
  438. 批量下载
  439. </a-button>
  440. )
  441. }
  442. },
  443. },
  444. FileListItem: {
  445. props: ['context', 'item', 'busy'],
  446. components: {
  447. ActionButtons,
  448. },
  449. render() {
  450. return (
  451. <a-list-item class={[this.context.$style.list, this.context.$style.actionlist]}>
  452. {this.busy[this.item.FileName] ? (
  453. <a-spin size='small' class={this.context.$style.spin} />
  454. ) : (
  455. ''
  456. )}
  457. <span
  458. class={this.context.$style.label}
  459. vOn:click={() => this.context.OpenMyFile(this.item.FileName)}
  460. >
  461. {this.context.$scopedSlots['file-icon']?.({
  462. item: this.item,
  463. }) || (
  464. <a-icon
  465. theme='filled'
  466. type={this.context.getIcon(this.item.FileName)}
  467. class={this.context.$style[this.context.getIcon(this.item.FileName)]}
  468. />
  469. )}
  470. {this.item.FileName} ({Vue.filter('sdBytesFormat')(this.item.size)})
  471. {!this.item.Completed && !['-3', '-4'].includes(this.item.CatNum) ? (
  472. <a-progress percent={90} show-info={false} size='small' status='active' />
  473. ) : (
  474. ''
  475. )}
  476. </span>
  477. <div class={[this.context.$style.rename, this.item.doReName ? '' : 'hide']}>
  478. <a-input
  479. vModel={this.context.newname}
  480. addon-after={this.context.getFileType(this.item.FileName)}
  481. max-length={parseInt(this.context.filenameMaxLength)}
  482. />
  483. <a-button vOn:click={() => this.context.ReName(this.item, false)}>取消</a-button>
  484. <a-button type='primary' vOn:click={() => this.context.UpdateName(this.item)}>
  485. 确定
  486. </a-button>
  487. </div>
  488. <ActionButtons context={this.context} item={this.item} />
  489. </a-list-item>
  490. )
  491. },
  492. },
  493. },
  494. mixins: [storeMixin, sdAttachmentVue],
  495. props: {
  496. /**
  497. * 获取套打的公文域
  498. * 返回一个Promise.reject 或 throw new Error可中断套打
  499. */
  500. getTaodaValue: {
  501. type: Function,
  502. default: () => {},
  503. },
  504. /**
  505. * 获取套打的模板
  506. */
  507. getTemplateUrl: {
  508. type: Function,
  509. default: () => {},
  510. },
  511. /**
  512. * 获取印章
  513. */
  514. getSealUrl: {
  515. type: Function,
  516. default: () => {},
  517. },
  518. /**
  519. * @ignore
  520. */
  521. listType: {
  522. type: String,
  523. default: undefined,
  524. },
  525. /**
  526. * 是否允许清稿
  527. */
  528. cleanCopy: {
  529. type: [Boolean, String],
  530. default: false,
  531. },
  532. /**
  533. * 是否允许套打
  534. */
  535. redHeader: {
  536. type: [Boolean, String],
  537. default: false,
  538. },
  539. /**
  540. * 是否允许盖章
  541. */
  542. putSealOn: {
  543. type: [Boolean, String],
  544. default: false,
  545. },
  546. /**
  547. * 是否允许转版(pdf 或 ofd)
  548. */
  549. changeFileFormat: {
  550. type: [Boolean, String],
  551. default: false,
  552. },
  553. /**
  554. * 接收的正文类型
  555. */
  556. acceptZwType: {
  557. type: String,
  558. default: '.doc,.docx,.wps',
  559. },
  560. /**
  561. * 是否留痕
  562. *
  563. */
  564. trackChanges: {
  565. type: [Boolean, String],
  566. default: true,
  567. },
  568. /**
  569. * 启用正文一屏预览功能,前提是部署了wps预览服务,并且表单也启用了enablePreview,该参数生效
  570. * @since 8.0.10
  571. */
  572. enableAttachmentExTab: {
  573. type: Boolean,
  574. default: true,
  575. },
  576. /**
  577. * 通过控件打开附件时设置水印,第一个参数当前附件属性,第二个属性是当前的操作
  578. * EditFile表示打开文件操作,QingGao清稿,UpdateRegion套打(只更新域),TaoDa套打,GaiZhang盖章
  579. * @since 8.0.11
  580. */
  581. waterMark: {
  582. type: Function,
  583. default: systemManage.getAttachmentWaterMark,
  584. },
  585. /**
  586. * 指定使用的插件,默认预览服务启用WPS在线编辑时是wps中台,否则x86是indidoc,信创环境是wps加载项,可强制在x86环境指定使用wps加载项,不支持在信创环境使用indidoc
  587. * 参数值只可以设置 INDIDOCX 或者 WPS 或者 WPSDOCX 或者不传
  588. * @since 8.0.17
  589. */
  590. pluginName: {
  591. type: String,
  592. default: () => {
  593. const isXC = document.body.classList.contains('platform-xc')
  594. const previewCfg = JSON.parse(sdLocalStorage.getItem('commonConfig') || '{}').oaDocPreview
  595. let fdService, fdMode, isConfig
  596. if (isXC) {
  597. fdService = 'xcPreviewServiceType'
  598. fdMode = 'xcPreviewMode'
  599. isConfig = previewCfg.environment?.includes('XC')
  600. } else {
  601. fdService = 'previewServiceType'
  602. fdMode = 'previewMode'
  603. isConfig = previewCfg.environment?.includes('X86')
  604. }
  605. isConfig = isConfig && previewCfg[fdMode] === 'EMBED_SINGLE_SCREEN'
  606. // 启用了预览服务 且 预览服务选择了WPS在线编辑时,控件使用wps中台(WPSDOCX),否则 如是信创环境使用wps,否则使用indidocx
  607. return isConfig && previewCfg[fdService] === 'WPS_ONLINE_EDIT'
  608. ? 'WPSDOCX'
  609. : isXC
  610. ? 'WPS'
  611. : 'INDIDOCX'
  612. },
  613. validator: function(value) {
  614. if (['INDIDOCX', 'WPS', 'WPSDOCX'].includes(value)) {
  615. return true
  616. } else {
  617. // eslint-disable-next-line no-console
  618. console.error('插件名必须是INDIDOCX或WPS或WPSDOCX')
  619. return false
  620. }
  621. },
  622. },
  623. /**
  624. * 是否显示修订痕迹和批注等,true显示,false不显示
  625. * @since 0.17
  626. */
  627. showMark: {
  628. type: Boolean,
  629. default: true,
  630. },
  631. /**
  632. * 指定附件到某个分类
  633. * @since 0.20
  634. */
  635. getCategory: {
  636. type: Function,
  637. default: () => {},
  638. },
  639. /**
  640. * 扩展附件分类
  641. * @since 0.20
  642. */
  643. categories: {
  644. type: Array,
  645. default: () => [],
  646. },
  647. /**
  648. * 不支持的SdAttachment的prop,不显示在帮助文档中
  649. * @ignore
  650. */
  651. downloadBatch: {
  652. type: Boolean,
  653. default: false,
  654. },
  655. temtype: {
  656. type: String,
  657. default: '',
  658. },
  659. temdataurl: {
  660. type: String,
  661. default: '',
  662. },
  663. /**
  664. * 是否打开正文
  665. */
  666. isOpenFile: {
  667. type: Boolean,
  668. default: true,
  669. },
  670. /**
  671. * 自定义选择模板
  672. */
  673. customSelectTemplateFun: {
  674. type: Function,
  675. },
  676. // 显示上传附件按钮
  677. uploadfj: {
  678. type: Boolean,
  679. default: true,
  680. },
  681. },
  682. data() {
  683. return {
  684. slCtl: {},
  685. attachments: [],
  686. init: -1, // -1加载中 0 失败 1 成功
  687. newname: '',
  688. visible: false,
  689. showTab: false,
  690. sdLocalStorage: sdLocalStorage,
  691. readerPlugin: '',
  692. pluginInstalled: -1,
  693. copyLoading: false,
  694. previewConfig: JSON.parse(sdLocalStorage.getItem('commonConfig') || '{}').oaDocPreview,
  695. defaultCategory: [
  696. {
  697. label: '正文',
  698. order: 100,
  699. id: '_inner',
  700. },
  701. {
  702. label: '附件',
  703. order: 200,
  704. id: 'fj',
  705. },
  706. {
  707. label: '痕迹',
  708. order: 300,
  709. id: 'hj',
  710. },
  711. ],
  712. busy: {},
  713. createZwLoading: false,
  714. createFjLoading: false,
  715. intelligentReview: this.webflow?.FlowData.attrs.audit$intelligentReview,
  716. }
  717. },
  718. inject: {
  719. attachValidate: { default: undefined },
  720. updateActivedAtt: { default: undefined },
  721. webflow: { default: () => ({}) },
  722. },
  723. computed: {
  724. fileClasses: {
  725. // 对文件分类
  726. get: function() {
  727. const classes = [...this.defaultCategory, ...this.categories].sort(
  728. (a, b) => a.order - b.order
  729. )
  730. classes.map((item) => {
  731. item.list = this.attachmentsList.filter(
  732. (file) => (this.getCategory(file) || this.getDefaultCategory(file)) === item.id
  733. )
  734. })
  735. return classes
  736. },
  737. },
  738. enablePreview() {
  739. let isConfig = false
  740. if (this.previewConfig) {
  741. if (!this.sdPlatform.isXC) {
  742. isConfig =
  743. this.previewConfig.environment?.includes('X86') &&
  744. this.previewConfig.previewMode === 'EMBED_SINGLE_SCREEN'
  745. } else {
  746. isConfig =
  747. this.previewConfig.environment?.includes('XC') &&
  748. this.previewConfig.xcPreviewMode === 'EMBED_SINGLE_SCREEN'
  749. }
  750. }
  751. return !!(
  752. isConfig &&
  753. this.webflow.FlowData &&
  754. (this.webflow.FlowData?.attrs.stepN !== 0 || this.webflow.FlowData?.attrs.isSubflow) &&
  755. this.enableAttachmentExTab
  756. )
  757. },
  758. versionVisiable() {
  759. // 有查看版本信息的api 且 (能上传正文 或 能上传附件 或 有附件)
  760. return (
  761. !!this.slCtl.Content?.Control?._getTerminalInfo &&
  762. (this._zwUploadable ||
  763. this._uploadable ||
  764. this.fileClasses.filter((item) => item.list.length > 0).length > 0)
  765. )
  766. },
  767. },
  768. watch: {
  769. 'computedZwList.0.code': function() {
  770. // 只有一屏预览才会使用这里的控件
  771. if (
  772. ['.pdf', '.ofd'].includes(this.computedZwList?.[0]?.Type) &&
  773. this.sdPlatform.isXC &&
  774. this.previewConfig?.standardPreviewMode === 'NPAPI'
  775. ) {
  776. // pdf ofd用树科
  777. this.readerPlugin = asyncComponent(() =>
  778. import(
  779. /* webpackChunkName: "sd-suwell-reader" */ '@/common/components/sd-suwell-reader.vue'
  780. )
  781. )
  782. } else {
  783. // word用wps查看组件,支持napi或请阅读的方式
  784. this.readerPlugin = asyncComponent(() =>
  785. import(/* webpackChunkName: "sd-wps-reader" */ '@/common/components/sd-wps-reader.vue')
  786. )
  787. }
  788. },
  789. },
  790. created() {
  791. if (this.enablePreview) {
  792. this.inTab = this.webflow?.updateAttachmentExTabs?.(this.groupId, true)
  793. }
  794. if (this.sdPlatform.isXC && this.pluginName === 'INDIDOCX') {
  795. // eslint-disable-next-line no-console
  796. console.error('当前环境是信创环境,不能使用INDIDOCX插件')
  797. }
  798. },
  799. destroyed() {
  800. if (this.inTab) {
  801. this.webflow?.updateAttachmentExTabs?.(this.groupId, false)
  802. }
  803. },
  804. methods: {
  805. // 智能核稿功能
  806. reviewFile(item) {
  807. // 检查是否开启参数,或token是否过期
  808. cubeServices.checkCubeToken('sensitive').then((res) => {
  809. if (!res) {
  810. Modal.warning({
  811. title: '提示',
  812. content: '未开启CUBE集成功能参数或单点认证失败,请联系管理员',
  813. })
  814. return false
  815. }
  816. // 更新token,防止过期
  817. loginService.updateAccessToken().then(() => {
  818. // 开启了参数,且token正常
  819. const cubeJson = JSON.parse(sdLocalStorage.getItem('cube') || '{}')
  820. // 项目修改
  821. axios.get('api/xcoa-mobile/v1/iamcubecontract/getCubeWebUrl').then((res) => {
  822. if (res.data !== '') {
  823. let url =
  824. res.data +
  825. `/projects/8/xc-sensitive?file_path=${
  826. location.origin
  827. }/api/framework/v1/task-form-process/download-attachments/${
  828. item.code
  829. }?access_token=${loginService.getTokens().access_token}`
  830. url += `&access_token=${cubeJson.sensitive_token}`
  831. url += `&file_id=${item.code}`
  832. url += `&file_format=${item.suffix}`
  833. window.open(url)
  834. }
  835. })
  836. })
  837. })
  838. },
  839. resort(values, id) {
  840. let l = []
  841. this.fileClasses.forEach((item) => {
  842. l = l.concat(item.id === id ? values : item.list)
  843. })
  844. this.attachments = [
  845. ...l,
  846. ...this.attachments.filter((item) => {
  847. return !this.attachmentsList.find((att) => att.code === item.code)
  848. }),
  849. ]
  850. this.saveFilesSort()
  851. },
  852. getDefaultCategory(item) {
  853. if (item.CatNum + '' === '-1') {
  854. return '_inner'
  855. }
  856. if (['0', '-5', '-6'].includes(item.CatNum + '')) {
  857. return 'fj'
  858. }
  859. if (['-3', '-4'].includes(item.CatNum + '')) {
  860. return 'hj'
  861. }
  862. },
  863. waitUntil(item, promise) {
  864. this.markBusy(item.name)
  865. promise.then(() => {
  866. this.markBusy(item.name, false)
  867. })
  868. },
  869. onlineWordZw(item) {
  870. // 非版式文件的在线正文
  871. return (
  872. item.Completed &&
  873. item.CatNum === '-1' &&
  874. !item.FileName.endsWith('ofd') &&
  875. !item.FileName.endsWith('pdf')
  876. )
  877. },
  878. customRequestZw(data, item) {
  879. if (!item || item.CatNum === '-1') {
  880. if (!attrAccept(data.file, this.acceptZwType)) {
  881. Message.warn('不支持的文件类型,支持的文件类型有' + this.acceptZwType)
  882. return
  883. }
  884. }
  885. this.customRequest(data, item?.CatNum || -1, item || '')
  886. },
  887. pluginInfo() {
  888. const lock = sdLocalStorage.getItem(`Lock${this.webflow?.FlowData?.instId}`)
  889. const lockId = lock && JSON.parse(lock).sessionId
  890. let lockInfo = ''
  891. if (lockId) {
  892. lockInfo = `|lockId=${lockId}`
  893. }
  894. const UploadParam = Object.keys(this.infoProperties).map((key) => {
  895. return `${key}=${this.infoProperties[key] || ''}`
  896. })
  897. return {
  898. UserName: this.userInfo.name,
  899. UploadParam: `${UploadParam.join('|')}|creatorId=${
  900. this.userInfo.account
  901. }${lockInfo}|groupId=${this.groupId}`,
  902. UploadUrl: `${location.origin}${location.pathname}api/xcoa-mobile/v1/iam-attachment-extend/attachments-upload/indi`,
  903. ServerUpdateUrl: `${location.origin}${location.pathname}resource/update/{0}`,
  904. idxV6version: '',
  905. sid: 'test',
  906. refresh_token: `${location.origin}${location.pathname}api/refresh|${
  907. loginService.getTokens().refresh_token
  908. }`,
  909. getActivationUrl: `${location.origin}${location.pathname}api/framework/v1/dict-manager/dictvalues?keyId=INDIDOCX`,
  910. WPSPluginUrl: `${location.origin}${location.pathname}resource/jsplugindir/`,
  911. }
  912. },
  913. loadPlugin(name) {
  914. const data = this.initValue || this.getFileListFromField?.() || []
  915. data.forEach((item) => {
  916. item.FileName = item.name
  917. item.Completed = 1
  918. item.CatNum = item.catNum?.toString()
  919. })
  920. this.attachments = data
  921. this.init = 1
  922. let plugin
  923. switch (this.pluginName) {
  924. case 'WPSDOCX':
  925. plugin = import(/* webpackChunkName: "wpsdocc" */ '../../../lib-external/wpsDocX.js')
  926. break
  927. case 'WPS':
  928. plugin = import(/* webpackChunkName: "wps" */ '../../../lib-external/wps.js')
  929. break
  930. case 'INDIDOCX':
  931. plugin = import(/* webpackChunkName: "indidocx" */ '../../../lib-external/IndiDocX.js')
  932. break
  933. default:
  934. break
  935. }
  936. plugin.then((pluginName) => {
  937. this.slCtl = pluginName.default.createInstance().slCtl
  938. const template = (
  939. <div>
  940. <p>
  941. 第一次使用请
  942. {!this.sdPlatform.isXC && this.pluginName !== 'WPS' ? (
  943. <a href='resource/IndiDocX8.0.exe' download='IndiDocX8.0.exe'>
  944. 安装indidocx控件
  945. </a>
  946. ) : (
  947. <a href='resource/jsplugindir/publish.html' target='_blank'>
  948. 安装wps控件
  949. </a>
  950. )}
  951. </p>
  952. <p>
  953. 已安装控件,请尝试
  954. <a
  955. vOn:click_stop_prevent={() => {
  956. this.initPlugin(true).then((val) => {
  957. Modal.destroyAll()
  958. Message.info({ content: `重新加载控件${val ? '成功' : '失败'}` })
  959. })
  960. }}
  961. >
  962. 重新加载控件
  963. </a>
  964. </p>
  965. </div>
  966. )
  967. const funs = [
  968. 'EditFile',
  969. 'QingGao',
  970. 'UpdateRegion',
  971. 'TaoDa',
  972. 'GaiZhang',
  973. 'DOCConvertToOFD',
  974. 'SaveFileToLocal',
  975. 'SaveMultiFiles',
  976. 'DOCConvertToPDF',
  977. ]
  978. funs.forEach((fun) => {
  979. const oldfun = this.slCtl.Content.Control[fun]
  980. if (oldfun) {
  981. const that = this
  982. this.slCtl.Content.Control[fun] = function() {
  983. if (that.pluginInstalled !== 1) {
  984. Modal.warning({
  985. title: '提示',
  986. content:
  987. that.pluginInstalled === 0
  988. ? this.pluginName === 'WPSDOCX'
  989. ? 'WPS中台初始化失败'
  990. : template
  991. : '控件正在加载中,请稍后重试',
  992. })
  993. that.attachments.forEach((item) => (item.busy = false)) // 不要busy状态
  994. that.attachments = [...that.attachments]
  995. return Promise.reject(new Error('未检测到控件'))
  996. }
  997. return oldfun.apply(this, arguments)
  998. }
  999. }
  1000. })
  1001. this.getFileList(true, true, true).then(() => {
  1002. this.init = 1
  1003. })
  1004. this.initPlugin()
  1005. })
  1006. },
  1007. initPlugin(flag = false) {
  1008. return this.slCtl
  1009. .checkInstall(this.pluginInfo(), flag)
  1010. .then((val) => {
  1011. if (val) {
  1012. this.pluginInstalled = 1
  1013. } else {
  1014. this.pluginInstalled = 0
  1015. }
  1016. return this.pluginInstalled
  1017. })
  1018. .catch(() => {
  1019. // eslint-disable-next-line no-console
  1020. console.log(this.pluginName + '检测' + this.pluginInstalled)
  1021. this.pluginInstalled = 0
  1022. return this.pluginInstalled
  1023. })
  1024. },
  1025. getFileList(noTriggerCheck = false, updateList = true, fromField = false) {
  1026. let p = Promise.resolve(this.initValue)
  1027. if (!fromField || !Array.isArray(this.initValue)) {
  1028. p = axios
  1029. .get(`api/framework/v1/attachment-extend/attachments-info-perm/${this.groupId}`, {
  1030. params: this.infoProperties,
  1031. })
  1032. .then((res) => res.data)
  1033. }
  1034. return p.then((data) => {
  1035. // 简单做下排序处理,正文 附件 痕迹文件等
  1036. data.forEach((item) => {
  1037. item.properties =
  1038. item.properties + '<completed>1</completed><file_unid>' + item.code + '</file_unid>'
  1039. if (
  1040. item.properties.includes('<RedTemplate>1</RedTemplate>') ||
  1041. item.properties.includes('<FromTemplate>1</FromTemplate>')
  1042. ) {
  1043. // 用filetype标记模板生成的红头文件
  1044. item.properties = item.properties.replace(
  1045. /<filetype>.*<\/filetype>/,
  1046. '<filetype>RedTemplate</filetype>'
  1047. )
  1048. }
  1049. })
  1050. const zw = data.filter((item) => item.properties.includes('<CatNum>-1</CatNum>'))
  1051. const fj = data.filter((item) => item.properties.includes('<CatNum>0</CatNum>'))
  1052. const others = data.filter((item) => !item.properties.match(/<CatNum>(-1|0)<\/CatNum>/))
  1053. data = [...zw, ...fj, ...others]
  1054. this.slCtl.setFileList(data)
  1055. const atts = JSON.parse(JSON.stringify(this.slCtl.Content.Files.FileList))
  1056. atts.forEach((item) => {
  1057. item.code = item.Unid
  1058. item.name = item.FileName
  1059. const file = data.find((i) => item.code === i.code) // 接口返回的附件列表,生成控件要的列表,带上原附件列表的一些属性
  1060. item.querystring = file.properties
  1061. // 兼容没有权限设置的情况,比如非流程表单
  1062. item.deletable = !this.readOnly
  1063. item.editable = !this.readOnly
  1064. item.renamable = !this.readOnly
  1065. item.replacable = !this.readOnly
  1066. if (file.attr) {
  1067. item.deletable = file.attr?.deletable
  1068. item.editable = file.attr?.editable
  1069. item.renamable = file.attr?.renamable
  1070. item.replacable = file.attr?.replacable
  1071. }
  1072. Object.assign(item, file)
  1073. })
  1074. if (updateList) {
  1075. // 如果是上传附件不直接拉列表更新,可能还有没上传玩的附件
  1076. this.attachments = atts
  1077. // 红头模板生成的正文给个标记
  1078. const redtemplatecode = data.find((item) =>
  1079. item.properties.includes('<RedTemplate>1</RedTemplate>')
  1080. )?.code
  1081. if (this.attachments.find((item) => item.Unid === redtemplatecode))
  1082. this.attachments.find((item) => item.Unid === redtemplatecode).RedTemplate = 1
  1083. this.$emit('input', this.attachments)
  1084. } else {
  1085. return atts
  1086. }
  1087. if (!noTriggerCheck && updateList) {
  1088. // 不是第一次加载不触发校验
  1089. this.filesChanged()
  1090. }
  1091. })
  1092. },
  1093. filesChanged() {
  1094. this.$emit('change', this.attachments)
  1095. this.attachValidate?.()
  1096. },
  1097. beforeUpload(event) {
  1098. const zw = this.attachments.find((item) => item.CatNum + '' === '-1')
  1099. if (zw) {
  1100. Message.warn(`已存在正文《${zw.FileName}》`)
  1101. event.preventDefault()
  1102. event.stopPropagation()
  1103. }
  1104. },
  1105. OpenMyFile(fname) {
  1106. loginService.updateAccessToken().then(() => {
  1107. // 因为打开ofd,确保每次打开accesstoken都是有效的
  1108. const item = this.attachmentsList.find((item) => item.name === fname)
  1109. if ((fname.endsWith('ofd') || fname.endsWith('pdf')) && this.sdPlatform.isXC) {
  1110. window.open(
  1111. `${this.$router.resolve('/sd-ofd-view').href}?url=${
  1112. item.Link
  1113. }&readonly=${!item.editable}&querystring=${item.querystring}&groupId=${
  1114. this.groupId
  1115. }&putSealOn=${this.putSealOn}`
  1116. )
  1117. return
  1118. }
  1119. this.markBusy(fname)
  1120. this.updateActivedAtt?.(item.code, !!item.editable)
  1121. this.slCtl.Content.Control.EditFile(
  1122. fname,
  1123. {
  1124. Status: !item.editable ? 2 : this.trackChanges ? 0 : -1,
  1125. AllowPrint: 'true',
  1126. WaterMark: this.waterMark(item, 'EditFile'),
  1127. UseMark: this.showMark.toString(),
  1128. },
  1129. this.pluginInfo()
  1130. )
  1131. .catch((e) => {
  1132. if (e === 'FormatNotSupport') {
  1133. // 不支持的文件,图片直接的打开,其他下载
  1134. if (['jpg', 'png', 'jpeg', 'bmp', 'gif'].includes(item.suffix)) {
  1135. axios({
  1136. url: `api/framework/v1/task-form-process/download-attachments/` + item.code,
  1137. responseType: 'blob',
  1138. }).then((res) => {
  1139. window.open(URL.createObjectURL(res.data))
  1140. })
  1141. } else {
  1142. this.SaveFile(fname)
  1143. }
  1144. }
  1145. })
  1146. .finally(() => {
  1147. this.getFileList(false, false).then((atts) => {
  1148. const index = this.attachments.findIndex((item) => item.catNum === '-1')
  1149. if (index !== -1) {
  1150. this.attachments[index] = atts.find((item) => item.catNum === '-1')
  1151. this.attachments = [...this.attachments]
  1152. }
  1153. })
  1154. this.markBusy(fname, false)
  1155. this.updateActivedAtt?.(item.code, false)
  1156. })
  1157. })
  1158. },
  1159. QingGaoFile(fname, mode) {
  1160. const item = this.attachmentsList.find((item) => item.name === fname)
  1161. loginService.updateRefreshToken().then(() => {
  1162. this.markBusy(fname)
  1163. this.slCtl.Content.Control.QingGao(
  1164. fname,
  1165. { Status: '-1', DeleteNotation: 'true', WaterMark: this.waterMark(item, 'QingGao') },
  1166. this.pluginInfo()
  1167. )?.then(() => {
  1168. this.markBusy(fname, false)
  1169. this.getFileList()
  1170. })
  1171. })
  1172. },
  1173. TaoDaFile(fname) {
  1174. const fnShowTaoDa = (fname) => {
  1175. // 套打过走更新逻辑
  1176. this.attachments = [...this.attachments]
  1177. // var ModelUrl = `${location.origin}${location.pathname}wwwroot/file/红头模板.doc?fileName=红头模板.doc`
  1178. var FileName = fname
  1179. var userFile = this.slCtl.Content.Control.getFileByName(fname)
  1180. if (
  1181. (userFile.TaodaInfo !== '' && userFile.TaodaInfo !== null) ||
  1182. userFile.FileType === 'RedTemplate'
  1183. ) {
  1184. // 只更新公文域
  1185. loginService.updateRefreshToken().then(() => {
  1186. this.markBusy(fname)
  1187. Promise.resolve(this.getTaodaValue())
  1188. .then((values) => {
  1189. this.updateActivedAtt?.(userFile.code, true)
  1190. this.slCtl.Content.Control.UpdateRegion(
  1191. FileName,
  1192. values,
  1193. '',
  1194. {
  1195. IsMaximization: this.isOpenFile ? 'true' : 'false',
  1196. Status: '-1',
  1197. WaterMark: this.waterMark(userFile, 'UpdateRegion'),
  1198. },
  1199. this.pluginInfo()
  1200. )?.finally(() => {
  1201. this.updateActivedAtt?.(userFile.code, false)
  1202. this.getFileList()
  1203. this.markBusy(fname, false)
  1204. })
  1205. })
  1206. .catch(() => {
  1207. this.markBusy(fname, false)
  1208. })
  1209. })
  1210. } else {
  1211. this.getTemplateUrl().then((ModelUrl) => {
  1212. loginService.updateRefreshToken().then(() => {
  1213. this.markBusy(fname)
  1214. Promise.resolve(this.getTaodaValue())
  1215. .then((values) => {
  1216. this.updateActivedAtt?.(userFile.code, true)
  1217. this.slCtl.Content.Control.TaoDa(
  1218. ModelUrl,
  1219. '正文部分',
  1220. FileName,
  1221. { Status: '-1', WaterMark: this.waterMark(userFile, 'TaoDa') },
  1222. '',
  1223. values,
  1224. this.userInfo.name,
  1225. this.pluginInfo()
  1226. )?.finally(() => {
  1227. this.updateActivedAtt?.(userFile.code, false)
  1228. this.getFileList()
  1229. this.markBusy(fname, false)
  1230. })
  1231. })
  1232. .catch(() => {
  1233. this.markBusy(fname, false)
  1234. })
  1235. })
  1236. })
  1237. }
  1238. }
  1239. Modal.confirm({
  1240. content: '请确认您是否已经做了清稿操作,如果没有,请您点击“取消”按钮返回,先清稿',
  1241. onOk() {
  1242. fnShowTaoDa(fname)
  1243. },
  1244. onCancel() {},
  1245. })
  1246. },
  1247. GaiZhangFile(fname) {
  1248. // 需要印章地址实现
  1249. this.getSealUrl().then((ModelUrl) => {
  1250. const item = this.attachmentsList.find((item) => item.name === fname)
  1251. loginService.updateRefreshToken().then(() => {
  1252. this.markBusy(fname)
  1253. this.updateActivedAtt?.(item.code, true)
  1254. this.slCtl.Content.Control.GaiZhang(
  1255. ModelUrl,
  1256. '印章',
  1257. fname,
  1258. { Status: '-1', WaterMark: this.waterMark(item, 'GaiZhang') },
  1259. '',
  1260. '',
  1261. this.pluginInfo()
  1262. )?.finally(() => {
  1263. this.updateActivedAtt?.(item.code, false)
  1264. this.getFileList()
  1265. this.markBusy(fname, false)
  1266. })
  1267. })
  1268. })
  1269. },
  1270. ConvertPDFFile(filename) {
  1271. // 监听转换完成事件
  1272. Modal.confirm({
  1273. content: '是否将此附件转换为PDF格式?',
  1274. onOk: () => {
  1275. loginService.updateRefreshToken().then(() => {
  1276. this.markBusy(filename)
  1277. this.slCtl.Content.Control.DOCConvertToPDF(filename, {}, this.pluginInfo())?.then(
  1278. () => {
  1279. this.getFileList()
  1280. this.markBusy(filename, false)
  1281. }
  1282. )
  1283. })
  1284. },
  1285. onCancel() {},
  1286. })
  1287. },
  1288. ConvertOFDFile(filename) {
  1289. // 监听转换完成事件
  1290. Modal.confirm({
  1291. content: '是否将此附件转换为OFD格式?',
  1292. onOk: () => {
  1293. loginService.updateRefreshToken().then(() => {
  1294. this.markBusy(filename)
  1295. this.slCtl.Content.Control.DOCConvertToOFD(filename, {}, this.pluginInfo())?.then(
  1296. () => {
  1297. this.getFileList()
  1298. this.markBusy(filename, false)
  1299. }
  1300. )
  1301. })
  1302. },
  1303. onCancel() {},
  1304. })
  1305. },
  1306. SaveFile(fname) {
  1307. // 非INDIDOCX控件使用原生js下载
  1308. if (this.pluginName !== 'INDIDOCX') {
  1309. download(this.attachmentsList.find((item) => item.name === fname).Link, fname)
  1310. return
  1311. }
  1312. loginService.updateRefreshToken().then(() => {
  1313. const item = this.attachmentsList.find((item) => item.name === fname)
  1314. this.markBusy(fname)
  1315. this.slCtl.Content.Control.SaveFileToLocal(
  1316. fname,
  1317. {
  1318. Status: Number(item.attr?.downloadstatus || -1),
  1319. },
  1320. this.pluginInfo()
  1321. ).then((file) => {
  1322. if (file?.fileinfo) {
  1323. Message.success('下载成功')
  1324. }
  1325. this.markBusy(fname, false)
  1326. })
  1327. })
  1328. },
  1329. markBusy(fname, isBusy = true) {
  1330. this.busy = { ...this.busy, [fname]: isBusy }
  1331. },
  1332. plsave() {
  1333. let p
  1334. if (this.attachmentsList.length <= 2) {
  1335. p = Promise.resolve(this.attachmentsList)
  1336. } else {
  1337. // 弹框下载
  1338. p = pickValues(sdListPickerVue, {
  1339. optionValue: 'Unid',
  1340. singleColumn: true,
  1341. loadListData: () => this.attachmentsList,
  1342. value: this.attachmentsList,
  1343. })
  1344. }
  1345. p.then((attachs) => {
  1346. if (attachs) {
  1347. if (attachs.length === 0) {
  1348. Message.warn('请选择需要下载的文件')
  1349. return
  1350. }
  1351. loginService.updateRefreshToken().then(() => {
  1352. const downMulti = this.slCtl?.Content?.Control?.SaveMultiFiles || undefined
  1353. if (downMulti) {
  1354. attachs.forEach((item) => this.markBusy(item.name))
  1355. downMulti(
  1356. attachs.map((item) => item.name).join('|'),
  1357. { Status: Number(attachs[0].attr?.downloadstatus || -1) },
  1358. this.pluginInfo()
  1359. ).then((flag) => {
  1360. if (flag && flag.fileinfo) {
  1361. Message.success('批量下载成功')
  1362. }
  1363. attachs.forEach((item) => this.markBusy(item.name, false))
  1364. })
  1365. } else {
  1366. // 多附件打包下载
  1367. const tmpArr = attachs[0].FileName.split('.')
  1368. tmpArr.pop()
  1369. const fileName = tmpArr.join('.')
  1370. download(
  1371. `api/framework/v1/attachment-extend/attachments-download/multi?groupId=${
  1372. this.groupId
  1373. }&codes=${attachs.map((_) => _.code).toString()}`,
  1374. `${fileName}等${attachs.length}个附件.zip`
  1375. )
  1376. }
  1377. })
  1378. }
  1379. })
  1380. },
  1381. uploadByTemplateCode(code, catNum = '-1') {
  1382. if (!this.temtype) {
  1383. this.temdataurl = `api/xcoa-mobile/v1/redhead-manager/doctype/${this.businessTypeId}/redheads`
  1384. }
  1385. let p
  1386. if (code) {
  1387. p = Promise.resolve(code)
  1388. } else {
  1389. const businessTypeId = this.webflow?.FlowData.processFormData.processFormPropertyValues.find(
  1390. (item) => item.name === 'businessTypeId'
  1391. )?.value
  1392. p = axios
  1393. .get(`api/framework/v1/redhead-manager/doctype/${businessTypeId}/redheads`)
  1394. .then((res) => {
  1395. return pickValues(sdListPickerVue, {
  1396. optionValue: 'attachment',
  1397. optionLabel: 'redheadName',
  1398. singleColumn: true,
  1399. loadListData: () => res.data,
  1400. single: true,
  1401. }).then((data) => {
  1402. if (data?.[0]?.attachment) {
  1403. return axios
  1404. .get(
  1405. `api/framework/v1/attachment-extend/attachments-info-perm/${data?.[0]?.attachment}`
  1406. )
  1407. .then((res) => {
  1408. return res.data[0].code
  1409. })
  1410. }
  1411. })
  1412. })
  1413. }
  1414. return p.then((code) => {
  1415. if (code) {
  1416. return axios({
  1417. method: 'post',
  1418. url: `api/framework/v1/attachment-extend/attachment-copy/${code}/${this.groupId}?catNum=${catNum}&fromTemplate=1`,
  1419. params: this.infoProperties,
  1420. })
  1421. }
  1422. })
  1423. },
  1424. revertZw(fname) {
  1425. this.markBusy(fname)
  1426. const lock = sdLocalStorage.getItem(`Lock${this.webflow?.FlowData?.instId}`)
  1427. const lockId = lock && JSON.parse(lock).sessionId
  1428. axios
  1429. .get('api/framework/v1/redhead-manager/revokeReadHead', {
  1430. params: {
  1431. groupId: this.groupId,
  1432. traceManuscriptCode: this.attachments.find((item) => item.CatNum === '-4').code,
  1433. mainBodyCode: this.attachments.find((item) => item.CatNum === '-1').code,
  1434. lockId,
  1435. ...this.infoProperties,
  1436. },
  1437. })
  1438. .then(this.getFileList)
  1439. .finally(() => {
  1440. this.markBusy(fname, false)
  1441. })
  1442. },
  1443. // (新建)在线编辑正文
  1444. createZw() {
  1445. this.createZwLoading = true
  1446. // 创建正文
  1447. this.slCtl.Content.Control.createFile(this.groupId, {
  1448. ...this.infoProperties,
  1449. catNum: -1,
  1450. environment: this.sdPlatform.isXC ? 'xc' : 'x86',
  1451. type: 'docx',
  1452. })
  1453. .then((data) => {
  1454. return this.getFileList().then(() => {
  1455. // 打开编辑
  1456. return this.editFileByUrl(data.fileName, data.url)
  1457. })
  1458. })
  1459. .finally(() => {
  1460. this.createZwLoading = false
  1461. })
  1462. },
  1463. // (新建)在线编辑附件
  1464. createFj() {
  1465. this.createFjLoading = true
  1466. // 创建附件
  1467. this.slCtl.Content.Control.createFile(this.groupId, {
  1468. ...this.infoProperties,
  1469. catNum: 0,
  1470. environment: this.sdPlatform.isXC ? 'xc' : 'x86',
  1471. type: 'docx',
  1472. })
  1473. .then((data) => {
  1474. return this.getFileList().then(() => {
  1475. // 打开编辑
  1476. return this.editFileByUrl(data.fileName, data.url)
  1477. })
  1478. })
  1479. .finally(() => {
  1480. this.createFjLoading = false
  1481. })
  1482. },
  1483. editFileByUrl(fname, url) {
  1484. const item = this.attachmentsList.find((item) => item.name === fname)
  1485. this.markBusy(fname)
  1486. this.updateActivedAtt?.(item.code, !!item.editable)
  1487. return this.slCtl.Content.Control.EditFileByUrl(fname, url).finally(() => {
  1488. this.markBusy(fname, false)
  1489. this.updateActivedAtt?.(item.code, false)
  1490. })
  1491. },
  1492. showVersionInfo() {
  1493. this.slCtl.Content?.Control?._getTerminalInfo?.()
  1494. },
  1495. },
  1496. }
  1497. </script>
  1498. <style module lang="scss">
  1499. @use '@/common/design' as *;
  1500. @import '@/common/components/sd-attachment.scss';
  1501. .attachmentextab {
  1502. position: relative;
  1503. display: flex;
  1504. flex-direction: column;
  1505. height: calc(100vh - 180px);
  1506. background: white;
  1507. }
  1508. .wps-empty {
  1509. display: flex;
  1510. flex-direction: column;
  1511. align-items: center;
  1512. justify-content: center;
  1513. height: 100%;
  1514. }
  1515. .tab {
  1516. top: 10px;
  1517. height: auto;
  1518. :global(.ant-tabs-bar) {
  1519. height: 39px;
  1520. }
  1521. :global(.ant-tabs-extra-content) {
  1522. > span {
  1523. display: inline-block;
  1524. margin-right: 10px;
  1525. }
  1526. }
  1527. :global(.ant-tabs-top-content) {
  1528. max-height: 175px;
  1529. overflow-y: auto;
  1530. }
  1531. overflow: visible;
  1532. }
  1533. .buttons {
  1534. padding: 6px 0;
  1535. text-align: right;
  1536. }
  1537. :global(.hide) {
  1538. display: none;
  1539. }
  1540. .reader {
  1541. height: 100%;
  1542. }
  1543. .filename {
  1544. float: left;
  1545. &[disabled] {
  1546. color: $text-color;
  1547. }
  1548. }
  1549. .contain {
  1550. position: relative;
  1551. .controlinfor {
  1552. position: absolute;
  1553. top: 0;
  1554. right: 0;
  1555. z-index: -1;
  1556. display: none;
  1557. padding: 6px 0;
  1558. opacity: 0;
  1559. }
  1560. .controlinfo {
  1561. z-index: -1;
  1562. opacity: 0;
  1563. }
  1564. &:hover {
  1565. .controlinfor,
  1566. .controlinfo {
  1567. z-index: 1;
  1568. opacity: 1;
  1569. transition: all 1s ease 2s;
  1570. }
  1571. }
  1572. }
  1573. </style>