index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. <template>
  2. <div class="sys-file-container">
  3. <el-card shadow="hover" :body-style="{ paddingBottom: '0' }">
  4. <el-form :model="state.queryParams" ref="queryForm" :inline="true">
  5. <el-form-item label="租户" v-if="userStore.userInfos.accountType == 999">
  6. <el-select v-model="state.queryParams.tenantId" placeholder="租户" style="width: 100%">
  7. <el-option :value="item.value" :label="`${item.label} (${item.host})`" v-for="(item, index) in state.tenantList" :key="index" />
  8. </el-select>
  9. </el-form-item>
  10. <el-form-item label="文件名称" prop="fileName">
  11. <el-input v-model="state.queryParams.fileName" placeholder="文件名称" clearable />
  12. </el-form-item>
  13. <el-form-item label="开始时间" prop="name">
  14. <el-date-picker v-model="state.queryParams.startTime" type="datetime" placeholder="开始时间" value-format="YYYY-MM-DD HH:mm:ss" />
  15. </el-form-item>
  16. <el-form-item label="结束时间" prop="code">
  17. <el-date-picker v-model="state.queryParams.endTime" type="datetime" placeholder="结束时间" value-format="YYYY-MM-DD HH:mm:ss" />
  18. </el-form-item>
  19. <el-form-item>
  20. <el-button-group>
  21. <el-button type="primary" icon="ele-Search" @click="handleQuery" v-auth="'sysFile:page'"> 查询 </el-button>
  22. <el-button icon="ele-Refresh" @click="resetQuery"> 重置 </el-button>
  23. </el-button-group>
  24. </el-form-item>
  25. <el-form-item>
  26. <el-button type="primary" icon="ele-Plus" @click="openUploadDialog" v-auth="'sysFile:uploadFile'"> 上传 </el-button>
  27. </el-form-item>
  28. </el-form>
  29. </el-card>
  30. <el-card class="full-table" shadow="hover" style="margin-top: 5px">
  31. <el-table :data="state.fileData" style="width: 100%" v-loading="state.loading" border>
  32. <el-table-column type="index" label="序号" width="55" align="center" />
  33. <el-table-column prop="fileName" label="名称" min-width="150" header-align="center" show-overflow-tooltip />
  34. <el-table-column prop="suffix" label="后缀" align="center" show-overflow-tooltip>
  35. <template #default="scope">
  36. <el-tag round>{{ scope.row.suffix }}</el-tag>
  37. </template>
  38. </el-table-column>
  39. <el-table-column prop="sizeKb" label="大小kb" align="center" show-overflow-tooltip />
  40. <el-table-column prop="url" label="预览" align="center">
  41. <template #default="scope">
  42. <el-image
  43. style="width: 60px; height: 60px"
  44. :src="getFileUrl(scope.row)"
  45. alt="无法预览"
  46. :lazy="true"
  47. :hide-on-click-modal="true"
  48. :preview-src-list="[getFileUrl(scope.row)]"
  49. :initial-index="0"
  50. fit="scale-down"
  51. preview-teleported
  52. >
  53. <template #error> </template>
  54. </el-image>
  55. </template>
  56. </el-table-column>
  57. <el-table-column prop="bucketName" label="存储位置" align="center" show-overflow-tooltip />
  58. <el-table-column prop="id" label="存储标识" align="center" show-overflow-tooltip />
  59. <el-table-column prop="fileType" label="文件类型" min-width="100" header-align="center" show-overflow-tooltip />
  60. <el-table-column prop="isPublic" label="是否公开" min-width="100" header-align="center" show-overflow-tooltip>
  61. <template #default="scope">
  62. <el-tag v-if="scope.row.isPublic === true" type="success">是</el-tag>
  63. <el-tag v-else type="danger">否</el-tag>
  64. </template>
  65. </el-table-column>
  66. <el-table-column prop="relationName" label="关联对象名称" min-width="150" align="center" />
  67. <el-table-column prop="relationId" label="关联对象Id" align="center" />
  68. <el-table-column prop="belongId" label="所属Id" align="center" />
  69. <el-table-column label="修改记录" width="100" align="center" show-overflow-tooltip>
  70. <template #default="scope">
  71. <ModifyRecord :data="scope.row" />
  72. </template>
  73. </el-table-column>
  74. <el-table-column label="操作" width="260" fixed="right" align="center" show-overflow-tooltip>
  75. <template #default="scope">
  76. <el-button-group>
  77. <el-button icon="ele-View" size="small" type="primary" @click="openFilePreviewDialog(scope.row)" v-auth="'sysFile:delete'"></el-button>
  78. <el-button icon="ele-Download" size="small" type="primary" @click="downloadFile(scope.row)" v-auth="'sysFile:downloadFile'"></el-button>
  79. <el-button icon="ele-Delete" size="small" type="danger" @click="delFile(scope.row)" v-auth="'sysFile:delete'"></el-button>
  80. <el-button icon="ele-Edit" size="small" type="primary" @click="openEditSysFile(scope.row)" v-auth="'sysFile:update'"></el-button>
  81. </el-button-group>
  82. </template>
  83. </el-table-column>
  84. </el-table>
  85. <el-pagination
  86. v-model:currentPage="state.tableParams.page"
  87. v-model:page-size="state.tableParams.pageSize"
  88. :total="state.tableParams.total"
  89. :page-sizes="[10, 20, 50, 100]"
  90. size="small"
  91. background
  92. @size-change="handleSizeChange"
  93. @current-change="handleCurrentChange"
  94. layout="total, sizes, prev, pager, next, jumper"
  95. />
  96. </el-card>
  97. <el-dialog v-model="state.dialogUploadVisible" :lock-scroll="false" draggable width="400px">
  98. <template #header>
  99. <div style="color: #fff">
  100. <el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"> <ele-UploadFilled /> </el-icon>
  101. <span> 上传文件 </span>
  102. </div>
  103. </template>
  104. <div>
  105. <el-select v-model="state.fileType" placeholder="请选择文件类型" style="margin-bottom: 10px">
  106. <el-option label="相关文件" value="相关文件" />
  107. <el-option label="归档文件" value="归档文件" />
  108. </el-select>
  109. 是否公开:
  110. <el-radio-group v-model="state.isPublic">
  111. <el-radio :value="false">否</el-radio>
  112. <el-radio :value="true">是</el-radio>
  113. </el-radio-group>
  114. <el-upload ref="uploadRef" drag :auto-upload="false" :limit="1" :file-list="state.fileList" action :on-change="handleChange" accept=".jpg,.png,.bmp,.gif,.txt,.xml,.pdf,.xlsx,.docx">
  115. <el-icon class="el-icon--upload">
  116. <ele-UploadFilled />
  117. </el-icon>
  118. <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
  119. <template #tip>
  120. <div class="el-upload__tip">请上传大小不超过 10MB 的文件</div>
  121. </template>
  122. </el-upload>
  123. </div>
  124. <template #footer>
  125. <span class="dialog-footer">
  126. <el-button @click="state.dialogUploadVisible = false">取消</el-button>
  127. <el-button type="primary" @click="uploadFile">确定</el-button>
  128. </span>
  129. </template>
  130. </el-dialog>
  131. <el-drawer :title="state.fileName" v-model="state.dialogDocxVisible" size="50%" destroy-on-close>
  132. <vue-office-docx :src="state.docxUrl" style="height: 100vh" @rendered="renderedHandler" @error="errorHandler" />
  133. </el-drawer>
  134. <el-drawer :title="state.fileName" v-model="state.dialogXlsxVisible" size="50%" destroy-on-close>
  135. <vue-office-excel :src="state.excelUrl" style="height: 100vh" @rendered="renderedHandler" @error="errorHandler" />
  136. </el-drawer>
  137. <el-drawer :title="state.fileName" v-model="state.dialogPdfVisible" size="50%" destroy-on-close>
  138. <vue-office-pdf :src="state.pdfUrl" style="height: 100vh" @rendered="renderedHandler" @error="errorHandler" />
  139. </el-drawer>
  140. <el-image-viewer v-if="state.showViewer" :url-list="state.previewList" :hideOnClickModal="true" @close="state.showViewer = false"></el-image-viewer>
  141. <EditSysFile ref="editSysFileRef" title="编辑文件" @handleQuery="handleQuery" />
  142. </div>
  143. </template>
  144. <script lang="ts" setup name="sysFile">
  145. import { onMounted, reactive, ref } from 'vue';
  146. import { ElMessageBox, ElMessage, UploadInstance } from 'element-plus';
  147. import VueOfficeDocx from '@vue-office/docx';
  148. import VueOfficeExcel from '@vue-office/excel';
  149. import VueOfficePdf from '@vue-office/pdf';
  150. import '@vue-office/docx/lib/index.css';
  151. import '@vue-office/excel/lib/index.css';
  152. import EditSysFile from '/@/views/system/file/component/editSysfile.vue';
  153. import ModifyRecord from '/@/components/table/modifyRecord.vue';
  154. import { downloadByUrl } from '/@/utils/download';
  155. import { getAPI } from '/@/utils/axios-utils';
  156. import {SysFileApi, SysTenantApi} from '/@/api-services/api';
  157. import { SysFile } from '/@/api-services/models';
  158. import { useUserInfo } from "/@/stores/userInfo";
  159. // const baseUrl = window.__env__.VITE_API_URL;
  160. const userStore = useUserInfo();
  161. const uploadRef = ref<UploadInstance>();
  162. const editSysFileRef = ref<InstanceType<typeof EditSysFile>>();
  163. const state = reactive({
  164. loading: false,
  165. tenantList: [] as Array<any>,
  166. fileData: [] as Array<SysFile>,
  167. queryParams: {
  168. tenantId: undefined,
  169. fileName: undefined,
  170. startTime: undefined,
  171. endTime: undefined,
  172. },
  173. tableParams: {
  174. page: 1,
  175. pageSize: 50,
  176. total: 0 as any,
  177. },
  178. dialogUploadVisible: false,
  179. diaglogEditFile: false,
  180. fileList: [] as any,
  181. dialogDocxVisible: false,
  182. dialogXlsxVisible: false,
  183. dialogPdfVisible: false,
  184. showViewer: false,
  185. docxUrl: '',
  186. excelUrl: '',
  187. pdfUrl: '',
  188. fileName: '',
  189. fileType: '',
  190. isPublic: false,
  191. previewList: [] as string[],
  192. });
  193. onMounted(async () => {
  194. if (userStore.userInfos.accountType == 999) {
  195. state.tenantList = await getAPI(SysTenantApi).apiSysTenantListGet().then(res => res.data.result ?? []);
  196. state.queryParams.tenantId = userStore.userInfos.currentTenantId as any;
  197. }
  198. handleQuery();
  199. });
  200. // 查询操作
  201. const handleQuery = async () => {
  202. if (state.queryParams.startTime == null) state.queryParams.startTime = undefined;
  203. if (state.queryParams.endTime == null) state.queryParams.endTime = undefined;
  204. state.loading = true;
  205. let params = Object.assign(state.queryParams, state.tableParams);
  206. var res = await getAPI(SysFileApi).apiSysFilePagePost(params);
  207. console.log(res);
  208. state.fileData = res.data.result?.items ?? [];
  209. state.tableParams.total = res.data.result?.total;
  210. state.loading = false;
  211. };
  212. // 重置操作
  213. const resetQuery = () => {
  214. state.queryParams.fileName = undefined;
  215. state.queryParams.startTime = undefined;
  216. state.queryParams.endTime = undefined;
  217. handleQuery();
  218. };
  219. // 打开上传页面
  220. const openUploadDialog = () => {
  221. state.fileList = [];
  222. state.dialogUploadVisible = true;
  223. state.isPublic = false;
  224. };
  225. // 通过onChanne方法获得文件列表
  226. const handleChange = (file: any, fileList: []) => {
  227. state.fileList = fileList;
  228. };
  229. // 上传
  230. const uploadFile = async () => {
  231. if (state.fileList.length < 1) return;
  232. await getAPI(SysFileApi).apiSysFileUploadFilePostForm(state.fileList[0].raw, state.fileType, state.isPublic, undefined);
  233. handleQuery();
  234. ElMessage.success('上传成功');
  235. state.dialogUploadVisible = false;
  236. };
  237. // 下载
  238. const downloadFile = async (row: any) => {
  239. // var res = await getAPI(SysFileApi).sysFileDownloadPost({ id: row.id });
  240. var fileUrl = getFileUrl(row);
  241. downloadByUrl({ url: fileUrl });
  242. };
  243. // 删除
  244. const delFile = (row: any) => {
  245. ElMessageBox.confirm(`确定删除文件:【${row.fileName}】?`, '提示', {
  246. confirmButtonText: '确定',
  247. cancelButtonText: '取消',
  248. type: 'warning',
  249. })
  250. .then(async () => {
  251. await getAPI(SysFileApi).apiSysFileDeletePost({ id: row.id });
  252. handleQuery();
  253. ElMessage.success('删除成功');
  254. })
  255. .catch(() => {});
  256. };
  257. // 打开文件预览页面
  258. const openFilePreviewDialog = async (row: any) => {
  259. if (row.suffix == '.pdf') {
  260. state.fileName = `【${row.fileName}${row.suffix}】`;
  261. state.pdfUrl = getFileUrl(row);
  262. state.dialogPdfVisible = true;
  263. } else if (row.suffix == '.docx') {
  264. state.fileName = `【${row.fileName}${row.suffix}】`;
  265. state.docxUrl = getFileUrl(row);
  266. state.dialogDocxVisible = true;
  267. } else if (row.suffix == '.xlsx') {
  268. state.fileName = `【${row.fileName}${row.suffix}】`;
  269. state.excelUrl = getFileUrl(row);
  270. state.dialogXlsxVisible = true;
  271. } else if (['.jpg', '.png', '.jpeg', '.bmp'].findIndex((e) => e == row.suffix) > -1) {
  272. state.previewList = [getFileUrl(row)];
  273. state.showViewer = true;
  274. } else {
  275. ElMessage.error('此文件格式不支持预览');
  276. }
  277. };
  278. // 改变页面容量
  279. const handleSizeChange = (val: number) => {
  280. state.tableParams.pageSize = val;
  281. handleQuery();
  282. };
  283. // 改变页码序号
  284. const handleCurrentChange = (val: number) => {
  285. state.tableParams.page = val;
  286. handleQuery();
  287. };
  288. // 获取文件地址
  289. const getFileUrl = (row: SysFile): string => {
  290. if (row.bucketName == 'Local') {
  291. return `/${row.filePath}/${row.id}${row.suffix}`;
  292. } else {
  293. return row.url!;
  294. }
  295. };
  296. // 打开编辑页面
  297. const openEditSysFile = (row: any) => {
  298. editSysFileRef.value?.openDialog(row);
  299. };
  300. // 文件渲染完成
  301. const renderedHandler = () => {};
  302. // 文件渲染失败
  303. const errorHandler = () => {};
  304. </script>