index.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. <!--
  2. * @Descripttion: POCT文件培训
  3. * @version: 1.0
  4. * @Author: Liu_jiaYin
  5. * @Date: 2024-03-01 13:47:32
  6. * @LastEditors: Do not edit
  7. * @LastEditTime: 2024-04-26 16:04:02
  8. -->
  9. <template>
  10. <!-- <el-dialog
  11. :visible.sync="dialogVisible"
  12. fullscreen
  13. :title="title"
  14. append-to-body
  15. custom-class="ibps-file-preview-dialog"
  16. >
  17. <div>
  18. <fView v-if="refresh" ref="fvView" :option-file="optionFile" :operation_status="operation_status" @hadLoadedFile="hadLoadedFile" />
  19. </div>
  20. </el-dialog> -->
  21. <!--个人修改 -->
  22. <el-dialog
  23. :visible.sync="dialogVisible"
  24. fullscreen
  25. append-to-body
  26. custom-class="ibps-file-preview-dialog"
  27. :show-close="false"
  28. >
  29. <template #title>
  30. <el-row>
  31. <el-col :span="11" class="titleHander">{{ title }}</el-col>
  32. <el-col :span="3" class="read" style="text-align: right;">阅读量:{{ lookNum }}</el-col>
  33. <el-col :span="10" style="text-align: right;">
  34. <el-popover
  35. v-model="deleteVisible"
  36. placement="top"
  37. width="160"
  38. >
  39. <p>文件删除之后将不能查看,确定要删除该文件吗?</p>
  40. <div style="text-align: right; margin: 0">
  41. <el-button size="mini" type="text" @click="deleteVisible = false">取消</el-button>
  42. <el-button type="primary" size="mini" @click="deleteFile">确定</el-button>
  43. </div>
  44. <el-button v-if="deleteShow" slot="reference" type="danger" icon="el-icon-delete" class="deleteBtn" @click="deleteVisible=true">删除</el-button>
  45. </el-popover>
  46. <el-button type="primary" icon="el-icon-view" @click="lookFile">查看文件信息</el-button>
  47. <el-button type="primary" icon="el-icon-s-fold" @click="hideLeft">左侧内容</el-button>
  48. <el-button v-if="deleteShow" type="primary" icon="el-icon-download" @click="updateFile">下载文件</el-button>
  49. <el-button type="danger" icon="el-icon-close" @click="closeDialog">关闭</el-button>
  50. </el-col>
  51. </el-row>
  52. </template>
  53. <div>
  54. <el-row>
  55. <el-col v-if="leftShow" :span="4" class="left-content">
  56. <el-timeline :reverse="reverse">
  57. <el-timeline-item
  58. v-for="(activity, index) in leftData"
  59. :key="index"
  60. :timestamp="activity.fa_fang_shi_jian_"
  61. :type="index === activeIndex ? type : ''"
  62. @click.stop.native="toggleActive(activity, index)"
  63. >
  64. <div>
  65. <el-tooltip class="itemStyle" effect="dark" placement="right-end" :content="showContent(activity,index)">
  66. <div>版本号:{{ activity.ban_ben_ }}/修订人:{{ getUserName(activity.bian_zhi_ren_) }}</div>
  67. </el-tooltip>
  68. </div>
  69. </el-timeline-item>
  70. </el-timeline>
  71. </el-col>
  72. <el-col :span="computedSpan"><fView v-if="refresh" ref="fvView" :option-file="optionFile" :operation_status="operation_status" @hadLoadedFile="hadLoadedFile" /></el-col>
  73. </el-row>
  74. </div>
  75. <!-- 查看文件信息弹窗 @close="closeDialog"-->
  76. <FileDialog
  77. v-if="innerVisible"
  78. :show-list="showList"
  79. :dig-data="digData"
  80. :inner-visible="innerVisible"
  81. :file-index="fileIndex"
  82. @pause="pauseTimer"
  83. @start="startTimer"
  84. @update-inner-visible="updateInnerVisible"
  85. />
  86. </el-dialog>
  87. </template>
  88. <script>
  89. /**
  90. * 文件预览
  91. * 1、'doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx' 类型支持
  92. * 2、图片支持缩放
  93. * 3、音频,语音支持
  94. * ==================
  95. * 下一版本支持
  96. * 1、pdf支持缩放
  97. * 2、音频,语音多格式支持
  98. * 3、压缩包支持
  99. */
  100. import fView from '@/business/platform/file/attachment/editFile/fView.vue'
  101. import { SYSTEM_URL, BASE_API } from '@/api/baseUrl'
  102. import ViewFile from '@/views/viewFile/index.vue'
  103. import Template from '@/business/platform/form/form-print/template.vue'
  104. import curdPost from '@/business/platform/form/utils/custom/joinCURD.js'
  105. import FileDialog from './fileDialog.vue'
  106. // import * as selectbox from 'bpmn-js-properties-panel/lib/factory/SelectEntryFactory'
  107. // import func from 'vue-editor-bridge'
  108. export default {
  109. name: 'file-training',
  110. components: {
  111. fView,
  112. ViewFile,
  113. Template,
  114. FileDialog
  115. },
  116. props: {
  117. visible: {
  118. type: Boolean,
  119. default: false
  120. },
  121. // fileInfos: {
  122. // type: Object,
  123. // default: () => {}
  124. // },
  125. fileInfos: {
  126. type: Array,
  127. default: () => {}
  128. }
  129. // template: {
  130. // type: Object,
  131. // default: () => {}
  132. // }
  133. },
  134. data () {
  135. const { userList, userId, role, isSuper } = this.$store.getters
  136. // const userId = this.$store.getters.userInfo.employee.id// 本人修改
  137. return {
  138. type: 'success',
  139. curFileName: '',
  140. dialogVisible: false,
  141. operation_status: 'fileTraining',
  142. title: '',
  143. browseTime: 0, // 浏览时长初始值为 0
  144. clearTimeSet: null,
  145. optionFile: {},
  146. tmpId: '',
  147. upFunc: () => {},
  148. height: 0,
  149. out: false, // 记录鼠标是否离开过被监听的位置,未离开过则startTimer不启用
  150. hadLoad: false,
  151. refresh: false,
  152. // 本人修改
  153. leftShow: true,
  154. scrollTimeout: null,
  155. reverse: false,
  156. fileInfo: '',
  157. fileJie: '',
  158. leftData: [],
  159. activeIndex: 0,
  160. userList: userList,
  161. userId: userId,
  162. innerVisible: false,
  163. currentPage: 1,
  164. pageSize: 10,
  165. lookNum: null,
  166. showList: [],
  167. digData: null,
  168. paused: false,
  169. role: role,
  170. isSuper: isSuper,
  171. deleteVisible: false,
  172. deleteShow: false
  173. }
  174. },
  175. // 本人修改
  176. computed: {
  177. computedSpan () {
  178. return this.leftShow ? 20 : 24
  179. },
  180. fileIndex () {
  181. if (this.leftData.length <= 0) {
  182. return 0
  183. }
  184. return this.leftData.findIndex(i => i === this.digData)
  185. }
  186. },
  187. watch: {
  188. visible: {
  189. immediate: true,
  190. handler (val) {
  191. this.digData = this.leftData[0]
  192. }
  193. },
  194. // 本人修改
  195. fileInfos: {
  196. handler (newVal) {
  197. this.leftData = newVal
  198. const temp = JSON.parse(JSON.stringify(newVal))
  199. temp.sort((a, b) => {
  200. return new Date(b.fa_fang_shi_jian_).getTime() - new Date(a.fa_fang_shi_jian_).getTime()
  201. })
  202. if (newVal !== temp) {
  203. this.leftData = temp
  204. }
  205. temp.forEach(val => {
  206. this.fileShow(temp[0])
  207. })
  208. // newVal.forEach(val => {
  209. // this.fileShow(val)
  210. // })
  211. },
  212. deep: true, // 深度监听,确保对象属性变化也能触发(本人修改)
  213. immediate: true
  214. },
  215. browseTime: {
  216. handler: function (val, oldVal) {
  217. if (this.curFileName) {
  218. this.title = `文件:《 ${this.curFileName} 》,查阅时长:${val}秒`
  219. } else {
  220. this.title = `文件:《 ${this.leftData[0]?.FILE_NAME_} 》,查阅时长:${val}秒`
  221. }
  222. if (!this.lookNum) {
  223. if (this.leftData[0]?.id) {
  224. this.checkNum(this.leftData[0])
  225. }
  226. }
  227. },
  228. immediate: true
  229. },
  230. dialogVisible: {
  231. handler: function (val, oldVal) {
  232. if (!val) {
  233. this.closeDialog()
  234. }
  235. }
  236. }
  237. },
  238. beforeDestroy () {
  239. this.fileType = ''
  240. this.option = {}
  241. // 本人修改
  242. this.removeMouseMoveListener()
  243. },
  244. mounted () {
  245. this.height = this.getDialogHeightHeight()
  246. // 页面切换时改变计时状态
  247. document.addEventListener('visibilitychange', this.handlePageChange)
  248. this.checkDialogBody()
  249. // task-3972 删除权限仅开放给超级用户和文件管理员
  250. // task-4059 下载、删除权限均只对超级用户和文件管理员开放
  251. const roleList = ['wjgly']
  252. const isPower = this.role.some(item => roleList.includes(item.alias))
  253. this.deleteShow = this.isSuper || isPower
  254. },
  255. methods: {
  256. getUserName (data) {
  257. const user = this.userList.find(item => item.userId === data)
  258. return user ? user.userName : '未知用户'
  259. },
  260. showContent (activity, index) {
  261. if (activity.cao_zuo_lei_xing_ === '新增') {
  262. return '第一版本'
  263. }
  264. return activity.xiu_ding_nei_rong ? activity.xiu_ding_nei_rong : '无修订原因'
  265. },
  266. handlePageChange () {
  267. if (document.visibilityState === 'hidden') {
  268. this.pauseTimer()
  269. } else {
  270. this.startTimer()
  271. }
  272. },
  273. closeDialog () {
  274. this.$emit('close')
  275. const fvView = this.$refs.fvView
  276. // 销毁子组件方法
  277. fvView.destoryZiComponent()
  278. if (this.browseTime >= 30) {
  279. this.upFunc(this.tmpId, this.browseTime)
  280. }
  281. // 针对关闭窗口或者浏览器的
  282. if (this.clearTimeSet != null) {
  283. clearInterval(this.clearTimeSet)
  284. this.clearTimeSet = null
  285. }
  286. this.leftShow = true
  287. this.out = false
  288. this.browseTime = 0
  289. // this.curFileName = ''// 本人添加
  290. // this.lookNum = null
  291. // this.showList = []
  292. // this.leftData = []
  293. // this.digData = null
  294. },
  295. hadLoadedFile (v) {
  296. this.setBrowseTime()
  297. this.hadLoad = true
  298. },
  299. setBrowseTime () {
  300. // 设置定时器
  301. this.clearTimeSet = setInterval(() => {
  302. this.browseTime++
  303. }, 1000)
  304. },
  305. getDialogHeightHeight () {
  306. return ((document.documentElement.clientHeight || document.body.clientHeight) - 60) + 'px'
  307. },
  308. startTimer () {
  309. if (this.dialogVisible && this.hadLoad && this.out && this.clearTimeSet == null) {
  310. this.clearTimeSet = setInterval(() => {
  311. this.browseTime++
  312. }, 1000)
  313. }
  314. },
  315. pauseTimer () {
  316. if (this.dialogVisible) {
  317. this.out = true
  318. clearInterval(this.clearTimeSet)
  319. this.clearTimeSet = null
  320. }
  321. },
  322. // 本人修改
  323. // id转换
  324. async idChange (id) {
  325. // const sql = `select id_ FROM t_wjxxb WHERE shu_ju_lai_yuan_ = '${id}'`
  326. return new Promise((resolve, reject) => {
  327. this.$common.request('query', {
  328. key: 'getFileInfoById',
  329. params: [id]
  330. }).then((res) => {
  331. const { data = [] } = res.variables || {}
  332. const firstId = data[0]?.id_
  333. resolve(firstId) // 解析 Promise 时返回 firstId
  334. }).catch(error => {
  335. reject(error) // 捕获错误并拒绝 Promise
  336. })
  337. })
  338. },
  339. toggleActive (activity, index) {
  340. if (this.activeIndex === index) { return }
  341. this.activeIndex = index
  342. this.digData = activity
  343. this.fileShow(activity)
  344. if (this.browseTime >= 30) {
  345. this.upFunc(this.tmpId, this.browseTime)
  346. }
  347. clearInterval(this.clearTimeSet)
  348. this.browseTime = 0
  349. this.curFileName = activity.FILE_NAME_
  350. this.checkNum(activity)// 阅读量
  351. // this.$forceUpdate()// 触发监听器
  352. },
  353. // 阅读量函数
  354. checkNum (activity) {
  355. // const sql = `select t_wjcyjl.* from t_wjcyjl INNER JOIN t_wjxxb ON t_wjcyjl.parent_id_ = t_wjxxb.id_ WHERE t_wjxxb.shu_ju_lai_yuan_ = '${activity.id}' order by create_time_ desc`
  356. // const sql1 = `select * from t_wjcyjl where parent_id_= '${activity.id}' order by create_time_ desc`
  357. this.$common.request('query', {
  358. key: 'getFileCheckRecord',
  359. params: [activity.id]
  360. }).then((res) => {
  361. const { data = [] } = res.variables || {}
  362. this.lookNum = data.length
  363. this.showList = data
  364. })
  365. },
  366. handleUpdate (fileId, time) {
  367. const addParams = {
  368. tableName: 't_wjcyjl',
  369. paramWhere: [
  370. {
  371. bian_zhi_ren_: this.userId,
  372. bian_zhi_shi_jian: this.$common.getDateNow(19),
  373. parent_id_: fileId,
  374. shi_chang_: time
  375. }
  376. ]
  377. }
  378. curdPost('add', addParams).then(res => {
  379. this.refreshData()
  380. })
  381. },
  382. hideLeft () {
  383. this.leftShow = !this.leftShow
  384. },
  385. lookFile () {
  386. // console.log(document.querySelector('iframe').contentWindow.document)
  387. // console.log(document.querySelector('iframe').contentWindow.document.body.innerHTML);
  388. if (this.digData) {
  389. this.checkNum(this.digData)
  390. } else {
  391. this.digData = this.leftData[0]
  392. this.checkNum(this.digData)
  393. }
  394. this.innerVisible = true
  395. },
  396. deleteFile () {
  397. this.deleteVisible = false
  398. const deleteParams = {
  399. tableName: 't_wjxxb',
  400. paramWhere: { id_: this.leftData[0].zId }
  401. }
  402. curdPost('delete', deleteParams).then(() => {
  403. this.$message({
  404. message: '删除成功!',
  405. type: 'warning'
  406. })
  407. this.dialogVisible = false
  408. })
  409. },
  410. // closeDialog1 (val) {
  411. // this.innerVisible = val
  412. // },
  413. updateFile () {
  414. const a = document.createElement('a')
  415. a.href = this.optionFile.url
  416. a.download = this.optionFile.data.fileName
  417. document.body.appendChild(a)
  418. a.click()
  419. a.remove()
  420. },
  421. fileShow (val) {
  422. return new Promise((resolve, reject) => {
  423. try {
  424. this.dialogVisible = true
  425. this.title = `文件:《${val.FILE_NAME_}》`
  426. this.idChange(val.id).then(res => {
  427. this.tmpId = res
  428. if (val.func) { this.upFunc = val.func }
  429. const data = {
  430. ext: val.fileInfos.EXT_,
  431. fileName: val.fileInfos.FILE_NAME_,
  432. id: val.fileInfos.ID_,
  433. index: 0,
  434. totalBytes: val.fileInfos.TOTAL_BYTES_
  435. }
  436. this.optionFile.url = `${BASE_API()}${SYSTEM_URL()}/file/download?attachmentId=${data.id}`
  437. this.optionFile.editUrl = `${BASE_API()}${SYSTEM_URL()}/file/editCallback?fileName=${data.fileName}&fileType=${data.ext}&type=fileTraining&id=${data.id}`
  438. this.optionFile.title = data.fileName // 文件名称
  439. this.optionFile.fileType = data.ext // 类型
  440. this.optionFile.data = data // 记录编制的位置,需要替换。
  441. this.optionFile.data.index = data.index
  442. // 使用 v-if 实现组件刷新功能
  443. this.refresh = false
  444. this.$nextTick(() => {
  445. this.refresh = true
  446. resolve() // 异步操作完成后 resolve
  447. })
  448. })
  449. } catch (error) {
  450. reject(error)
  451. }
  452. })
  453. },
  454. // 排序函数
  455. sortByFabushijianDesc (data) {
  456. return data.sort((a, b) => {
  457. const dateA = new Date(a.fa_fang_shi_jian_)
  458. const dateB = new Date(b.fa_fang_shi_jian_)
  459. return dateB - dateA // 降序排序
  460. })
  461. },
  462. formattedTimestamp (timestamp) {
  463. const date = new Date(timestamp)
  464. // 获取年月日时分秒
  465. const year = date.getFullYear()
  466. const month = String(date.getMonth() + 1).padStart(2, '0')
  467. const day = String(date.getDate()).padStart(2, '0')
  468. const hours = String(date.getHours()).padStart(2, '0')
  469. const minutes = String(date.getMinutes()).padStart(2, '0')
  470. const seconds = String(date.getSeconds()).padStart(2, '0')
  471. // 格式化日期
  472. return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
  473. },
  474. checkDialogBody () {
  475. const intervalId = setInterval(() => {
  476. if (document.querySelector('iframe')) {
  477. this.dialogBody = document.querySelector('iframe')
  478. this.addMouseMoveListener()
  479. clearInterval(intervalId)
  480. }
  481. }, 200)
  482. },
  483. addMouseMoveListener () {
  484. if (this.dialogBody && !this.mouseMoveHandler) {
  485. this.mouseMoveHandler = () => {
  486. console.log('鼠标移动事件!!!!')
  487. }
  488. this.dialogBody.addEventListener('mousemove', this.mouseMoveHandler, true)
  489. }
  490. },
  491. removeMouseMoveListener () {
  492. if (this.dialogBody && this.mouseMoveHandler) {
  493. this.dialogBody.removeEventListener('mousemove', this.mouseMoveHandler, true)
  494. this.mouseMoveHandler = null
  495. }
  496. },
  497. updateInnerVisible (newVal) {
  498. this.innerVisible = newVal
  499. }
  500. }
  501. }
  502. </script>
  503. <style lang="scss">
  504. .ibps-file-preview-dialog {
  505. width: 80%;
  506. z-index:99999;
  507. .el-dialog__body {
  508. padding: 0;
  509. }
  510. .file-type-txt {
  511. height: calc(88vh) !important;
  512. }
  513. .itemStyle:hover{
  514. cursor: pointer;
  515. }
  516. .titleHander, .read{
  517. line-height: 32px;
  518. }
  519. .deleteBtn{
  520. margin: 0 10px 0 0;
  521. }
  522. }
  523. .left-content{
  524. padding-top: 2%;
  525. }
  526. // .file-read-num{
  527. // display: inline-block;
  528. // margin-left: 60px;
  529. // }
  530. </style>