xm-sd-attachment-ex.vue 50 KB

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