FilePage.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. <script setup lang="tsx">
  2. import { ContentWrap } from '@/components/ContentWrap'
  3. import { Search } from '@/components/Search'
  4. import { useI18n } from '@/hooks/web/useI18n'
  5. import { ElButton, ElTooltip, ElMessage, ElButtonGroup } from 'element-plus'
  6. import { Table } from '@/components/Table'
  7. import { getTableListApi, delTableListApi, saveTableApi, updateTableApi } from '@/api/manage/file'
  8. import { useTable } from '@/hooks/web/useTable'
  9. import { ref, unref } from 'vue'
  10. import { useEmitt } from '@/hooks/event/useEmitt'
  11. import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
  12. import { TableSetting } from '@/components/TableSetting'
  13. import { usePageStore } from '@/store/modules/page'
  14. import { set } from 'lodash-es'
  15. import { useStorage } from '@/hooks/web/useStorage'
  16. import { Dialog } from '@/components/Dialog'
  17. import { FileData } from '@/api/manage/types'
  18. import Write from './components/Write.vue'
  19. import { useIcon } from '@/hooks/web/useIcon'
  20. import { Qrcode } from '@/components/Qrcode'
  21. defineOptions({
  22. name: 'FilePage'
  23. })
  24. const QRIcon = useIcon({ icon: 'ic:round-qr-code' })
  25. const DeleteIcon = useIcon({ icon: 'ep:delete' })
  26. const DownLoadIcon = useIcon({ icon: 'ep:download' })
  27. const copyIcon = useIcon({ icon: 'ep:document-copy' })
  28. const EditIcon = useIcon({ icon: 'ep:edit' })
  29. const id = ref<string>('')
  30. const QrVisible = ref<boolean>(false)
  31. const QrSrc = ref<string>('')
  32. const { getStorage } = useStorage()
  33. const searchParams = ref({})
  34. const setSearchParams = (params: any) => {
  35. searchParams.value = params
  36. getList()
  37. }
  38. const currentRow = ref<FileData>({
  39. virtualPath: ''
  40. })
  41. const formState = ref<string>('add')
  42. const { tableRegister, tableState, tableMethods } = useTable({
  43. fetchDataApi: async () => {
  44. const { currentPage, pageSize } = tableState
  45. const res = await getTableListApi({
  46. pageNum: unref(currentPage),
  47. pageSize: unref(pageSize),
  48. ...unref(searchParams)
  49. })
  50. return {
  51. list: res.data.list,
  52. total: Number(res.data.totalCount)
  53. }
  54. },
  55. fetchDelApi: async () => {
  56. const res = await delTableListApi(unref(id))
  57. return !!res
  58. }
  59. })
  60. const { loading, dataList, total, currentPage, pageSize } = tableState
  61. const { getList, delList, setColumn } = tableMethods
  62. getList()
  63. useEmitt({
  64. name: 'getList',
  65. callback: (type: string) => {
  66. if (type === 'add') {
  67. currentPage.value = 1
  68. }
  69. getList()
  70. }
  71. })
  72. const { t } = useI18n()
  73. const appStore = usePageStore()
  74. const dialogVisible = ref(false)
  75. // const fileList = ref([])
  76. const crudSchemas: CrudSchema[] = [
  77. {
  78. field: 'name',
  79. label: '名称',
  80. minWidth: 100,
  81. table: {
  82. hidden: false
  83. },
  84. form: {
  85. colProps: {
  86. span: 24
  87. }
  88. }
  89. },
  90. {
  91. field: 'version',
  92. label: '版本号',
  93. minWidth: 100,
  94. search: {
  95. hidden: true
  96. },
  97. table: {
  98. hidden: false
  99. }
  100. },
  101. {
  102. field: 'code',
  103. label: '版本序列号',
  104. minWidth: 100,
  105. table: {
  106. hidden: false
  107. },
  108. search: {
  109. hidden: true
  110. },
  111. form: {
  112. component: 'InputNumber'
  113. }
  114. },
  115. {
  116. field: 'fileName',
  117. label: '文件名',
  118. minWidth: 120,
  119. table: {
  120. hidden: false
  121. },
  122. form: {
  123. hidden: true
  124. }
  125. },
  126. {
  127. field: 'virtualPath',
  128. label: '文件链接',
  129. minWidth: 120,
  130. search: {
  131. hidden: true
  132. },
  133. table: {
  134. hidden: false
  135. },
  136. form: {
  137. colProps: {
  138. span: 24
  139. },
  140. component: 'Upload',
  141. componentProps: {
  142. action: '/api/sysApk/uploadFile',
  143. limit: 1,
  144. class: 'filePageUploader',
  145. fileList: currentRow.value.virtualPath
  146. ? [
  147. {
  148. url: currentRow.value.virtualPath
  149. }
  150. ]
  151. : [],
  152. headers: {
  153. token: getStorage('token')
  154. },
  155. onSuccess: (response) => {
  156. currentRow.value = {
  157. ...currentRow.value,
  158. ...response.data
  159. }
  160. },
  161. onExceed: (_files, responses) => {
  162. currentRow.value = {
  163. ...currentRow.value,
  164. ...responses[0].response.data
  165. }
  166. },
  167. slots: {
  168. default: () => <ElButton type="primary">上传文件</ElButton>
  169. }
  170. }
  171. }
  172. },
  173. {
  174. field: 'filePath',
  175. label: '文件路径',
  176. minWidth: 120,
  177. search: {
  178. hidden: true
  179. },
  180. table: {
  181. hidden: false
  182. },
  183. form: {
  184. hidden: true
  185. }
  186. },
  187. {
  188. field: 'content',
  189. label: '版本更新说明',
  190. minWidth: 120,
  191. search: {
  192. hidden: true
  193. },
  194. table: {
  195. hidden: false
  196. },
  197. form: {
  198. colProps: {
  199. span: 24
  200. },
  201. componentProps: {
  202. type: 'textarea'
  203. }
  204. }
  205. },
  206. {
  207. field: 'remark',
  208. label: '备注',
  209. minWidth: 120,
  210. search: {
  211. hidden: true
  212. },
  213. table: {
  214. hidden: false
  215. },
  216. form: {
  217. colProps: {
  218. span: 24
  219. },
  220. componentProps: {
  221. type: 'textarea'
  222. }
  223. }
  224. },
  225. {
  226. field: 'createTime',
  227. label: '创建时间',
  228. minWidth: 160,
  229. table: {
  230. hidden: false
  231. },
  232. search: {
  233. hidden: true
  234. },
  235. form: {
  236. hidden: true
  237. }
  238. },
  239. {
  240. field: 'action',
  241. width: '250px',
  242. label: '操作',
  243. search: {
  244. hidden: true
  245. },
  246. form: {
  247. hidden: true
  248. },
  249. detail: {
  250. hidden: true
  251. },
  252. table: {
  253. hidden: false,
  254. fixed: 'right',
  255. slots: {
  256. default: (data: any) => {
  257. return (
  258. <ElButtonGroup>
  259. <ElTooltip content="编辑">
  260. <ElButton text icon={EditIcon} onClick={() => handleEdit(data.row)} />
  261. </ElTooltip>
  262. <ElTooltip content="二维码">
  263. <ElButton text icon={QRIcon} onClick={() => showQrCode(data.row)} />
  264. </ElTooltip>
  265. <ElTooltip content="下载">
  266. <ElButton text icon={DownLoadIcon} onClick={() => downloadFile(data.row)} />
  267. </ElTooltip>
  268. <ElTooltip content="复制链接">
  269. <ElButton text icon={copyIcon} onClick={() => onCopy(data.row)} />
  270. </ElTooltip>
  271. <ElTooltip content="删除">
  272. <ElButton text icon={DeleteIcon} type="danger" onClick={() => delData(data.row)} />
  273. </ElTooltip>
  274. </ElButtonGroup>
  275. )
  276. }
  277. }
  278. }
  279. }
  280. ]
  281. // @ts-ignore
  282. const getSchemas = () => {
  283. let localSchemas = appStore.getPageData['FilePage']
  284. if (localSchemas && localSchemas.schemas) {
  285. let localSchemasArr = localSchemas.schemas
  286. for (let i = 0; i < localSchemasArr.length; i++) {
  287. let item = localSchemasArr[i]
  288. let index = crudSchemas.findIndex((e) => {
  289. return e.field == item.field
  290. })
  291. if (index > 0) {
  292. set(crudSchemas[index], 'table.hidden', item.table.hidden)
  293. }
  294. }
  295. }
  296. }
  297. getSchemas()
  298. let allSchemas = useCrudSchemas(crudSchemas).allSchemas
  299. // 修改列设置后调用
  300. const setSchemas = (schemas: CrudSchema[]) => {
  301. let arr = schemas.map((item) => {
  302. return {
  303. field: item.field,
  304. path: 'hidden',
  305. value: item.table ? item.table.hidden : false
  306. }
  307. })
  308. setColumn(arr)
  309. }
  310. const writeRef = ref<ComponentRef<typeof Write>>()
  311. const handleEdit = (row: FileData) => {
  312. currentRow.value = row
  313. formState.value = 'edit'
  314. dialogVisible.value = true
  315. }
  316. const save = async () => {
  317. const write = unref(writeRef)
  318. const formData = await write?.submit()
  319. if (formData) {
  320. delLoading.value = true
  321. try {
  322. if (formData.id) {
  323. let res = await updateTableApi(formData)
  324. if (res) {
  325. currentPage.value = 1
  326. getList()
  327. }
  328. } else {
  329. let res = await saveTableApi(formData)
  330. if (res) {
  331. currentPage.value = 1
  332. getList()
  333. }
  334. }
  335. } catch (error) {
  336. } finally {
  337. delLoading.value = false
  338. dialogVisible.value = false
  339. }
  340. }
  341. }
  342. const delLoading = ref(false)
  343. const delData = async (row: FileData) => {
  344. if (!row.id) return
  345. id.value = row?.id
  346. delLoading.value = true
  347. await delList(unref(id).length).finally(() => {
  348. delLoading.value = false
  349. })
  350. }
  351. const showQrCode = (row: FileData) => {
  352. QrSrc.value = row.virtualPath
  353. QrVisible.value = true
  354. }
  355. const downloadFile = (row: FileData) => {
  356. window.open(row.virtualPath)
  357. }
  358. const onCopy = (row: FileData) => {
  359. navigator.clipboard.writeText(row.virtualPath).then(() => {
  360. ElMessage({
  361. message: '复制成功!',
  362. type: 'success'
  363. })
  364. })
  365. }
  366. const handleAdd = () => {
  367. formState.value = 'add'
  368. currentRow.value = {
  369. virtualPath: ''
  370. }
  371. dialogVisible.value = true
  372. }
  373. </script>
  374. <template>
  375. <ContentWrap>
  376. <Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
  377. <div class="mb-10px">
  378. <el-button type="primary" @click="handleAdd">上传文件</el-button>
  379. <TableSetting page="FilePage" :data="crudSchemas" @set-schemas="setSchemas" />
  380. </div>
  381. <Table
  382. v-model:pageSize="pageSize"
  383. v-model:currentPage="currentPage"
  384. :columns="allSchemas.tableColumns"
  385. :data="dataList"
  386. :loading="loading"
  387. :pagination="{
  388. total: total
  389. }"
  390. @register="tableRegister"
  391. />
  392. </ContentWrap>
  393. <Dialog v-model="dialogVisible" :title="formState == 'add' ? '新增文件' : '编辑文件'">
  394. <Write ref="writeRef" :form-schema="allSchemas.formSchema" :current-row="currentRow" />
  395. <template #footer>
  396. <ElButton type="primary" :loading="delLoading" @click="save">
  397. {{ t('exampleDemo.save') }}
  398. </ElButton>
  399. <ElButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton>
  400. </template>
  401. </Dialog>
  402. <Dialog v-model="QrVisible" width="430px" title="二维码">
  403. <Qrcode
  404. :width="395"
  405. :options="{
  406. color: {
  407. dark: '#55D187',
  408. light: '#ffffff'
  409. }
  410. }"
  411. :text="QrSrc"
  412. />
  413. <template #footer>
  414. <ElButton @click="QrVisible = false">{{ t('dialogDemo.close') }}</ElButton>
  415. </template>
  416. </Dialog>
  417. </template>
  418. @/hooks/event/useEmitt
  419. <style lang="less">
  420. .uploadBtn {
  421. display: inline-block;
  422. margin-right: 12px;
  423. }
  424. .filePageUploader {
  425. width: 100%;
  426. }
  427. </style>