From 9956824d5413fa24c81ea9cad8677a83552db856 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Sun, 9 Feb 2025 03:09:09 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20alova.js=E6=9B=BF=E6=8D=A2axios?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/package.json | 1 + web/pnpm-lock.yaml | 29 ++++ web/src/api/panel/file/index.ts | 40 +++--- web/src/components/common/CodeEditor.vue | 18 +-- web/src/utils/http/index.ts | 23 ++-- web/src/views/file/CompressModal.vue | 18 ++- web/src/views/file/ListTable.vue | 161 +++++++++++------------ web/src/views/file/PermissionModal.vue | 9 +- web/src/views/file/PreviewModal.vue | 6 +- web/src/views/file/SearchModal.vue | 15 +-- web/src/views/file/ToolBar.vue | 56 ++++---- web/src/views/file/UploadModal.vue | 21 +-- web/src/views/firewall/ForwardView.vue | 12 +- web/src/views/firewall/IpRuleView.vue | 12 +- web/src/views/firewall/RuleView.vue | 12 +- web/src/views/website/IndexView.vue | 16 ++- 16 files changed, 241 insertions(+), 208 deletions(-) diff --git a/web/package.json b/web/package.json index c2c1ec67..a85657df 100644 --- a/web/package.json +++ b/web/package.json @@ -23,6 +23,7 @@ "gen-auto-import": "tsx gen-auto-import.ts" }, "dependencies": { + "@alova/adapter-xhr": "^2.1.1", "@eslint/eslintrc": "^3.2.0", "@fontsource-variable/jetbrains-mono": "^5.1.1", "@guolao/vue-monaco-editor": "^1.5.4", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 5ae2fefc..8e744f26 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@alova/adapter-xhr': + specifier: ^2.1.1 + version: 2.1.1(alova@3.2.8) '@eslint/eslintrc': specifier: ^3.2.0 version: 3.2.0 @@ -207,6 +210,11 @@ importers: packages: + '@alova/adapter-xhr@2.1.1': + resolution: {integrity: sha512-053tY4wVEL5PUX8HwvFrp/y1wTObGjMvwVBLUVSOcZKuZwcoUraWdQoWAdNRnSWfwJoPEd+ta9EdS4KPqTcz9g==} + peerDependencies: + alova: ^3.0.20 + '@alova/shared@1.1.2': resolution: {integrity: sha512-8q/gMHFpzm7XYcaUlsyTCMDRRhFnewwheTeObMjPl1+bFdr+wZuBEHEPYIyd8tyzLwfrqpBeonaMN2tlngM8EA==} @@ -989,36 +997,42 @@ packages: engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm-musl@2.5.1': resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [musl] '@parcel/watcher-linux-arm64-glibc@2.5.1': resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm64-musl@2.5.1': resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [musl] '@parcel/watcher-linux-x64-glibc@2.5.1': resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-x64-musl@2.5.1': resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [musl] '@parcel/watcher-win32-arm64@2.5.1': resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} @@ -1096,51 +1110,61 @@ packages: resolution: {integrity: sha512-MW6N3AoC61OfE1VgnN5O1OW0gt8VTbhx9s/ZEPLBM11wEdHjeilPzOxVmmsrx5YmejpGPvez8QwGGvMU+pGxpw==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.34.3': resolution: {integrity: sha512-2SQkhr5xvatYq0/+H6qyW0zvrQz9LM4lxGkpWURLoQX5+yP8MsERh4uWmxFohOvwCP6l/+wgiHZ1qVwLDc7Qmw==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.34.3': resolution: {integrity: sha512-R3JLYt8YoRwKI5shJsovLpcR6pwIMui/MGG/MmxZ1DYI3iRSKI4qcYrvYgDf4Ss2oCR3RL3F3dYK7uAGQgMIuQ==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.34.3': resolution: {integrity: sha512-4XQhG8v/t3S7Rxs7rmFUuM6j09hVrTArzONS3fUZ6oBRSN/ps9IPQjVhp62P0W3KhqJdQADo/MRlYRMdgxr/3w==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loongarch64-gnu@4.34.3': resolution: {integrity: sha512-QlW1jCUZ1LHUIYCAK2FciVw1ptHsxzApYVi05q7bz2A8oNE8QxQ85NhM4arLxkAlcnS42t4avJbSfzSQwbIaKg==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-powerpc64le-gnu@4.34.3': resolution: {integrity: sha512-kMbLToizVeCcN69+nnm20Dh0hrRIAjgaaL+Wh0gWZcNt8e542d2FUGtsyuNsHVNNF3gqTJrpzUGIdwMGLEUM7g==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.34.3': resolution: {integrity: sha512-YgD0DnZ3CHtvXRH8rzjVSxwI0kMTr0RQt3o1N92RwxGdx7YejzbBO0ELlSU48DP96u1gYYVWfUhDRyaGNqJqJg==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-s390x-gnu@4.34.3': resolution: {integrity: sha512-dIOoOz8altjp6UjAi3U9EW99s8nta4gzi52FeI45GlPyrUH4QixUoBMH9VsVjt+9A2RiZBWyjYNHlJ/HmJOBCQ==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.34.3': resolution: {integrity: sha512-lOyG3aF4FTKrhpzXfMmBXgeKUUXdAWmP2zSNf8HTAXPqZay6QYT26l64hVizBjq+hJx3pl0DTEyvPi9sTA6VGA==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.34.3': resolution: {integrity: sha512-usztyYLu2i+mYzzOjqHZTaRXbUOqw3P6laNUh1zcqxbPH1P2Tz/QdJJCQSnGxCtsRQeuU2bCyraGMtMumC46rw==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.34.3': resolution: {integrity: sha512-ojFOKaz/ZyalIrizdBq2vyc2f0kFbJahEznfZlxdB6pF9Do6++i1zS5Gy6QLf8D7/S57MHrmBLur6AeRYeQXSA==} @@ -3732,6 +3756,11 @@ packages: snapshots: + '@alova/adapter-xhr@2.1.1(alova@3.2.8)': + dependencies: + '@alova/shared': 1.1.2 + alova: 3.2.8 + '@alova/shared@1.1.2': {} '@ampproject/remapping@2.3.0': diff --git a/web/src/api/panel/file/index.ts b/web/src/api/panel/file/index.ts index 63450e38..6a89dd57 100644 --- a/web/src/api/panel/file/index.ts +++ b/web/src/api/panel/file/index.ts @@ -1,47 +1,39 @@ -import { request } from '@/utils' +import { http } from '@/utils' export default { // 创建文件/文件夹 - create: (path: string, dir: boolean): any => request.post('/file/create', { path, dir }), + create: (path: string, dir: boolean): any => http.Post('/file/create', { path, dir }), // 获取文件内容 - content: (path: string): any => request.get('/file/content', { params: { path } }), + content: (path: string): any => http.Get('/file/content', { params: { path } }), // 保存文件 - save: (path: string, content: string): any => request.post('/file/save', { path, content }), + save: (path: string, content: string): any => http.Post('/file/save', { path, content }), // 删除文件 - delete: (path: string): any => request.post('/file/delete', { path }), + delete: (path: string): any => http.Post('/file/delete', { path }), // 上传文件 - upload: (path: string, formData: FormData, onProgress: any): any => { - formData.append('path', path) - return request.post('/file/upload', formData, { - headers: { 'Content-Type': 'multipart/form-data' }, - onUploadProgress: (progressEvent: any) => { - onProgress({ percent: Math.ceil((progressEvent.loaded / progressEvent.total) * 100) }) - } - }) - }, + upload: (formData: FormData): any => http.Post('/file/upload', formData), // 检查文件是否存在 - exist: (paths: string[]): any => request.post('/file/exist', paths), + exist: (paths: string[]): any => http.Post('/file/exist', paths), // 移动文件 - move: (paths: any[]): any => request.post('/file/move', paths), + move: (paths: any[]): any => http.Post('/file/move', paths), // 复制文件 - copy: (paths: any[]): any => request.post('/file/copy', paths), + copy: (paths: any[]): any => http.Post('/file/copy', paths), // 远程下载 remoteDownload: (path: string, url: string): any => - request.post('/file/remoteDownload', { path, url }), + http.Post('/file/remoteDownload', { path, url }), // 获取文件信息 - info: (path: string): any => request.get('/file/info', { params: { path } }), + info: (path: string): any => http.Get('/file/info', { params: { path } }), // 修改文件权限 permission: (path: string, mode: string, owner: string, group: string): any => - request.post('/file/permission', { path, mode, owner, group }), + http.Post('/file/permission', { path, mode, owner, group }), // 压缩文件 compress: (dir: string, paths: string[], file: string): any => - request.post('/file/compress', { dir, paths, file }), + http.Post('/file/compress', { dir, paths, file }), // 解压文件 - unCompress: (file: string, path: string): any => request.post('/file/unCompress', { file, path }), + unCompress: (file: string, path: string): any => http.Post('/file/unCompress', { file, path }), // 搜索文件 search: (path: string, keyword: string, sub: boolean, page: number, limit: number): any => - request.get('/file/search', { params: { path, keyword, sub, page, limit } }), + http.Get('/file/search', { params: { path, keyword, sub, page, limit } }), // 获取文件列表 list: (path: string, page: number, limit: number, sort: string): any => - request.get('/file/list', { params: { path, page, limit, sort } }) + http.Get('/file/list', { params: { path, page, limit, sort } }) } diff --git a/web/src/components/common/CodeEditor.vue b/web/src/components/common/CodeEditor.vue index 2ebad9ea..622e4c2b 100644 --- a/web/src/components/common/CodeEditor.vue +++ b/web/src/components/common/CodeEditor.vue @@ -18,25 +18,25 @@ const props = defineProps({ const disabled = ref(false) // 在出现错误的情况下禁用保存 const data = ref('') -const get = async () => { - await file - .content(props.path) - .then((res) => { - data.value = decodeBase64(res.data.content) +const get = () => { + useRequest(file.content(props.path)) + .onSuccess(({ data }) => { + data.value = decodeBase64(data.content) window.$message.success('获取成功') }) - .catch(() => { + .onError(() => { disabled.value = true }) } -const save = async () => { +const save = () => { if (disabled.value) { window.$message.error('当前状态下不可保存') return } - await file.save(props.path, data.value) - window.$message.success('保存成功') + useRequest(file.save(props.path, data.value)).onSuccess(() => { + window.$message.success('保存成功') + }) } onMounted(() => { diff --git a/web/src/utils/http/index.ts b/web/src/utils/http/index.ts index 85aa15d0..6c58cac8 100644 --- a/web/src/utils/http/index.ts +++ b/web/src/utils/http/index.ts @@ -1,6 +1,7 @@ import { resolveResError } from '@/utils/http/helpers' +import type { AlovaXHRResponse } from '@alova/adapter-xhr' +import { xhrRequestAdapter } from '@alova/adapter-xhr' import { createAlova, Method } from 'alova' -import adapterFetch from 'alova/fetch' import VueHook from 'alova/vue' import axios from 'axios' import { reqReject, reqResolve, resReject, resResolve } from './interceptors' @@ -27,15 +28,21 @@ export const http = createAlova({ id: 'panel', cacheFor: null, statesHook: VueHook, - requestAdapter: adapterFetch(), + requestAdapter: xhrRequestAdapter(), baseURL: import.meta.env.VITE_BASE_API, responded: { - onSuccess: async (response: Response, method: Method) => { - const ct = response.headers.get('Content-Type') - const json = - ct && ct.includes('application/json') - ? await response.json() - : { code: response.status, message: await response.text() } + onSuccess: async (response: AlovaXHRResponse, method: Method) => { + const ct = response.headers['Content-Type'] || response.headers['content-type'] + let json + try { + if (ct && ct.includes('application/json')) { + json = typeof response.data === 'string' ? JSON.parse(response.data) : response.data + } else { + json = { code: response.status, message: response.data } + } + } catch (error) { + json = { code: response.status, message: 'JSON 解析失败' } + } const { status, statusText } = response const { meta } = method if (status !== 200) { diff --git a/web/src/views/file/CompressModal.vue b/web/src/views/file/CompressModal.vue index 3d833a83..af42280d 100644 --- a/web/src/views/file/CompressModal.vue +++ b/web/src/views/file/CompressModal.vue @@ -24,26 +24,24 @@ const ensureExtension = (extension: string) => { } } -const handleArchive = async () => { +const handleArchive = () => { ensureExtension(format.value) loading.value = true const message = window.$message.loading('正在压缩中...', { duration: 0 }) const paths = selected.value.map((item) => item.replace(path.value, '').replace(/^\//, '')) - await api - .compress(path.value, paths, file.value) - .then(() => { - window.$message.success('压缩成功') + useRequest(api.compress(path.value, paths, file.value)) + .onSuccess(() => { show.value = false selected.value = [] + window.$message.success('压缩成功') }) - .catch(() => { - window.$message.error('压缩失败') + .onComplete(() => { + message?.destroy() + loading.value = false + window.$bus.emit('file:refresh') }) - message?.destroy() - loading.value = false - window.$bus.emit('file:refresh') } onMounted(() => { diff --git a/web/src/views/file/ListTable.vue b/web/src/views/file/ListTable.vue index 36720e7a..6af41400 100644 --- a/web/src/views/file/ListTable.vue +++ b/web/src/views/file/ListTable.vue @@ -1,5 +1,14 @@ @@ -34,7 +40,6 @@ const uploadRequest = ({ file, onFinish, onError, onProgress }: UploadCustomRequ :segmented="false" > - 若上传报网络错误,请开启面板 HTTPS 后重试 点击或者拖动文件到该区域来上传 - 大文件建议使用 SFTP 上传 + 大文件建议使用 SFTP 等方式上传 diff --git a/web/src/views/firewall/ForwardView.vue b/web/src/views/firewall/ForwardView.vue index a7c501b8..75e383a4 100644 --- a/web/src/views/firewall/ForwardView.vue +++ b/web/src/views/firewall/ForwardView.vue @@ -131,21 +131,21 @@ const handleDelete = (row: any) => { }) } -const batchDelete = () => { +const batchDelete = async () => { if (selectedRowKeys.value.length === 0) { window.$message.info('请选择要删除的规则') return } - for (const key of selectedRowKeys.value) { - // 解析json + const promises = selectedRowKeys.value.map((key: any) => { const rule = JSON.parse(key) - useRequest(firewall.deleteForward(rule)).onSuccess(() => { + return useRequest(firewall.deleteForward(rule)).then(() => { window.$message.success(`${rule.protocol} ${rule.target_ip}:${rule.target_port} 删除成功`) }) - } + }) - refresh() + await Promise.all(promises) + await refresh() } const onChecked = (rowKeys: any) => { diff --git a/web/src/views/firewall/IpRuleView.vue b/web/src/views/firewall/IpRuleView.vue index 2513b409..ad5c8294 100644 --- a/web/src/views/firewall/IpRuleView.vue +++ b/web/src/views/firewall/IpRuleView.vue @@ -173,21 +173,21 @@ const handleDelete = (row: any) => { }) } -const batchDelete = () => { +const batchDelete = async () => { if (selectedRowKeys.value.length === 0) { window.$message.info('请选择要删除的规则') return } - for (const key of selectedRowKeys.value) { - // 解析json + const promises = selectedRowKeys.value.map((key: any) => { const rule = JSON.parse(key) - useRequest(firewall.deleteIpRule(rule)).onSuccess(() => { + return useRequest(firewall.deleteIpRule(rule)).then(() => { window.$message.success(`${rule.address} 删除成功`) }) - } + }) - refresh() + await Promise.all(promises) + await refresh() } const onChecked = (rowKeys: any) => { diff --git a/web/src/views/firewall/RuleView.vue b/web/src/views/firewall/RuleView.vue index e7856c99..5f7cc0bb 100644 --- a/web/src/views/firewall/RuleView.vue +++ b/web/src/views/firewall/RuleView.vue @@ -210,23 +210,23 @@ const handleDelete = async (row: any) => { }) } -const batchDelete = () => { +const batchDelete = async () => { if (selectedRowKeys.value.length === 0) { window.$message.info('请选择要删除的规则') return } - for (const key of selectedRowKeys.value) { - // 解析json + const promises = selectedRowKeys.value.map((key: any) => { const rule = JSON.parse(key) - useRequest(firewall.deleteRule(rule)).onSuccess(() => { + return useRequest(firewall.deleteRule(rule)).then(() => { const port = rule.port_start == rule.port_end ? rule.port_start : `${rule.port_start}-${rule.port_end}` window.$message.success(`${rule.family} 规则 ${port}/${rule.protocol} 删除成功`) }) - } + }) - refresh() + await Promise.all(promises) + await refresh() } const onChecked = (rowKeys: any) => { diff --git a/web/src/views/website/IndexView.vue b/web/src/views/website/IndexView.vue index ed9b8d80..2a87977a 100644 --- a/web/src/views/website/IndexView.vue +++ b/web/src/views/website/IndexView.vue @@ -307,19 +307,21 @@ const handleCreate = async () => { }) } -const batchDelete = async () => { +const bulkDelete = async () => { if (selectedRowKeys.value.length === 0) { window.$message.info('请选择要删除的网站') return } - for (const id of selectedRowKeys.value) { - useRequest(website.delete(id, true, false)).onSuccess(() => { - refresh() - const site = data.value.find((item: any) => item.id === id) + const promises = selectedRowKeys.value.map((id: any) => { + const site = data.value.find((item: any) => item.id === id) + return useRequest(website.delete(id, true, false)).then(() => { window.$message.success('网站 ' + site?.name + ' 删除成功') }) - } + }) + + await Promise.all(promises) + await refresh() } const formatDbValue = (value: string) => { @@ -346,7 +348,7 @@ onMounted(() => { {{ $t('websiteIndex.create.trigger') }} - +