index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. <template>
  2. <div class="upload">
  3. <!-- <div class="upload-wrapper">
  4. <header class="upload-header">
  5. <div class="manageTitle">{{ title }}</div>
  6. <el-upload ref="upload" action="#" multiple :loading="exceed" :accept="acceptTypesStr" :show-file-list="false" :http-request="uploadHandler" :before-upload="beforeUpload">
  7. <el-button slot="trigger" type="primary" size="small">
  8. 上传文件
  9. </el-button>
  10. </el-upload>
  11. </header>
  12. <main class="upload-main">
  13. <ul v-if="totalProgressList.length" class="upload-list">
  14. <li v-for="(item, index) in totalProgressList" :key="item.key" class="upload-list-item">
  15. <div class="upload-list-item-wrapper">
  16. <div class="upload-list-item-image">
  17. <img src="@/assets/nav/ic_ex.png" :alt="item.fileName" />
  18. </div>
  19. <div class="upload-list-item-details">
  20. <div>
  21. <span class="upload-list-item-name">{{ item.fileName }}</span>
  22. <span class="upload-list-item-state" :class="stateClass(item.state)">{{ stateFormat(item.state) }}</span>
  23. <i v-show="item.state === 2" class="upload-list-item-retry el-icon-refresh-right" @click="retry(item)" />
  24. </div>
  25. <div>
  26. <span class="upload-list-item-time">{{ item.time }}</span>
  27. </div>
  28. </div>
  29. </div>
  30. <el-divider v-if="index !== totalProgressList.length - 1" />
  31. </li>
  32. </ul>
  33. <NoData v-else :image-width="230" :image-height="160" />
  34. </main>
  35. </div> -->
  36. <div class="upload-wrapper">
  37. <header class="upload-header">
  38. <div class="flex">
  39. <div class="manageTitle">{{ title2 }}</div>
  40. <div>
  41. <el-date-picker v-model="input" size="small" value-format="yyyy-MM-dd" type="date" placeholder="选择日期">
  42. </el-date-picker>
  43. <el-button class="btn-refresh" type="primary" icon="el-icon-refresh" @click="getStateData" />
  44. </div>
  45. </div>
  46. <div class="upload-header-right">
  47. <!-- <el-date-picker v-model="flightDate" size="small" type="daterange" value-format="yyyy-MM-dd" start-placeholder="开始日期" end-placeholder="结束日期" :picker-options="dateRangePickerOptions" :clearable="false" />
  48. <el-button class="btn-refresh" type="primary" icon="el-icon-refresh" @click="getStateData" /> -->
  49. <el-upload ref="upload" action="#" multiple :loading="exceed" :accept="acceptTypesStr" :show-file-list="false" :http-request="uploadHandler" :before-upload="beforeUpload">
  50. <el-button slot="trigger" type="primary" size="small">
  51. 上传文件
  52. </el-button>
  53. </el-upload>
  54. </div>
  55. </header>
  56. <main class="upload-main">
  57. <div class="upload-main-content flex-wrap">
  58. <div v-loading="loading1" element-loading-text="拼命加载中" element-loading-spinner="el-icon-loading" element-loading-background="rgba(0, 0, 0, 0.8)" class="upload-main-content-left">
  59. <el-table :data="capData" :highlight-current-row="true" @cell-click="stateClick" border stripe fit height="100%" class="state-table">
  60. <el-table-column label="日期">
  61. <template slot-scope="scope">
  62. <div>{{ currDate(scope.row.thedate) }}</div>
  63. </template>
  64. </el-table-column>
  65. <el-table-column width="100" prop="total" label="条数"></el-table-column>
  66. </el-table>
  67. </div>
  68. <div v-loading="loading" element-loading-text="拼命加载中" element-loading-spinner="el-icon-loading" element-loading-background="rgba(0, 0, 0, 0.8)" class="upload-main-content-right">
  69. <el-table :data="tableData" border stripe fit height="100%" class="upload-table" :cell-class-name="cellClass">
  70. <el-table-column v-for="column in tableColumns" :key="column.key" :prop="column.prop" :label="column.prop">
  71. <template slot-scope="scope">
  72. <el-tooltip v-if="column.showTooltip" class="item" effect="dark" placement="top-start">
  73. <div slot="content" class="tooltip-content">
  74. {{
  75. formatter(
  76. scope.row,
  77. scope.column,
  78. scope.row[scope.column.property]
  79. )
  80. }}
  81. </div>
  82. <div class="tooltip-trigger">
  83. {{
  84. formatter(
  85. scope.row,
  86. scope.column,
  87. scope.row[scope.column.property]
  88. )
  89. }}
  90. </div>
  91. </el-tooltip>
  92. <span v-else>
  93. {{
  94. formatter(
  95. scope.row,
  96. scope.column,
  97. scope.row[scope.column.property]
  98. )
  99. }}
  100. </span>
  101. </template>
  102. </el-table-column>
  103. <template #empty>
  104. <NoData :image-width="230" :image-height="160" />
  105. </template>
  106. </el-table>
  107. </div>
  108. </div>
  109. </main>
  110. </div>
  111. </div>
  112. </template>
  113. <script>
  114. import { parseTime } from '@/utils'
  115. import { UploadFile, WhatQuery } from '@/api/dataIntegration'
  116. import NoData from '@/components/nodata/index.vue'
  117. const acceptTypes = ['xlsx', 'xls']
  118. const acceptTypesStr = acceptTypes.reduce((prevStr, currStr) => {
  119. return `${prevStr}${prevStr ? ',' : ''}.${currStr}`
  120. }, '')
  121. const units = ['B', 'KB', 'MB', 'GB']
  122. const getUnit = (size, callTime = 0) => {
  123. if (size < 1024) {
  124. return `${size}${units[callTime]}`
  125. }
  126. return getUnit(size / 1024, callTime + 1)
  127. }
  128. const maxSize = 20 * 1024 * 1024
  129. const short = getUnit(maxSize)
  130. export default {
  131. name: 'Upload',
  132. components: { NoData },
  133. data () {
  134. return {
  135. title: '速运行李上传',
  136. acceptTypesStr: acceptTypesStr,
  137. totalProgressList: [],
  138. limit: 3,
  139. title2: '速运行李数据',
  140. flightDate: new Array(2).fill(parseTime(new Date(), '{y}-{m}-{d}')),
  141. dateRangePickerOptions: {
  142. onPick: this.dateRangePickHandler,
  143. disabledDate: this.dateRangeDisabled,
  144. },
  145. input: '',
  146. tableColumns: [
  147. {
  148. prop: 'C0',
  149. },
  150. {
  151. prop: 'C1',
  152. },
  153. {
  154. prop: 'C2',
  155. },
  156. {
  157. prop: 'C3',
  158. },
  159. {
  160. prop: 'C4',
  161. },
  162. {
  163. prop: 'C5',
  164. },
  165. {
  166. prop: 'C6',
  167. },
  168. {
  169. prop: 'C7',
  170. },
  171. {
  172. prop: 'C8',
  173. },
  174. {
  175. prop: 'C9',
  176. },
  177. {
  178. prop: 'createtime',
  179. },
  180. {
  181. prop: 'message',
  182. showTooltip: true,
  183. },
  184. {
  185. prop: 'sendResult',
  186. showTooltip: true,
  187. },
  188. ],
  189. tableData: [],
  190. stateData: [],
  191. loading: false,
  192. loading1: false
  193. }
  194. },
  195. computed: {
  196. currentProgressNum () {
  197. return this.totalProgressList.reduce((prevCount, currentProgress) => {
  198. return currentProgress.state > 0 ? prevCount : prevCount + 1
  199. }, 0)
  200. },
  201. exceed () {
  202. return this.currentProgressNum >= this.limit
  203. },
  204. currDate () {
  205. return function (time) {
  206. if (time) {
  207. return parseTime(new Date(time), '{y}-{m}-{d} {h}:{m}:{s}')
  208. } else {
  209. return '-'
  210. }
  211. }
  212. },
  213. capData () {
  214. const val = this.input
  215. return this.stateData.filter(data => !val || data.thedate && data.thedate.includes(val))
  216. }
  217. },
  218. // watch: {
  219. // flightDate: {
  220. // handler () {
  221. // this.getTableData()
  222. // },
  223. // deep: true,
  224. // immediate: true,
  225. // },
  226. // },
  227. created () {
  228. this.getStateData()
  229. },
  230. methods: {
  231. beforeUpload (file) {
  232. const progress = this.getProgress(file)
  233. if (progress && progress.state === 0) {
  234. this.$message.warning(`${file.name} 上传中,请勿重复上传`)
  235. return
  236. }
  237. if (this.exceed) {
  238. this.$message.warning(
  239. `${file.name} 上传出错:超出最大同时上传数量,最多同时上传 ${this.limit} 个文件`
  240. )
  241. }
  242. const extensionName = file.name.split('.').pop()
  243. const acceptExtention = acceptTypes.includes(extensionName)
  244. if (!acceptExtention) {
  245. this.$message.warning(
  246. `${file.name} 上传出错:上传文件只能是 ${acceptTypes.join('/')} 格式`
  247. )
  248. return false
  249. }
  250. if (file.size > maxSize) {
  251. this.$message.warning(
  252. `${file.name} 上传出错:上传文件大小不能超过 ${short}`
  253. )
  254. return false
  255. }
  256. },
  257. uploadHandler ({ file }) {
  258. if (!this.exceed) {
  259. this.uploadFile(file)
  260. }
  261. const progress = this.getProgress(file)
  262. if (progress) {
  263. progress.state = this.exceed ? 2 : 0
  264. return
  265. }
  266. const newProgress = {
  267. file,
  268. key: `${file.name}${file.lastModified}`,
  269. fileName: file.name,
  270. state: this.exceed ? 2 : 0,
  271. time: parseTime(Date.now(), '{y}-{m}-{d} {h}:{i}:{s}'),
  272. }
  273. this.totalProgressList.push(newProgress)
  274. },
  275. stateClass (state) {
  276. const classMap = ['pending', 'success', 'failure']
  277. return `upload-list-item-state-${classMap[state]}`
  278. },
  279. stateFormat (state) {
  280. const textMap = ['上传中···', '上传成功', '上传失败']
  281. return textMap[state]
  282. },
  283. setState (file, state) {
  284. const progress = this.getProgress(file)
  285. if (progress) {
  286. progress.state = state
  287. }
  288. },
  289. getProgress (file) {
  290. const key = `${file.name}${file.lastModified}`
  291. return this.totalProgressList.find(progress => progress.key === key)
  292. },
  293. retry (progress) {
  294. if (this.exceed) {
  295. this.$message.warning('已达到最大同时上传数量,请稍后再试')
  296. return
  297. }
  298. this.uploadFile(progress.file)
  299. progress.state = 0
  300. },
  301. async uploadFile (file) {
  302. try {
  303. const formData = new FormData()
  304. formData.append('file', file)
  305. formData.append('serviceId', SERVICE_ID.expressTransportationUpload)
  306. formData.append('token', '167885843630200099999999')
  307. const { code } = await UploadFile(formData)
  308. if (Number(code) !== 0) {
  309. throw new Error('失败')
  310. }
  311. this.$message.success(`${file.name} 上传成功`)
  312. this.setState(file, 1)
  313. this.getStateData()
  314. } catch (error) {
  315. this.$message.error(`${file.name} 上传失败`)
  316. this.setState(file, 2)
  317. }
  318. },
  319. dateRangePickHandler ({ maxDate, minDate }) {
  320. if (!maxDate) {
  321. this.pickedDate = minDate
  322. } else {
  323. this.pickedDate = null
  324. }
  325. },
  326. dateRangeDisabled (date) {
  327. return this.pickedDate
  328. ? Math.abs(date - this.pickedDate) > 2 * 24 * 60 * 60 * 1000
  329. : false
  330. },
  331. cellClass ({ row, column, rowIndex, columnIndex }) {
  332. const classes = []
  333. if (column.property === 'createtime') {
  334. classes.push('pre-line')
  335. }
  336. return classes.join(' ')
  337. },
  338. formatter (row, column, cellValue) {
  339. const value = String(cellValue ?? '').trim()
  340. switch (column.property) {
  341. case 'createtime':
  342. return value.replace('T', '\n')
  343. default:
  344. return cellValue
  345. }
  346. },
  347. async getTableData (cid = null) {
  348. this.loading = true
  349. try {
  350. // const dataContent = [this.flightDate[0], this.flightDate[1]]
  351. const dataContent = [
  352. `${this.flightDate[0]} 00:00:00`,
  353. `${this.flightDate[1]} 23:59:59`,
  354. ]
  355. const {
  356. code,
  357. returnData: { listValues },
  358. } = await WhatQuery({
  359. id: SERVICE_ID.expressTransportation,
  360. dataContent: cid ? [cid] : dataContent,
  361. })
  362. if (Number(code) !== 0) {
  363. throw new Error('失败')
  364. }
  365. this.tableData = listValues
  366. } catch (error) {
  367. this.$message.error('查询表格失败')
  368. }
  369. this.loading = false
  370. },
  371. async getStateData () {
  372. this.loading1 = true
  373. try {
  374. const {
  375. code,
  376. returnData: { listValues },
  377. } = await WhatQuery({
  378. id: SERVICE_ID.expressTransportationNum,
  379. dataContent: [],
  380. })
  381. if (Number(code) !== 0) {
  382. throw new Error('失败')
  383. }
  384. this.stateData = listValues
  385. } catch (error) {
  386. this.$message.error('查询表格失败')
  387. }
  388. this.loading1 = false
  389. },
  390. stateClick (row) {
  391. const { dataObjectId } = row
  392. if (dataObjectId) {
  393. this.getTableData(dataObjectId)
  394. }
  395. },
  396. },
  397. }
  398. </script>
  399. <style lang="scss" scoped>
  400. .upload {
  401. width: 100%;
  402. height: calc(100vh - 100px);
  403. padding: 24px 24px 0;
  404. &-wrapper {
  405. width: 100%;
  406. padding: 24px 24px 0;
  407. background-color: #fff;
  408. height: 100%;
  409. }
  410. &-header {
  411. display: flex;
  412. justify-content: space-between;
  413. }
  414. &-main {
  415. height: calc(100% - 32px);
  416. padding-top: 30px;
  417. &-content {
  418. height: 100%;
  419. padding-bottom: 32px;
  420. &-left {
  421. width: 330px;
  422. margin-right: 20px;
  423. ::v-deep .state-table {
  424. .el-table__header {
  425. .cell {
  426. font-weight: bold;
  427. color: #101116;
  428. white-space: nowrap;
  429. }
  430. }
  431. .cell {
  432. text-align: center;
  433. }
  434. }
  435. }
  436. &-right {
  437. flex: 1;
  438. width: calc(100% - 350px);
  439. }
  440. }
  441. }
  442. &-list {
  443. margin: 0;
  444. padding: 0;
  445. width: 100%;
  446. height: 100%;
  447. overflow-x: hidden;
  448. overflow-y: auto;
  449. &-item {
  450. &-wrapper {
  451. display: flex;
  452. justify-content: flex-start;
  453. align-items: center;
  454. }
  455. &-image {
  456. margin-right: 16px;
  457. }
  458. &-details {
  459. font-size: 14px;
  460. font-family: Helvetica, "Microsoft YaHei";
  461. > div {
  462. height: 16px;
  463. line-height: 16px;
  464. &:first-child {
  465. margin-bottom: 6px;
  466. }
  467. }
  468. }
  469. &-name {
  470. color: #303133;
  471. margin-right: 16px;
  472. }
  473. &-state {
  474. &-success {
  475. color: #40a349;
  476. }
  477. &-failure {
  478. color: #d53e3e;
  479. }
  480. }
  481. &-retry {
  482. width: 16px;
  483. height: 16px;
  484. text-align: center;
  485. line-height: 16px;
  486. border-radius: 50%;
  487. margin-left: 16px;
  488. font-size: 14px;
  489. color: #2d67e3;
  490. cursor: pointer;
  491. &:hover {
  492. background-color: #2d67e3;
  493. color: #fff;
  494. }
  495. }
  496. &-time {
  497. color: #afb4bf;
  498. }
  499. }
  500. }
  501. ::v-deep &-table {
  502. width: 100%;
  503. .cell {
  504. padding: 0;
  505. text-align: center;
  506. font-size: 14px;
  507. font-family: Helvetica, "Microsoft YaHei";
  508. letter-spacing: 0;
  509. }
  510. .pre-line .cell {
  511. white-space: pre-line;
  512. }
  513. .el-table__header-wrapper,
  514. .el-table__fixed-header-wrapper {
  515. .cell {
  516. font-weight: bold;
  517. color: #101116;
  518. white-space: nowrap;
  519. }
  520. }
  521. }
  522. }
  523. .upload-header-right {
  524. display: flex;
  525. align-items: center;
  526. }
  527. .btn-refresh {
  528. margin-left: 10px;
  529. padding: 0;
  530. width: 30px;
  531. height: 30px;
  532. }
  533. .tooltip-content {
  534. max-width: 500px;
  535. }
  536. .tooltip-trigger {
  537. padding: 0 10px;
  538. white-space: nowrap;
  539. text-overflow: ellipsis;
  540. overflow: hidden;
  541. }
  542. </style>