index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. <template>
  2. <div class="file-container">
  3. <el-card class="top-box">
  4. <div slot="header" class="clearfix card-header">
  5. <span>排行榜</span>
  6. </div>
  7. <el-scrollbar class="top-scrollbar">
  8. <div v-loading="topLoading" class="top-item-box">
  9. <div v-for="(item, index) in topData" :key="item.id" class="top-item">
  10. <div class="item-default">
  11. <div :class="['top-rank', index<=2 ? 'top-rank_'+index : '']">{{ index + 1 }}</div>
  12. <div>
  13. <!-- <div class="top-name">{{ item.fileName }}</div> -->
  14. <ellipsis-tooltip :content="item.fileName" placement="top" class="top-name" width="270px">
  15. <a style="cursor: text">{{ item.fileName }}</a>
  16. </ellipsis-tooltip>
  17. <div>
  18. <span class="top-size">{{ formatBytes(item.size) }}</span>
  19. <span class="top-count">{{ formatCount(item.downloads) }}次下载</span>
  20. </div>
  21. </div>
  22. <el-button type="primary" size="small" style="width:56px">下载</el-button>
  23. </div>
  24. </div>
  25. </div>
  26. </el-scrollbar>
  27. </el-card>
  28. <el-card class="file-box">
  29. <div slot="header" class="clearfix card-header">
  30. <span>软件下载</span>
  31. <el-button type="primary" size="small" class="header-upload-btn" @click="showUpload">上传</el-button>
  32. </div>
  33. <div class="list-filter">
  34. <el-form ref="filterForm" :model="formData" inline>
  35. <el-form-item label="名称">
  36. <el-input v-model="formData.fileName" class="filter-item" clearable />
  37. </el-form-item>
  38. <el-form-item>
  39. <el-button type="primary" @click="searchTable">查询</el-button>
  40. </el-form-item>
  41. </el-form>
  42. </div>
  43. <el-tabs v-model="curTab" class="list-tabs" @tab-click="getTablelist">
  44. <el-tab-pane v-for="typeItem in typeData" :key="dictType + '-' + typeItem.id" :label="typeItem.label" :name="typeItem.id + ''">
  45. <div v-loading="loading" class="list-box">
  46. <el-scrollbar class="list-scrollbar">
  47. <div class="list-item-box">
  48. <div v-for="item in tableData" :key="item.id" class="list-item">
  49. <div class="item-content">
  50. <!-- <el-avatar :size="56" :src="item.icon | formatImgUrl" class="item-icon" /> -->
  51. <div class="item-title">
  52. <div class="item-name">{{ item.fileName }}</div>
  53. <div class="item-system">上传时间:{{ item.createTime }}</div>
  54. </div>
  55. </div>
  56. <div class="item-tip">
  57. <span>大小:{{ formatBytes(item.size) }}</span>
  58. <span>下载量:{{ formatCount(item.downloads) }}次</span>
  59. </div>
  60. <div class="item-footer">
  61. <el-button type="text" size="small" @click="downloadItem(item)">下载</el-button>
  62. <el-button type="text" size="small" @click="deleteItem(item.id)">删除</el-button>
  63. </div>
  64. </div>
  65. </div>
  66. </el-scrollbar>
  67. <div v-if="total > 0" class="page">
  68. <el-pagination
  69. layout="total, sizes, prev, pager, next, jumper"
  70. :current-page="current"
  71. :total="total"
  72. :page-sizes="pageSizeAll"
  73. :page-size="size"
  74. @size-change="handleSizeChange"
  75. @current-change="handleCurrentChange"
  76. />
  77. </div>
  78. </div>
  79. </el-tab-pane>
  80. </el-tabs>
  81. </el-card>
  82. <el-dialog
  83. v-loading="uploadLoading"
  84. :visible.sync="visible"
  85. :close-on-press-escape="true"
  86. :close-on-click-modal="false"
  87. :show-close="false"
  88. destroy-on-close
  89. custom-class="main-edit-dialog file-upload-dialog"
  90. title="软件上传"
  91. >
  92. <el-form :ref="formName" :model="formData" :rules="rules" label-width="100px">
  93. <el-form-item prop="type" label="软件类型">
  94. <el-select v-model="formData.type" placeholder="请选择">
  95. <el-option v-for="item in typeData" :key="item.id" :label="item.label" :value="item.id" />
  96. </el-select>
  97. </el-form-item>
  98. <el-form-item prop="fileName" label="上传软件" style="height: 100px;">
  99. <upload-file ref="uploadFile" :file-type="formData.type" :upload-change="uploadChange" :auto-upload="false" :on-success="uploadSuccess" :on-error="uploadError" />
  100. <div>{{ formData.fileName }}</div>
  101. </el-form-item>
  102. </el-form>
  103. <div slot="footer">
  104. <el-button @click="close">取消</el-button>
  105. <el-button type="primary" @click="saveFile">确定</el-button>
  106. </div>
  107. </el-dialog>
  108. </div>
  109. </template>
  110. <script>
  111. import { fetchDictData } from '@/api/dict'
  112. import { fetchFileList, fetchTopN, pushDeleteFile } from '@/api/file'
  113. import { hasValidRecords, formatDictData, isNull } from '@/utils/convert'
  114. import { formatBytes, formatCount } from '@/utils'
  115. import EllipsisTooltip from '@/components/EllipsisTooltip'
  116. import UploadFile from '@/components/Upload/UploadFile.vue'
  117. export default {
  118. name: 'Application',
  119. components: {
  120. EllipsisTooltip,
  121. UploadFile
  122. },
  123. data() {
  124. return {
  125. // type
  126. dictType: 'software_type',
  127. typeData: [],
  128. // top N
  129. topN: 50,
  130. topData: [],
  131. // table
  132. current: 1,
  133. size: 16,
  134. total: 0,
  135. pageSizeAll: [10, 16, 20, 50, 100, 200, 500],
  136. tableData: [],
  137. // filter
  138. formData: {
  139. type: null,
  140. fileName: ''
  141. },
  142. rules: {
  143. type: [
  144. { required: true, message: '请选择软件类型', trigger: 'change' }
  145. ],
  146. fileName: [
  147. { required: true, message: '请选择软件', trigger: 'change' }
  148. ]
  149. },
  150. // dialog
  151. visible: false,
  152. formName: 'uploadForm',
  153. // others
  154. curTab: '',
  155. loading: false,
  156. topLoading: false,
  157. uploadLoading: false
  158. }
  159. },
  160. created() {
  161. this.getTypeData()
  162. this.searchTable()
  163. },
  164. methods: {
  165. // 改变每页显示条数
  166. handleSizeChange(val) {
  167. this.current = 1
  168. this.size = val
  169. this.getTablelist()
  170. },
  171. // 切换第几页
  172. handleCurrentChange(val) {
  173. this.current = val
  174. this.getTablelist()
  175. },
  176. // 重置搜索项
  177. resetTable(formName) {
  178. this.$refs[formName].resetFields()
  179. this.getTablelist()
  180. },
  181. // 点击搜索按钮
  182. searchTable() {
  183. this.current = 1
  184. this.getTablelist()
  185. },
  186. // 获取table数据
  187. getTablelist() {
  188. this.formData.type = parseInt(this.curTab)
  189. const params = {
  190. page: this.current,
  191. size: this.size,
  192. params: {
  193. delFlag: 0,
  194. type: this.curTab,
  195. fileName: this.formData.fileName
  196. }
  197. }
  198. this.loading = true
  199. fetchFileList(params).then(response => {
  200. if (hasValidRecords(response)) {
  201. this.tableData = response.data.records
  202. this.total = response.data.total
  203. } else {
  204. this.tableData = []
  205. this.total = 0
  206. }
  207. this.loading = false
  208. }).catch(error => {
  209. console.log(error)
  210. this.loading = false
  211. this.$message.error({
  212. type: 'error',
  213. duration: 0,
  214. showClose: true,
  215. message: ': ' + error.message
  216. })
  217. })
  218. },
  219. downloadItem(item) {
  220. window.open(item.url, '_blank')
  221. item.downloads++
  222. },
  223. deleteItem(id) {
  224. this.loading = true
  225. pushDeleteFile(id).then(res => {
  226. this.getTablelist()
  227. }).catch(error => {
  228. console.log(error)
  229. this.loading = false
  230. this.$message.error({
  231. type: 'error',
  232. duration: 0,
  233. showClose: true,
  234. message: '删除软件出错: ' + error.message
  235. })
  236. })
  237. },
  238. getTopNData() {
  239. const typeIds = this.typeData.map(item => item.id).join(',')
  240. fetchTopN(typeIds, this.topN).then(res => {
  241. if (!isNull(res.data)) {
  242. this.topData = res.data
  243. } else {
  244. this.topData = []
  245. }
  246. this.topLoading = false
  247. }).catch(error => {
  248. console.log(error)
  249. this.topLoading = false
  250. this.$message.error({
  251. type: 'error',
  252. duration: 0,
  253. showClose: true,
  254. message: ': ' + error.message
  255. })
  256. })
  257. },
  258. getTypeData() {
  259. this.topLoading = true
  260. fetchDictData(this.dictType).then(response => {
  261. if (!isNull(response.data)) {
  262. this.typeData = response.data
  263. this.curTab = this.typeData[0].id + ''
  264. this.formData.type = parseInt(this.curTab)
  265. this.getTopNData()
  266. this.searchTable()
  267. } else {
  268. this.typeData = []
  269. }
  270. }).catch(error => {
  271. console.log(error)
  272. this.topLoading = false
  273. this.$message.error({
  274. type: 'error',
  275. duration: 0,
  276. showClose: true,
  277. message: ': ' + error.message
  278. })
  279. })
  280. },
  281. // dialog
  282. showUpload() {
  283. this.visible = true
  284. },
  285. close() {
  286. this.visible = false
  287. },
  288. // Upload
  289. uploadSuccess() {
  290. this.uploadLoading = false
  291. this.formData.fileName = ''
  292. this.visible = false
  293. this.searchTable()
  294. },
  295. uploadError(error) {
  296. this.uploadLoading = false
  297. this.$message.error({
  298. type: 'error',
  299. duration: 0,
  300. showClose: true,
  301. message: '上传软件出错:' + error.message
  302. })
  303. },
  304. uploadChange(file) {
  305. if (file.status === 'ready') {
  306. this.formData.fileName = file.name
  307. }
  308. },
  309. saveFile() {
  310. this.$refs[this.formName].validate((valid) => {
  311. if (valid) {
  312. this.uploadLoading = true
  313. this.$refs['uploadFile'].submit()
  314. }
  315. })
  316. },
  317. formatDictData(type) {
  318. return formatDictData(this.typeData, type)
  319. },
  320. formatBytes(size) {
  321. return formatBytes(size)
  322. },
  323. formatCount(count) {
  324. return formatCount(count)
  325. }
  326. }
  327. }
  328. </script>
  329. <style lang="scss" scoped>
  330. .file-container {
  331. display: flex;
  332. ::v-deep {
  333. .el-card__header {
  334. height: 56px;
  335. padding: 12px 20px;
  336. }
  337. .el-card__body {
  338. height: calc(100% - 56px);
  339. padding: 10px 0px 10px 20px;
  340. }
  341. .el-scrollbar__wrap{
  342. overflow-x: hidden;
  343. }
  344. .el-scrollbar__bar.is-horizontal {
  345. display: none;
  346. }
  347. }
  348. .card-header {
  349. font-size: 18px;
  350. line-height: 32px;
  351. height: 32px;
  352. color: rgba(0,0,0,0.85);
  353. font-weight: bold;
  354. }
  355. .header-upload-btn {
  356. float: right;
  357. height: 32px;
  358. font-size: 14px;
  359. }
  360. .top-box {
  361. width: 400px;
  362. min-width: 400px;
  363. height: 100%;
  364. margin-right: 5px;
  365. }
  366. .top-scrollbar {
  367. height: 100%;
  368. }
  369. .top-item-box {
  370. margin-right: 10px;
  371. }
  372. .item-default {
  373. height: 50px;
  374. display: flex;
  375. // justify-content: space-between;
  376. align-items: center;
  377. border-bottom: 1px solid rgba(0,0,0,0.1);
  378. color: rgba(0,0,0,0.85);
  379. &>div {
  380. display: inline-block;
  381. margin-right: 10px;
  382. }
  383. .top-rank {
  384. width: 20px;
  385. text-align: center;
  386. }
  387. .top-rank_0 {
  388. background-color: #F63842;
  389. border-radius: 1px;
  390. color: #f0f0f0;
  391. }
  392. .top-rank_1 {
  393. background-color: #FB982D;
  394. border-radius: 1px;
  395. color: #f0f0f0;
  396. }
  397. .top-rank_2 {
  398. background-color: #FADB14;
  399. border-radius: 1px;
  400. color: #8e8686;
  401. }
  402. .top-name {
  403. width: 270px;
  404. margin-bottom: 5px;
  405. }
  406. .top-size,.top-count{
  407. color: rgba(0,0,0,0.45);
  408. font-size: 14px;
  409. }
  410. .top-size {
  411. display: inline-block;
  412. padding-right: 5px;
  413. border-right: 1px solid rgba(0,0,0,0.1);
  414. margin-right: 5px;
  415. }
  416. }
  417. .file-box {
  418. background-color: #fff;
  419. width: 100%;
  420. height: 100%;
  421. min-width: 875px;
  422. .upload-btn {
  423. display: inline-block;
  424. margin-left: 10px;
  425. }
  426. .page {
  427. margin-right: 20px;
  428. text-align: right;
  429. }
  430. }
  431. .list-filter {
  432. height: 60px;
  433. }
  434. .list-tabs {
  435. height: calc(100% - 60px);
  436. ::v-deep {
  437. .el-tabs__content {
  438. height: calc(100% - 55px);
  439. }
  440. .el-tab-pane {
  441. height: 100%;
  442. }
  443. }
  444. }
  445. .list-box {
  446. height: 100%;
  447. }
  448. .list-scrollbar {
  449. height: calc(100% - 32px);
  450. }
  451. .list-item-box {
  452. display: flex;
  453. flex-wrap: wrap;
  454. }
  455. .list-item {
  456. width: 325px;
  457. height: 150px;
  458. border: 1px solid rgba(0,0,0,0.09);
  459. border-radius: 4px;
  460. margin-right: 16px;
  461. margin-bottom: 16px;
  462. padding: 10px 10px 0 10px;
  463. .item-content {
  464. display: flex;
  465. align-items: center;
  466. height: calc(100% - 72px);
  467. }
  468. .item-icon {
  469. margin-right: 10px;
  470. }
  471. .item-title {
  472. .item-name, .item-system {
  473. width: 240px;
  474. text-overflow: ellipsis;
  475. overflow: hidden;
  476. white-space: nowrap;
  477. }
  478. .item-name {
  479. font-size: 15px;
  480. font-weight: 550;
  481. color: rgba(0,0,0,0.85);
  482. }
  483. .item-system {
  484. font-size: 14px;
  485. color: rgba(0,0,0,0.65);
  486. margin-top: 5px;
  487. }
  488. }
  489. .item-tip {
  490. height: 32px;
  491. color: rgba(0,0,0,0.65);
  492. font-size: 14px;
  493. &>span{
  494. display: inline-block;
  495. line-height: 32px;
  496. width: 150px;
  497. }
  498. }
  499. .item-footer {
  500. height: 34px;
  501. width: 100%;
  502. color: rgba(0,0,0,0.85);
  503. border-top: 1px solid rgba(0,0,0,0.10);
  504. text-align: center;
  505. .el-button {
  506. width: calc(50% - 5px);
  507. color: rgba(0,0,0,0.85);
  508. font-size: 14px;
  509. }
  510. .el-button+.el-button {
  511. border-left: 1px solid rgba(0,0,0,0.10);
  512. }
  513. }
  514. }
  515. }
  516. </style>