index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652
  1. <template>
  2. <a-card>
  3. <!-- 搜索和操作区域 -->
  4. <a-row :gutter="16" style="margin-bottom: 16px">
  5. <a-col :span="18">
  6. <a-input v-model:value="formState.fileName" placeholder="请输入资源名称" style="width: 150px" />
  7. <a-button type="primary" style="margin-left: 8px" @click="handleSearch">查询</a-button>
  8. <a-button style="margin-left: 8px" @click="handleReset">重置</a-button>
  9. </a-col>
  10. </a-row>
  11. <!-- 表格 -->
  12. <a-table
  13. :columns="currentColumns"
  14. :data-source="dataSource"
  15. :pagination="false"
  16. :loading="loading"
  17. bordered
  18. :row-key="(record) => record.id"
  19. :row-selection="rowSelection"
  20. >
  21. <template #bodyCell="{ column, text, record }">
  22. <template
  23. v-if="
  24. ['fileName', 'collegeIdName', 'majorIdName', 'resourceTypeName', 'suffix', 'uploadTime'].includes(
  25. column.dataIndex
  26. )
  27. "
  28. >
  29. <div class="multiLine-ellipsis" :title="text">{{ text || '-' }}</div>
  30. </template>
  31. <!-- 状态列 -->
  32. <template v-if="column.key === 'verifyStatus'">
  33. <span v-if="record.verifyStatus === '0'">
  34. <a-badge status="processing" text="未发布" />
  35. </span>
  36. <span v-else-if="record.verifyStatus === 'uploaded'">
  37. <a-badge status="success" text="已上传" />
  38. </span>
  39. <span v-else-if="record.verifyStatus === '1'">
  40. <a-badge status="default" text="待审核" />
  41. </span>
  42. <span v-else-if="record.verifyStatus === '2'">
  43. <a-badge status="success" text="已发布" />
  44. </span>
  45. <span v-else-if="record.verifyStatus === '4'">
  46. <a-badge status="error" text="已删除" />
  47. </span>
  48. </template>
  49. <template v-if="column.dataIndex === 'fileUrl'">
  50. <!-- 动态图标 + 格式提示 -->
  51. <a-tooltip :title="`${record.suffix || '未知'}`">
  52. <component
  53. :is="fileTypeIcons[record.suffix?.toLowerCase()] || fileTypeIcons['*']"
  54. :style="{ fontSize: '24px', color: getIconColor(record.suffix) }"
  55. />
  56. </a-tooltip>
  57. </template>
  58. <!-- 操作列 -->
  59. <template v-else-if="column.key === 'action'">
  60. <div class="editable-cell">
  61. <a @click="handlePublish(record)">查看</a>
  62. </div>
  63. </template>
  64. </template>
  65. </a-table>
  66. <div class="dis-flex-sb margin-top">
  67. <div>
  68. <CustomPagination
  69. :total="pagination.total"
  70. :current="pagination.pageNum"
  71. :pageSize="pagination.pageSize"
  72. :showQuickJumper="true"
  73. :showSizeChanger="true"
  74. :showTotal="(total) => `共 ${total} 条数据`"
  75. @change="handlePageChange"
  76. @showSizeChange="handlePageSizeChange"
  77. />
  78. </div>
  79. </div>
  80. <!-- 权限树模态框 -->
  81. <permissionTree v-if="permissionTreeVisible" @close="permissionTreeVisible = false"></permissionTree>
  82. <!-- 审核播放模态框 -->
  83. <auditModal
  84. v-if="auditModalVisible"
  85. :recordData="publishedData"
  86. :isAudit="auditState"
  87. @confirm="auditConfirm"
  88. @close="auditModalVisible = false"
  89. ></auditModal>
  90. <!-- 资源上传模态框 -->
  91. <resourceUpload
  92. v-if="uploadModalVisible"
  93. :isState="isState"
  94. :resourcesId="editResourcesId"
  95. @close="uploadModalVisible = false"
  96. @getList="getList"
  97. ></resourceUpload>
  98. <!-- 发布模态框 -->
  99. <releaseModal v-if="releaseVisible" @close="releaseVisible = false" @confirm="releaseConfirm"></releaseModal>
  100. </a-card>
  101. </template>
  102. <script setup>
  103. import { useRouter, useRoute } from 'vue-router'
  104. import { ref, onMounted } from 'vue'
  105. import { DownOutlined } from '@ant-design/icons-vue'
  106. import myFavoritesApi from '@/api/myFavorites'
  107. import tool from '@/utils/tool'
  108. import {
  109. FileOutlined,
  110. FileImageOutlined,
  111. FilePdfOutlined,
  112. FileWordOutlined,
  113. FileExcelOutlined,
  114. FilePptOutlined,
  115. FileTextOutlined,
  116. FileZipOutlined,
  117. PlaySquareOutlined
  118. } from '@ant-design/icons-vue'
  119. // eslint-disable-next-line vue/no-setup-props-destructure
  120. const { pageType } = defineProps({
  121. pageType: {
  122. type: String,
  123. default: () => {}
  124. }
  125. })
  126. const router = useRouter()
  127. const fileTypeIcons = {
  128. // 图片类
  129. jpg: 'FileImageOutlined',
  130. jpeg: 'FileImageOutlined',
  131. png: 'FileImageOutlined',
  132. gif: 'FileImageOutlined',
  133. // 文档类
  134. pdf: 'FilePdfOutlined',
  135. ppt: 'FilePptOutlined',
  136. pptx: 'FilePptOutlined',
  137. doc: 'FileWordOutlined',
  138. docx: 'FileWordOutlined',
  139. xls: 'FileExcelOutlined',
  140. xlsx: 'FileExcelOutlined',
  141. txt: 'FileTextOutlined',
  142. // 视频类
  143. mp4: 'PlaySquareOutlined',
  144. mov: 'PlaySquareOutlined',
  145. // 压缩包
  146. zip: 'FileZipOutlined',
  147. rar: 'FileZipOutlined',
  148. // 默认图标
  149. '*': 'FileOutlined'
  150. }
  151. // 数据源
  152. const dataSource = ref([])
  153. //发布按钮状态
  154. const releaseVisible = ref(false)
  155. const permissionTreeVisible = ref(false) //权限树
  156. const auditModalVisible = ref(false) //播放审核
  157. const isPublishBulk = ref(false) //是否批量发布
  158. const loading = ref(false) // 列表loading
  159. const isState = ref(0) //是否是编辑 0:新增 1:编辑
  160. const editResourcesId = ref(null) //资源id
  161. // 搜索值
  162. const searchValue = ref('')
  163. //课程类型
  164. const courseTypeOptions = tool.dictList('COURSE_TYPE')
  165. const suffixTypeOptions = ref([])
  166. const pagination = reactive({
  167. pageSize: 10,
  168. pageNum: 1,
  169. total: 0
  170. })
  171. const formState = reactive({
  172. fileName: null,
  173. verifyStatus: '0',
  174. resourcesId: null,
  175. // majorId: null, //专业
  176. collegeId: null, //院校一级id
  177. collegeTwoId: null, //院校二级id
  178. collegeThreeId: null, //院校三级id
  179. resourceType: null,
  180. suffix: null
  181. })
  182. // 添加选择状态
  183. const majorIdName = ref([])
  184. const majorOptions = ref([]) //专业
  185. const selectedRowKeys = ref([])
  186. const selectedRows = ref([])
  187. const publishedData = ref([]) //当前点击数据
  188. // 行选择配置
  189. const rowSelection = computed(() => {
  190. return {
  191. selectedRowKeys: selectedRowKeys.value,
  192. onChange: (keys, rows) => {
  193. selectedRowKeys.value = keys
  194. selectedRows.value = rows
  195. },
  196. onSelectAll: (selected, selectedRows, changeRows) => {
  197. if (selected) {
  198. // 全选当前页
  199. selectedRowKeys.value = dataSource.value.map((item) => item.id)
  200. selectedRows.value = dataSource.value
  201. } else {
  202. // 取消全选
  203. selectedRowKeys.value = []
  204. selectedRows.value = []
  205. }
  206. },
  207. onSelectInvert: () => {
  208. // 反选当前页
  209. const allKeys = dataSource.value.map((item) => item.id)
  210. const newSelectedKeys = allKeys.filter((key) => !selectedRowKeys.value.includes(key))
  211. selectedRowKeys.value = newSelectedKeys
  212. selectedRows.value = dataSource.value.filter((item) => newSelectedKeys.includes(item.id))
  213. }
  214. }
  215. })
  216. // 列定义
  217. const columnsUnpublished = [
  218. {
  219. title: '资源名称',
  220. align: 'center',
  221. dataIndex: 'fileName',
  222. key: 'fileName'
  223. },
  224. {
  225. title: '资源类型',
  226. align: 'center',
  227. dataIndex: 'resourceTypeName',
  228. key: 'resourceTypeName'
  229. },
  230. {
  231. title: '浏览量',
  232. dataIndex: 'viewCount',
  233. align: 'center',
  234. key: 'viewCount'
  235. },
  236. {
  237. title: '用户名',
  238. align: 'center',
  239. dataIndex: 'userName',
  240. key: 'userName'
  241. },
  242. {
  243. title: '操作',
  244. align: 'center',
  245. key: 'action'
  246. }
  247. ]
  248. const columnsPending = [
  249. {
  250. title: '编号',
  251. dataIndex: 'id',
  252. align: 'center',
  253. key: 'id'
  254. },
  255. {
  256. title: '资源名称',
  257. align: 'center',
  258. dataIndex: 'fileName',
  259. key: 'fileName'
  260. },
  261. {
  262. title: '所属院系',
  263. align: 'center',
  264. dataIndex: 'collegeAllIdName',
  265. key: 'collegeAllIdName'
  266. },
  267. {
  268. title: '资源类型',
  269. align: 'center',
  270. dataIndex: 'resourceTypeName',
  271. key: 'resourceTypeName'
  272. },
  273. {
  274. title: '资源格式',
  275. align: 'center',
  276. dataIndex: 'suffix',
  277. key: 'suffix'
  278. },
  279. {
  280. title: '上传时间',
  281. align: 'center',
  282. dataIndex: 'uploadTime',
  283. key: 'uploadTime'
  284. },
  285. {
  286. title: '状态',
  287. align: 'center',
  288. key: 'verifyStatus'
  289. },
  290. {
  291. title: '资源缩略图',
  292. align: 'center',
  293. dataIndex: 'fileUrl',
  294. key: 'fileUrl'
  295. },
  296. {
  297. title: '操作',
  298. align: 'center',
  299. key: 'action'
  300. }
  301. ]
  302. const columnsPublished = [...columnsPending]
  303. const columnsRecycle = [...columnsPending]
  304. const collegeMajorOptions = ref([]) //院校下拉数据
  305. const resourceTypeOptions = ref([]) //资源类型下拉数据
  306. const fileformatOptions = ref([]) //资源格式下拉数据
  307. const currentColumns = computed(() => {
  308. switch (formState.verifyStatus) {
  309. case '0':
  310. return columnsUnpublished
  311. case '1':
  312. return columnsPending
  313. case '2':
  314. return columnsPublished
  315. case '3':
  316. return columnsPublished
  317. case '4':
  318. return columnsRecycle
  319. default:
  320. return []
  321. }
  322. })
  323. const getIconColor = (suffix) => {
  324. const type = suffix?.toLowerCase()
  325. if (['jpg', 'jpeg', 'png', 'gif'].includes(type)) return '#ff4d4f' // 图片红色
  326. if (['pdf'].includes(type)) return '#f5222d' // PDF红色
  327. if (['ppt', 'pptx'].includes(type)) return '#fa8c16' // PPT橙色
  328. if (['doc', 'docx'].includes(type)) return '#1890ff' // Word蓝色
  329. if (['xls', 'xlsx'].includes(type)) return '#52c41a' // Excel绿色
  330. return '#666' // 默认灰色
  331. }
  332. const getListData = () => {
  333. loading.value = true
  334. let params = {
  335. current: pagination.pageNum,
  336. size: pagination.pageSize,
  337. fileName: formState.fileName
  338. };
  339. myFavoritesApi
  340. .page(params)
  341. .then((res) => {
  342. dataSource.value = res.data.records
  343. pagination.total = res.data.total
  344. loading.value = false
  345. })
  346. .catch((err) => {
  347. console.log(err)
  348. dataSource.value = []
  349. pagination.total = 0
  350. loading.value = false
  351. })
  352. }
  353. const changeCollegeMajor = (value, selectedOptions) => {
  354. console.log('Selected:', value, selectedOptions)
  355. majorIdName.value = selectedOptions.map((it) => it.name).join('/')
  356. formState.collegeId = value[0] || null
  357. formState.collegeTwoId = value[1] || null
  358. formState.collegeThreeId = value[2] || null
  359. if (selectedOptions.length) {
  360. // 获取选中的最后一级
  361. const lastSelected = selectedOptions[selectedOptions.length - 1]
  362. console.log(lastSelected, '最后一级id')
  363. getCollegeMajor(lastSelected.id)
  364. }
  365. }
  366. const getList = () => {
  367. getListData()
  368. uploadModalVisible.value = false
  369. }
  370. // 方法
  371. const handleSearch = () => {
  372. console.log('Search:', searchValue.value)
  373. getListData()
  374. }
  375. const handleReset = () => {
  376. searchValue.value = null
  377. majorIdName.value = null
  378. formState.fileName = null
  379. formState.resourceType = null
  380. formState.suffix = null
  381. formState.collegeTwoId = null
  382. // formState.majorId = null
  383. formState.collegeId = null
  384. formState.collegeThreeId = null
  385. getListData()
  386. }
  387. const tabChange = () => {
  388. dataSource.value = []
  389. getListData()
  390. }
  391. //发布
  392. const handlePublish = (record) => {
  393. publishedData.value = record;
  394. router.push({
  395. path: '/portal/resourceDetails',
  396. query: {
  397. id: record.resourceId
  398. }
  399. })
  400. }
  401. // 批量发布方法
  402. const batchPublish = () => {
  403. if (selectedRows.value.length === 0) {
  404. message.warning('请至少选择一条记录')
  405. return
  406. }
  407. isState.value = 0
  408. isPublishBulk.value = true
  409. releaseVisible.value = true
  410. }
  411. // 全选当前页数据
  412. const selectAll = () => {
  413. selectedRowKeys.value = dataSource.value.map((item) => item.id)
  414. selectedRows.value = dataSource.value
  415. }
  416. // 反选当前页数据
  417. const invertSelection = () => {
  418. const allKeys = dataSource.value.map((item) => item.id)
  419. const newSelectedKeys = allKeys.filter((key) => !selectedRowKeys.value.includes(key))
  420. selectedRowKeys.value = newSelectedKeys
  421. selectedRows.value = dataSource.value.filter((item) => newSelectedKeys.includes(item.id))
  422. }
  423. //发布确定
  424. const releaseConfirm = (obj) => {
  425. console.log(obj, selectedRows.value, '传回来的数据')
  426. releaseVisible.value = false
  427. if (isPublishBulk.value) {
  428. // const batchParams = selectedRows.value.map((item) => ({
  429. // id: item.id,
  430. // coverImage: item.coverImage,
  431. // resourceDesc: item.resourceDesc,
  432. // verifyStatus: 1
  433. // }))
  434. const params = {
  435. ids: selectedRows.value.map((item) => item.id).join(','),
  436. coverImage: obj.coverImageId,
  437. resourceDesc: obj.resourceDesc,
  438. verifyStatus: 1
  439. }
  440. console.log(params, '批量发布参数')
  441. // handleRelease(params)
  442. } else {
  443. const params = {
  444. ids: publishedData.value.id,
  445. coverImage: obj.coverImageId,
  446. resourceDesc: obj.resourceDesc,
  447. verifyStatus: 1
  448. }
  449. console.log(params, '发布参数')
  450. handleRelease(params)
  451. }
  452. }
  453. // updateStatus接口调用
  454. const handleRelease = (Params) => {
  455. resourceAuditApi
  456. .updateStatus(Params)
  457. .then((res) => {
  458. getListData()
  459. selectedRowKeys.value = []
  460. })
  461. .catch((err) => {
  462. console.error(err)
  463. })
  464. }
  465. const auditState = ref(null)
  466. const handleAudit = (record) => {
  467. console.log('Audit:', record)
  468. publishedData.value = record
  469. auditState.value = true
  470. auditModalVisible.value = true
  471. }
  472. const handleView = (record) => {
  473. publishedData.value = record
  474. auditState.value = false
  475. auditModalVisible.value = true
  476. }
  477. const handleDownload = (record) => {
  478. resourceAuditApi
  479. .downloadfile({
  480. userFileId: record.fileId,
  481. shareBatchNum: record.shareBatchNum == null ? '' : record.shareBatchNum,
  482. extractionCode: record.extractionCode == null ? '' : record.extractionCode,
  483. admin: true
  484. })
  485. .then((res) => {
  486. console.log('下载成功:', res)
  487. // 创建Blob对象
  488. const url = window.URL.createObjectURL(new Blob([res]))
  489. const link = document.createElement('a')
  490. link.href = url
  491. link.download = record.fileName || `file_${record.id}.${record.suffix}`
  492. document.body.appendChild(link)
  493. link.click()
  494. window.URL.revokeObjectURL(url)
  495. document.body.removeChild(link)
  496. })
  497. .catch((err) => {
  498. console.error(err)
  499. })
  500. }
  501. const handlePermission = (record) => {
  502. console.log('Permission:', record)
  503. permissionTreeVisible.value = true
  504. }
  505. const auditConfirm = (obj) => {
  506. console.log('auditConfirm:', obj)
  507. const params = {
  508. ids: obj.id,
  509. verifyStatus: obj.auditResult
  510. }
  511. resourceAuditApi
  512. .updateStatus(params)
  513. .then((res) => {
  514. if (res.code == 200) {
  515. auditModalVisible.value = false
  516. }
  517. getListData()
  518. })
  519. .catch((err) => {
  520. console.error(err)
  521. })
  522. }
  523. const handleDelete = (record) => {
  524. console.log('Delete:', record)
  525. }
  526. const handleRestore = (record) => {
  527. const params = {
  528. ids: record.id,
  529. verifyStatus: 0
  530. }
  531. resourceAuditApi
  532. .updateStatus(params)
  533. .then((res) => {
  534. getListData()
  535. })
  536. .catch((err) => {
  537. console.error(err)
  538. })
  539. }
  540. //资源编辑
  541. const edit = (record) => {
  542. console.log('Restore:', record)
  543. uploadModalVisible.value = true
  544. isState.value = 1
  545. editResourcesId.value = record.id
  546. }
  547. //资源删除
  548. const resourcesDelete = (record) => {
  549. if (formState.verifyStatus == 4) {
  550. const params = [
  551. {
  552. id: record.id
  553. }
  554. ]
  555. resourceAuditApi
  556. .deletefile(params)
  557. .then((res) => {
  558. getListData()
  559. })
  560. .catch((err) => {
  561. console.error(err)
  562. })
  563. } else {
  564. const params = {
  565. ids: record.id,
  566. verifyStatus: 4
  567. }
  568. resourceAuditApi
  569. .updateStatus(params)
  570. .then((res) => {
  571. getListData()
  572. })
  573. .catch((err) => {
  574. console.error(err)
  575. })
  576. }
  577. }
  578. // 上传资源模态框
  579. const uploadModalVisible = ref(false)
  580. // 显示上传模态框
  581. const showUploadModal = () => {
  582. isState.value = 0
  583. uploadModalVisible.value = true
  584. }
  585. // 翻页
  586. const handlePageChange = (page) => {
  587. pagination.pageNum = page
  588. getListData()
  589. }
  590. // 每页条数
  591. const handlePageSizeChange = (pageNum, size) => {
  592. pagination.pageNum = 1
  593. pagination.pageSize = size
  594. getListData()
  595. }
  596. onMounted(() => {
  597. // if (pageType == 'economize') {
  598. // formState.verifyStatus = '1'
  599. // }
  600. // getFileformat()
  601. // getResourceTypeTree()
  602. getListData()
  603. })
  604. </script>
  605. <style scoped>
  606. .editable-cell {
  607. position: relative;
  608. }
  609. .ant-dropdown-link {
  610. margin-left: 8px;
  611. }
  612. .upload-area {
  613. border: 2px dashed #3ca9f5;
  614. padding: 40px;
  615. text-align: center;
  616. }
  617. .upload-area p {
  618. margin: 10px 0;
  619. }
  620. .file-item {
  621. display: flex;
  622. align-items: center;
  623. margin: 10px 0;
  624. }
  625. .file-item .ant-progress {
  626. flex: 1;
  627. margin: 0 10px;
  628. }
  629. </style>