ArchivePreview.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. <template>
  2. <div style="padding: 20px; background-color: #f9f9f9; border-radius: 4px">
  3. <div
  4. style="
  5. display: flex;
  6. justify-content: space-between;
  7. align-items: center;
  8. margin-bottom: 20px;
  9. "
  10. >
  11. <div style="font-size: 16px; font-weight: 500">档案预览</div>
  12. <div>
  13. <el-button style="margin-right: 10px" @click="handlePrevStep">
  14. 上一步
  15. </el-button>
  16. <el-button type="success" @click="handleComplete">完成</el-button>
  17. </div>
  18. </div>
  19. <!-- 状态提示区域 -->
  20. <div
  21. v-if="archiveStatus !== null"
  22. style="margin-bottom: 20px; padding: 15px; border-radius: 4px"
  23. :style="statusStyle"
  24. >
  25. <div style="display: flex; align-items: center">
  26. <i :class="statusIcon" style="font-size: 20px; margin-right: 10px"></i>
  27. <span style="font-size: 14px">{{ statusText }}</span>
  28. <span
  29. v-if="archiveTime && archiveStatus === '2'"
  30. style="margin-left: 20px; color: #909399; font-size: 12px"
  31. >
  32. 生成时间:{{ archiveTime }}
  33. </span>
  34. </div>
  35. </div>
  36. <!-- 操作按钮区域 -->
  37. <div class="preview-actions" style="margin-bottom: 20px">
  38. <!-- 未生成状态:显示生成卷宗按钮 -->
  39. <el-button
  40. v-if="archiveStatus === '0'"
  41. type="primary"
  42. icon="el-icon-document-add"
  43. :loading="generating"
  44. @click="handleGenerateArchive"
  45. >
  46. 生成卷宗
  47. </el-button>
  48. <!-- 已生成状态:显示预览、下载、重新生成按钮 -->
  49. <template v-if="archiveStatus === '2'">
  50. <el-button
  51. plain
  52. type="primary"
  53. icon="el-icon-download"
  54. @click="handleDownload"
  55. >
  56. 下载
  57. </el-button>
  58. <el-button
  59. plain
  60. type="warning"
  61. icon="el-icon-refresh"
  62. :loading="generating"
  63. @click="handleRegenerate"
  64. >
  65. 重新生成
  66. </el-button>
  67. </template>
  68. <!-- 生成失败状态:显示重新生成按钮 -->
  69. <el-button
  70. v-if="archiveStatus === '3'"
  71. type="danger"
  72. icon="el-icon-refresh"
  73. :loading="generating"
  74. @click="handleGenerateArchive"
  75. >
  76. 重新生成
  77. </el-button>
  78. </div>
  79. <!-- 档案预览内容区域 -->
  80. <div
  81. style="
  82. border: 1px solid #dcdfe6;
  83. border-radius: 4px;
  84. padding: 20px;
  85. background-color: #fff;
  86. min-height: 400px;
  87. "
  88. >
  89. <!-- 已生成且有URL时显示预览 -->
  90. <div v-if="archiveStatus === '2' && archiveUrl">
  91. <iframe
  92. :src="previewUrl"
  93. style="width: 100%; height: 600px; border: none"
  94. ></iframe>
  95. </div>
  96. <!-- 其他状态显示提示 -->
  97. <div v-else style="text-align: center; color: #909399; padding: 100px 0">
  98. <i
  99. :class="emptyIcon"
  100. style="font-size: 48px; margin-bottom: 20px; display: block"
  101. ></i>
  102. <div>{{ emptyText }}</div>
  103. </div>
  104. </div>
  105. </div>
  106. </template>
  107. <script>
  108. import {
  109. getArchiveStatus,
  110. generateArchive,
  111. queConfirmArchive,
  112. } from '@/api/audit/archive'
  113. export default {
  114. name: 'ArchivePreview',
  115. props: {
  116. taskId: {
  117. type: String,
  118. default: '',
  119. },
  120. },
  121. data() {
  122. return {
  123. loading: false,
  124. generating: false,
  125. archiveStatus: null, // 0-未生成 1-生成中 2-已生成 3-生成失败
  126. archiveUrl: '',
  127. archiveTime: '',
  128. pollingTimer: null, // 轮询定时器
  129. pollingInterval: 5000, // 轮询间隔(毫秒)
  130. }
  131. },
  132. computed: {
  133. // kkFileView 预览URL
  134. previewUrl() {
  135. if (!this.archiveUrl) return ''
  136. const fileUrl = this.getFullFileUrl(this.archiveUrl)
  137. const encodedUrl = encodeURIComponent(Base64.encode(fileUrl))
  138. return `${host}:8012/onlinePreview?url=${encodedUrl}`
  139. },
  140. statusText() {
  141. const textMap = {
  142. 0: '卷宗尚未生成,请点击"生成卷宗"按钮开始生成',
  143. 1: '卷宗正在生成中,请稍候...',
  144. 2: '卷宗已生成完成',
  145. 3: '卷宗生成失败,请重新生成',
  146. }
  147. return textMap[this.archiveStatus] || '未知状态'
  148. },
  149. statusIcon() {
  150. const iconMap = {
  151. 0: 'el-icon-info',
  152. 1: 'el-icon-loading',
  153. 2: 'el-icon-success',
  154. 3: 'el-icon-error',
  155. }
  156. return iconMap[this.archiveStatus] || 'el-icon-info'
  157. },
  158. statusStyle() {
  159. const styleMap = {
  160. 0: { backgroundColor: '#f4f4f5', color: '#909399' },
  161. 1: { backgroundColor: '#fdf6ec', color: '#e6a23c' },
  162. 2: { backgroundColor: '#f0f9eb', color: '#67c23a' },
  163. 3: { backgroundColor: '#fef0f0', color: '#f56c6c' },
  164. }
  165. return styleMap[this.archiveStatus] || {}
  166. },
  167. emptyIcon() {
  168. const iconMap = {
  169. 0: 'el-icon-document',
  170. 1: 'el-icon-loading',
  171. 3: 'el-icon-warning',
  172. }
  173. return iconMap[this.archiveStatus] || 'el-icon-document'
  174. },
  175. emptyText() {
  176. const textMap = {
  177. 0: '请先生成卷宗',
  178. 1: '卷宗生成中,请稍候...',
  179. 3: '卷宗生成失败,请重新生成',
  180. }
  181. return textMap[this.archiveStatus] || '档案预览内容区域'
  182. },
  183. },
  184. watch: {
  185. taskId: {
  186. handler(newVal) {
  187. if (newVal) {
  188. this.fetchArchiveStatus()
  189. }
  190. },
  191. immediate: true,
  192. },
  193. // 监听状态变化,控制轮询
  194. archiveStatus: {
  195. handler(newVal) {
  196. if (newVal === '1') {
  197. // 生成中状态,启动轮询
  198. this.startPolling()
  199. } else {
  200. // 其他状态,停止轮询
  201. this.stopPolling()
  202. }
  203. },
  204. },
  205. },
  206. beforeDestroy() {
  207. // 组件销毁前清除定时器
  208. this.stopPolling()
  209. },
  210. methods: {
  211. async fetchArchiveStatus() {
  212. if (!this.taskId) return
  213. try {
  214. this.loading = true
  215. const response = await getArchiveStatus(this.taskId)
  216. if (response && response.value) {
  217. const data = response.value
  218. this.archiveStatus = data.archiveStatus || '0'
  219. this.archiveUrl = data.archiveUrl || ''
  220. this.archiveTime = data.archiveTime || ''
  221. } else {
  222. this.archiveStatus = '0'
  223. }
  224. } catch (error) {
  225. console.error('获取归档状态失败:', error)
  226. this.archiveStatus = '0'
  227. } finally {
  228. this.loading = false
  229. }
  230. },
  231. async handleGenerateArchive() {
  232. if (!this.taskId) {
  233. this.$message.warning('缺少任务信息')
  234. return
  235. }
  236. try {
  237. this.generating = true
  238. const response = await generateArchive(this.taskId)
  239. if (response && response.success !== false) {
  240. this.$message.success(response.message || '卷宗生成请求已提交')
  241. // 刷新状态
  242. await this.fetchArchiveStatus()
  243. } else {
  244. this.$message.error(response.message || '生成失败')
  245. }
  246. } catch (error) {
  247. console.error('生成卷宗失败:', error)
  248. this.$message.error(
  249. '生成失败:' + (error.message || '请检查网络连接')
  250. )
  251. } finally {
  252. this.generating = false
  253. }
  254. },
  255. handleRegenerate() {
  256. this.$confirm(
  257. '确定要重新生成卷宗吗?这将覆盖现有的卷宗文件。',
  258. '提示',
  259. {
  260. confirmButtonText: '确定',
  261. cancelButtonText: '取消',
  262. type: 'warning',
  263. }
  264. )
  265. .then(() => {
  266. this.handleGenerateArchive()
  267. })
  268. .catch(() => {})
  269. },
  270. // 获取完整文件URL
  271. getFullFileUrl(fileUrl) {
  272. if (!fileUrl) return ''
  273. if (fileUrl.startsWith('http')) {
  274. return fileUrl
  275. }
  276. return window.context.form + fileUrl
  277. },
  278. // 使用 kkFileView 预览
  279. handlePreview() {
  280. if (!this.archiveUrl) {
  281. this.$message.warning('卷宗文件地址为空')
  282. return
  283. }
  284. const fileUrl = this.getFullFileUrl(this.archiveUrl)
  285. const encodedUrl = encodeURIComponent(Base64.encode(fileUrl))
  286. window.open(`${host}:8012/onlinePreview?url=${encodedUrl}`, '_blank')
  287. },
  288. handleDownload() {
  289. if (!this.archiveUrl) {
  290. this.$message.warning('卷宗文件地址为空')
  291. return
  292. }
  293. const fileUrl = this.getFullFileUrl(this.archiveUrl)
  294. // 创建下载链接
  295. const link = document.createElement('a')
  296. link.href = fileUrl
  297. link.download = '档案卷宗.pdf'
  298. link.target = '_blank'
  299. document.body.appendChild(link)
  300. link.click()
  301. document.body.removeChild(link)
  302. },
  303. handlePrevStep() {
  304. this.$emit('prev-step')
  305. },
  306. handleComplete() {
  307. // 检查卷宗是否已生成
  308. if (this.archiveStatus !== '2') {
  309. this.$message.warning('请先生成卷宗后再完成归档')
  310. return
  311. }
  312. this.$confirm('确定要完成归档吗?', '提示', {
  313. confirmButtonText: '确定',
  314. cancelButtonText: '取消',
  315. type: 'warning',
  316. })
  317. .then(async () => {
  318. try {
  319. const response = await queConfirmArchive(this.taskId)
  320. if (response && response.success !== false) {
  321. this.$message.success(response.message || '归档成功')
  322. this.$emit('complete')
  323. } else {
  324. this.$message.error(response.message || '归档失败')
  325. }
  326. } catch (error) {
  327. console.error('确定归档失败:', error)
  328. this.$message.error(
  329. '归档失败:' + (error.message || '请检查网络连接')
  330. )
  331. }
  332. })
  333. .catch(() => {})
  334. },
  335. // 启动轮询
  336. startPolling() {
  337. // 先清除已有的定时器
  338. this.stopPolling()
  339. // 启动新的定时器
  340. this.pollingTimer = setInterval(() => {
  341. this.fetchArchiveStatus()
  342. }, this.pollingInterval)
  343. },
  344. // 停止轮询
  345. stopPolling() {
  346. if (this.pollingTimer) {
  347. clearInterval(this.pollingTimer)
  348. this.pollingTimer = null
  349. }
  350. },
  351. },
  352. }
  353. </script>
  354. <style scoped lang="scss"></style>