index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  1. <template>
  2. <div class="ws-autograph">
  3. <el-upload action="#"
  4. list-type="picture-card"
  5. :http-request="_handleRequest"
  6. :file-list="fileList"
  7. :before-upload="_handleBeforeUpload"
  8. :accept="accept"
  9. :multiple="!limit || parseInt(limit) > 1"
  10. ref="elUpload">
  11. <template v-slot:trigger>
  12. <div ref="triggerDiv">-</div>
  13. </template>
  14. <template v-slot:file="{ file }">
  15. <div class="thumbnail-group">
  16. <img v-if="isPictureFile(file)"
  17. style="width: 100%; height: 70px;border-radius:4px;"
  18. v-lazy="_getFileImg(file)" />
  19. </div>
  20. <span class="el-upload-list__item-actions"
  21. style="width: 100%; height: 70px;border-radius:4px;"
  22. v-if="file.status == 'success' && (editable || download || preview)">
  23. <span v-if="isPictureFile(file) && preview"
  24. class="el-upload-list__item-preview"
  25. @click="_handlePreview(file)">
  26. <i class="el-icon-zoom-in"></i>
  27. </span>
  28. <span v-if="download && !isPictureFile(file)"
  29. class="el-upload-list__item-delete"
  30. @click="_handleDownload(file)">
  31. <i class="el-icon-view"
  32. v-if="isOnlineFile(file)"></i>
  33. <i class="el-icon-download"
  34. v-else></i>
  35. </span>
  36. <span v-if="editable"
  37. class="el-upload-list__item-delete"
  38. @click="_handleRemove(file)">
  39. <i class="el-icon-delete"></i>
  40. </span>
  41. </span>
  42. </template>
  43. </el-upload>
  44. <el-dialog :visible.sync="dialogVisible"
  45. @closed="_handlePreviewClosed"
  46. :append-to-body="true">
  47. <img v-lazy="dialogImageUrl"
  48. width="100%" />
  49. </el-dialog>
  50. <template v-if="editableintm">
  51. <div class="trigger-group"
  52. v-if="editable">
  53. </div>
  54. <div v-if="editablely">--</div>
  55. </template>
  56. </div>
  57. </template>
  58. <script>
  59. import { getFileList, saveFiles } from '@/model/upload'
  60. import { getOssInterimCredentials } from '@/model/procurement/spare'
  61. import { uuid } from '@/utils/assist'
  62. import downloadNow from '../WsDownload/download'
  63. import { EventBus } from 'base-core-lib'
  64. import { mapActions } from 'vuex'
  65. export default {
  66. name: 'WsAutograph',
  67. props: {
  68. appendixId: String,
  69. editable: {
  70. // 是否为编辑模式
  71. type: Boolean,
  72. default: true
  73. },
  74. remarkWord: {
  75. // 是否有备注要求
  76. type: Boolean,
  77. default: true
  78. },
  79. preview: {
  80. // 是否允许预览
  81. type: Boolean,
  82. default: true
  83. },
  84. download: {
  85. // 是否允许下载
  86. type: Boolean,
  87. default: true
  88. },
  89. showName: {
  90. // 是否显示文件名
  91. type: Boolean,
  92. default: true
  93. },
  94. limit: {
  95. // 文件总数量限制
  96. type: Number,
  97. default: undefined
  98. },
  99. accept: {
  100. // 限制文件类型
  101. type: String,
  102. default:
  103. '.jpg, .jpeg, .png, .gif, .pdf, .doc, .docx, .xls, .xlsx, .zip, .rar, .eml'
  104. },
  105. ossKey: {
  106. type: String,
  107. required: true
  108. },
  109. compId: {
  110. // 公司ID
  111. type: String,
  112. required: true
  113. },
  114. vesselId: {
  115. // 船舶ID
  116. type: String,
  117. required: false
  118. },
  119. tableName: {
  120. // 要保存的表的名字
  121. type: String,
  122. required: false
  123. }
  124. },
  125. data () {
  126. return {
  127. vesselBankFlag: 'V',
  128. // vesselBankFlag:
  129. // localStorage.getItem('ws-pf_serviceTypeFlag') == 'true' ? 'B' : 'V',
  130. curFileNum: 0,
  131. tempFileList: [],
  132. initFileList: [],
  133. loading: false, // 是否正在初始化filelsit
  134. fileList: [],
  135. removeList: [],
  136. dialogImageUrl: '',
  137. dialogVisible: false,
  138. editableintm: true,
  139. editablely: false,
  140. downloadName: '',
  141. loadingView: '', // loading弹出层
  142. uploadParams: {
  143. companyId: this.compId,
  144. basePath: '' //config.getUploadPath(this.ossKey, this.compId)
  145. }
  146. }
  147. },
  148. computed: {
  149. formatTxt () {
  150. return this.accept
  151. .replace(/\./g, '')
  152. .replace(/ /g, '')
  153. .replace(/,/g, '、 ')
  154. },
  155. thisAppendixIds () {
  156. return this.appendixId
  157. }
  158. },
  159. watch: {
  160. // 监听props.appendixId, 变化时向服务器请求
  161. async thisAppendixIds (newV, oldV) {
  162. if (!this.appendixId && !this.editable) {
  163. this.editablely = true
  164. }
  165. this.getDefaultFiles(newV)
  166. }
  167. },
  168. mounted () {
  169. this.getDefaultFiles(this.appendixId)
  170. const handleChange = this.$refs.elUpload.$refs['upload-inner'].handleChange
  171. this.$refs.elUpload.$refs['upload-inner'].handleChange = ev => {
  172. this.initFileList = []
  173. this.curFileNum = ev.target.files.length
  174. for (let i = 0; i < this.curFileNum; i++) {
  175. this.initFileList.push(ev.target.files[i])
  176. this.tempFileList[i] = i
  177. }
  178. handleChange(ev)
  179. }
  180. },
  181. methods: {
  182. ...mapActions('common', ['uploadShipFiles']),
  183. // 获取图片文件
  184. _getFileImg (file) {
  185. if (!file) {
  186. return ''
  187. }
  188. if (this.isPictureFile(file)) {
  189. this.editableintm = false
  190. this.editablely = false
  191. if (file.url.indexOf('blob') === 0 || this.vesselBankFlag === 'V') {
  192. return file.url
  193. }
  194. return file.url + '?x-oss-process=image/resize,w_120,h_120'
  195. }
  196. return ''
  197. },
  198. async getDefaultFiles (appendixIds) {
  199. this.fileList = []
  200. if (appendixIds) {
  201. this.loading = true
  202. const list = await this._getFileList(appendixIds)
  203. this.loading = false
  204. if (list) {
  205. this.fileList = list.map((item, idx) => {
  206. return {
  207. appendixId: item.appendixId,
  208. name: item.appendixName,
  209. url: this.getFilePath(item.appendixPath),
  210. uid: uuid()
  211. }
  212. })
  213. this.$emit('onChange', this.fileList.length)
  214. }
  215. }
  216. },
  217. // 处理上传按钮点击
  218. handleUploadClick () {
  219. this.$refs.triggerDiv.click()
  220. },
  221. // 处理预览
  222. _handlePreview (file) {
  223. this.dialogImageUrl = file.url
  224. this.downloadName = file.name
  225. this.dialogVisible = true
  226. },
  227. // 处理预览关闭
  228. _handlePreviewClosed () {
  229. this.dialogImageUrl = ''
  230. this.downloadName = ''
  231. },
  232. // 处理删除
  233. _handleRemove (file) {
  234. if (file.appendixId) {
  235. // 这是要删除已经存在的附件
  236. const index = this.fileList.findIndex(
  237. itm => itm.appendixId === file.appendixId
  238. )
  239. this.fileList.splice(index, 1)
  240. this.removeList.push(file.appendixId)
  241. } else {
  242. // 这是删除已上传但还未保存的附件
  243. const index = this.fileList.findIndex(itm => itm.uid === file.uid)
  244. this.fileList.splice(index, 1)
  245. }
  246. this.editableintm = true
  247. this.$emit('onChange', this.fileList.length)
  248. },
  249. // 自定义上传过程
  250. _handleRequest (param) {
  251. if (this.vesselBankFlag === 'V') {
  252. //船端
  253. this._uploadFilesShip(param)
  254. }
  255. },
  256. async _uploadFilesShip (param) {
  257. this.loading = true
  258. const file = param.file
  259. try {
  260. const data = await this.uploadShipFiles({
  261. file: file,
  262. companyId: this.compId || '',
  263. modelId: this.isNull(this.tableName)
  264. ? ''
  265. : this.tableName.split('_')[0]
  266. })
  267. const item = {
  268. appendixName: data.appendixName,
  269. appendixPath: data.appendixPath,
  270. appendixSize: this.toKB(file.size),
  271. appendixType: localStorage.getItem('ws-pf_serviceTypeFlag') == 'true'
  272. ? 'B'
  273. : 'V',
  274. fromTableName: this.tableName,
  275. vesselId: this.vesselId,
  276. uid: file.uid,
  277. url: this.getFilePath(data.appendixPath),
  278. name: file.name
  279. }
  280. this.$emit('uploadSuccess', item)
  281. let fileIndex = this.getFileIndex(item.uid)
  282. if (fileIndex >= 0) {
  283. this.tempFileList.splice(fileIndex, 1, item)
  284. this._handleSuccessOnce()
  285. }
  286. } catch (error) {
  287. this.handleError()
  288. }
  289. },
  290. // 处理单个文件上传成功
  291. _handleSuccessOnce () {
  292. this.curFileNum--
  293. if (this.curFileNum === 0) {
  294. this.fileList.push.apply(
  295. this.fileList,
  296. this.tempFileList.filter(item => {
  297. return !this.isNull(item.uid)
  298. })
  299. )
  300. EventBus.$emit('success', this.$t('showMessage.saveautographOss'))
  301. this.tempFileList = []
  302. this.$emit('onChange', this.fileList.length)
  303. }
  304. },
  305. // 处理下载
  306. _handleDownload (file) {
  307. downloadNow(file.url, file.name, this.vesselBankFlag)
  308. },
  309. _handleBeforeUpload (file) {
  310. if (file.size > 15 * 1024 * 1024) {
  311. EventBus.$emit('error', this.$t('showMessage.filesCannot15m'))
  312. return false
  313. }
  314. if (this.limit) {
  315. let limitFileNum = parseInt(this.limit)
  316. let currentFileNum = this.fileList.length + this.curFileNum
  317. if (limitFileNum > 1 && currentFileNum > limitFileNum) {
  318. this._handleExceed()
  319. return false
  320. }
  321. if (limitFileNum === 1) {
  322. this.fileList = []
  323. }
  324. }
  325. return true
  326. },
  327. //处理文件上传失败
  328. _handleError () {
  329. this.curFileNum--
  330. EventBus.$emit('error', this.$t('showMessage.saveErrorOss'))
  331. },
  332. _handleExceed () {
  333. EventBus.$emit('warning', `最多只能上传${this.limit}个文件`)
  334. },
  335. // 传入appendixIds 从服务器获取文件列表信息
  336. _getFileList (appendixIds) {
  337. return new Promise(async next => {
  338. try {
  339. next(await getFileList({ appendixIds }).toPromise() || [])
  340. } catch (error) {
  341. next()
  342. }
  343. })
  344. },
  345. handleSaveBill () {
  346. return new Promise((resolve, reject) => {
  347. const oldAppendixIds = this.removeList.join()
  348. const newAppendixs = this.fileList
  349. const params = { newAppendixs, oldAppendixIds }
  350. saveFiles(params)
  351. .toPromise()
  352. .then(res => {
  353. resolve(res.join())
  354. })
  355. .catch(err => {
  356. EventBus.$emit('error', this.$t('showMessage.saveFilesError'))
  357. reject()
  358. })
  359. })
  360. },
  361. // 获取文件样式
  362. getFileClass (file) {
  363. const ext = this.getExtName(file.name)
  364. return 'ext ' + ext
  365. },
  366. // 是否为图片
  367. isPictureFile (file) {
  368. if (file && file.name) {
  369. const name = this.getExtName(file.name)
  370. return (
  371. name === 'jpg' ||
  372. name === 'png' ||
  373. name === 'jpeg' ||
  374. name === 'gif' ||
  375. name === 'webp' ||
  376. name === 'bmp'
  377. )
  378. }
  379. return false
  380. },
  381. // 是否为在线打开文件
  382. isOnlineFile (file) {
  383. if (file && file.name) {
  384. const name = this.getExtName(file.name)
  385. return name === 'pdf' || name === 'tet'
  386. }
  387. return false
  388. },
  389. getOssInter () {
  390. return new Promise(async next => {
  391. try {
  392. const data = await getOssInterimCredentials().toPromise()
  393. next(data)
  394. } catch (error) {
  395. next()
  396. }
  397. })
  398. },
  399. getFileIndex (uid) {
  400. for (let i = 0; i < this.initFileList.length; i++) {
  401. if (this.initFileList[i].uid === uid) {
  402. return i
  403. }
  404. }
  405. return -1
  406. },
  407. // 显示loading
  408. showLoading () {
  409. this.loadingView = this.$loading({
  410. lock: true,
  411. text: 'Loading',
  412. spinner: 'el-icon-loading',
  413. background: 'rgba(0, 0, 0, 0.7)'
  414. })
  415. },
  416. // 隐藏loading
  417. hideLoading () {
  418. this.loadingView.close()
  419. },
  420. // 获取后缀名
  421. getExtName (url) {
  422. if (!url) {
  423. return url
  424. }
  425. url = url.substr(url.lastIndexOf('.') + 1)
  426. if (url.indexOf('?') >= 0) {
  427. url = url.substr(url.indexOf('?'))
  428. }
  429. return url
  430. },
  431. checkUrl (path) {
  432. if (path.indexOf('http://') > -1 || path.indexOf('https://') > -1) {
  433. let pathSplit = path.split('.com/')
  434. if (pathSplit) {
  435. path = pathSplit[1]
  436. }
  437. }
  438. return path
  439. },
  440. getFilePath (path) {
  441. // if (this.vesselBankFlag === 'V') {
  442. // return (
  443. // localStorage.getItem('ws-pf_systemUrl') +
  444. // '/static/' +
  445. // this.slash(path)
  446. // )
  447. // }
  448. // return path
  449. // let sysUrl = process.env.NODE_ENV === "production"?"http://" + window.location.host+"/":"http://product-dev.winsea.com/";
  450. let sysUrl = this.$store.getters.baseInfo.fileUrl
  451. path = this.checkUrl(path)
  452. // if (this.clientFag === "B") {
  453. // // sysUrl+="pb/"
  454. // }
  455. return sysUrl + this.slash(path)
  456. },
  457. // 字节转KB
  458. toKB (bt) {
  459. return (bt / 1024).toFixed(2) + 'KB'
  460. },
  461. clearFiles () {
  462. this.fileList = []
  463. },
  464. // 反斜杠转斜杠
  465. slash (str) {
  466. return str.replace(/\\/g, '/')
  467. }
  468. }
  469. }
  470. </script>
  471. <style lang="scss" scoped>
  472. $picture-hsize: 70px;
  473. $picture-wsize: 100%;
  474. $ctrl-margin-left: 10px;
  475. .ws-autograph {
  476. width: 100%;
  477. height: 70px !important;
  478. margin-bottom: 0px;
  479. .upload-noramark-btn {
  480. padding: 0px;
  481. border: none;
  482. }
  483. .trigger-group {
  484. margin-bottom: 10px;
  485. .upload-btn {
  486. display: inline-block;
  487. border-radius: 4px;
  488. }
  489. .tip {
  490. display: inline-block;
  491. margin: 0;
  492. padding: 0;
  493. font-size: 12px;
  494. line-height: 17px;
  495. color: #999;
  496. margin-bottom: 4px;
  497. margin-top: 4px;
  498. vertical-align: middle;
  499. }
  500. }
  501. /deep/ .el-upload-list--picture-card .el-upload-list__item {
  502. margin: 0 0px 0px 0;
  503. }
  504. /deep/ .el-loading-mask {
  505. height: $picture-hsize;
  506. }
  507. /deep/ .thumbnail-group {
  508. width: $picture-wsize;
  509. height: $picture-hsize;
  510. .el-upload-list__item-thumbnail {
  511. display: block;
  512. border-radius: 6px;
  513. border: 1px solid #ccd6e3;
  514. background-color: #fff;
  515. }
  516. }
  517. /deep/ .el-upload-list__item-actions {
  518. height: $picture-hsize;
  519. overflow: hidden;
  520. border-radius: 6px;
  521. align-items: center;
  522. display: flex;
  523. justify-content: center;
  524. }
  525. .fileName {
  526. font-size: 12px;
  527. color: #666;
  528. margin: 0;
  529. text-align: center;
  530. margin-top: 4px;
  531. margin-bottom: 4px;
  532. white-space: nowrap;
  533. overflow: hidden;
  534. text-overflow: ellipsis;
  535. }
  536. /deep/
  537. .el-upload-list--picture-card
  538. .el-upload-list__item-actions
  539. span
  540. + span {
  541. margin-left: $ctrl-margin-left;
  542. }
  543. /deep/ .el-upload.el-upload--picture-card {
  544. display: none;
  545. }
  546. /deep/ .el-upload-list__item {
  547. width: $picture-wsize;
  548. height: auto;
  549. border: 0;
  550. background: none;
  551. }
  552. .ws-upload-progress {
  553. width: 84%;
  554. top: 88px;
  555. }
  556. .ext {
  557. background-size: $picture-hsize * 0.7;
  558. background-repeat: no-repeat;
  559. background-position: center;
  560. }
  561. .doc {
  562. background-image: url('~@/assets/images/common/upload/doc.png');
  563. }
  564. .docx {
  565. background-image: url('~@/assets/images/common/upload/docx.png');
  566. }
  567. .eml {
  568. background-image: url('~@/assets/images/common/upload/eml.png');
  569. }
  570. .pdf {
  571. background-image: url('~@/assets/images/common/upload/pdf.png');
  572. }
  573. .rar {
  574. background-image: url('~@/assets/images/common/upload/rar.png');
  575. }
  576. .xls {
  577. background-image: url('~@/assets/images/common/upload/xls.png');
  578. }
  579. .xlsx {
  580. background-image: url('~@/assets/images/common/upload/xlsx.png');
  581. }
  582. .zip {
  583. background-image: url('~@/assets/images/common/upload/zip.png');
  584. }
  585. }
  586. // 取消ele动画
  587. /deep/ .el-list-leave-active {
  588. transition: all 0s;
  589. }
  590. /deep/ .el-list-enter-active {
  591. transition: all 0s;
  592. }
  593. .el-button--text {
  594. color: #666666;
  595. }
  596. .el-button--text:hover {
  597. color: #4a89f1;
  598. }
  599. .el-button--text:focus {
  600. color: #ffffff;
  601. }
  602. </style>