From 647772a7abd8ee424aff95c92187f7d5d15fceb2 Mon Sep 17 00:00:00 2001 From: jingrow Date: Mon, 3 Nov 2025 00:39:38 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=88=A0=E9=99=A4=E5=B7=B2?= =?UTF-8?q?=E5=8F=91=E5=B8=83=E5=BA=94=E7=94=A8=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/views/dev/MyPublishedApps.vue | 198 ++++-------------- .../jingrow/api/local_app_installer.py | 30 +++ 2 files changed, 67 insertions(+), 161 deletions(-) diff --git a/apps/jingrow/frontend/src/views/dev/MyPublishedApps.vue b/apps/jingrow/frontend/src/views/dev/MyPublishedApps.vue index 95346fe..fa23345 100644 --- a/apps/jingrow/frontend/src/views/dev/MyPublishedApps.vue +++ b/apps/jingrow/frontend/src/views/dev/MyPublishedApps.vue @@ -100,18 +100,10 @@ {{ t('View Details') }} - {{ t('Update') }} - - - {{ t('Install') }} + {{ t('Delete') }} @@ -151,15 +143,6 @@ - - - @@ -170,8 +153,6 @@ import { NInput, NButton, NIcon, NSpin, NEmpty, NSelect, NPagination, useMessage import { Icon } from '@iconify/vue' import axios from 'axios' import { t } from '@/shared/i18n' -import { get_session_api_headers } from '@/shared/api/auth' -import InstallProgressModal from './InstallProgressModal.vue' const message = useMessage() const dialog = useDialog() @@ -185,16 +166,6 @@ const page = ref(1) const pageSize = ref(parseInt(localStorage.getItem('itemsPerPage') || '20')) const sortBy = ref('creation desc') -// 安装相关状态 -const installing = ref(false) -const installProgress = ref(0) -const installMessage = ref('') -const installStatus = ref<'success' | 'error' | 'info'>('info') -const showProgressModal = ref(false) - -// 已安装应用集合 -const installedAppNames = ref>(new Set()) - // 排序选项 const sortOptions = computed(() => [ { label: t('Latest'), value: 'creation desc' }, @@ -287,151 +258,56 @@ function getStatusClass(status: string): string { return status.toLowerCase().replace(/\s+/g, '-') } -async function installApp(app: any) { - if (!app.file_url && !app.repository_url) { - message.error(t('应用文件URL或仓库地址不存在')) +async function deleteApp(app: any) { + // 使用记录的name字段删除 + const recordName = app.name + if (!recordName) { + message.error(t('应用名称不存在')) return } - // 先检查应用是否已存在 - try { - const appName = app.app_name || app.name - if (appName) { - const checkResponse = await axios.get(`/jingrow/check-app/${appName}`) - - if (checkResponse.data.exists) { - // 显示确认对话框 - dialog.warning({ - title: t('应用已存在'), - content: t('应用 "{0}" 已安装,是否覆盖安装?').replace('{0}', appName), - positiveText: t('确认覆盖'), - negativeText: t('取消'), - onPositiveClick: () => { - performInstall(app) - } - }) - return - } + // 显示确认对话框,显示应用标题 + const appTitle = app.title || app.app_name || recordName + dialog.warning({ + title: t('确认删除'), + content: t('确定要删除应用 "{0}" 吗?此操作不可恢复。').replace('{0}', appTitle), + positiveText: t('确认删除'), + negativeText: t('取消'), + onPositiveClick: async () => { + await performDelete(recordName) } - } catch (error) { - console.error('Check app exists error:', error) - } - - performInstall(app) + }) } -async function performInstall(app: any) { +async function performDelete(appName: string) { try { - installing.value = true - installProgress.value = 0 - installMessage.value = t('正在准备安装...') - installStatus.value = 'info' - showProgressModal.value = true + // 调用本地API,由后端转发到云端 + const response = await axios.post('/jingrow/delete-published-app', { + name: appName + }, { + withCredentials: true + }) - let response - - // 优先使用文件URL,否则使用git仓库 - if (app.file_url) { - installMessage.value = t('正在下载应用包...') - setTimeout(() => { - installProgress.value = 20 - }, 300) - - installProgress.value = 30 - installMessage.value = t('正在安装应用...') - - response = await axios.post('/jingrow/install-from-url', new URLSearchParams({ - url: app.file_url - }), { - headers: { - ...get_session_api_headers(), - 'Content-Type': 'application/x-www-form-urlencoded' - } - }) - } else if (app.repository_url) { - installMessage.value = t('正在克隆仓库...') - setTimeout(() => { - installProgress.value = 20 - }, 300) - - installProgress.value = 30 - installMessage.value = t('正在安装应用...') - - const params = new URLSearchParams({ - repo_url: app.repository_url - }) - - response = await axios.post('/jingrow/install-from-git', params, { - headers: { - ...get_session_api_headers(), - 'Content-Type': 'application/x-www-form-urlencoded' - } - }) - } - - if (!response) { - throw new Error(t('无法确定安装方式')) - } - - // 更新进度到安装完成 - installProgress.value = 100 - - if (response.data.success) { - // 所有步骤完成后才显示成功 - installing.value = false - installStatus.value = 'success' - installMessage.value = t('应用安装成功!') - message.success(t('应用安装成功')) - - // 刷新已安装应用列表 - loadInstalledApps() - - setTimeout(() => { - showProgressModal.value = false - }, 2000) + if (response.data && response.data.success) { + message.success(response.data.message || t('应用删除成功')) + // 刷新应用列表 + loadApps() } else { - throw new Error(response.data.error || t('安装失败')) + const errorMsg = response.data?.message || response.data?.error || t('删除失败') + message.error(errorMsg) } } catch (error: any) { - console.error('Install app error:', error) - installing.value = false - installStatus.value = 'error' - installMessage.value = error.response?.data?.detail || error.message || t('安装失败') - message.error(error.response?.data?.detail || t('安装失败')) - - setTimeout(() => { - showProgressModal.value = false - }, 3000) + console.error('Delete app error:', error) + const errorMsg = error.response?.data?.detail || + error.response?.data?.message || + error.message || + t('删除失败') + message.error(errorMsg) } } -// 加载已安装应用列表 -async function loadInstalledApps() { - try { - const response = await axios.get('/jingrow/installed-app-names') - if (response.data.success) { - const apps = response.data.apps || [] - installedAppNames.value = new Set(apps) - } - } catch (error) { - console.error('Load installed apps error:', error) - } -} - -// 检查应用是否已安装 -function isAppInstalled(appName: string): boolean { - if (!appName) return false - return installedAppNames.value.has(appName.toLowerCase()) -} - onMounted(() => { loadApps() - loadInstalledApps() - - // 监听全局事件 - window.addEventListener('installedAppsUpdated', () => { - loadInstalledApps() - }) }) // 监听搜索和排序变化 diff --git a/apps/jingrow/jingrow/api/local_app_installer.py b/apps/jingrow/jingrow/api/local_app_installer.py index b37b1a4..2090d51 100644 --- a/apps/jingrow/jingrow/api/local_app_installer.py +++ b/apps/jingrow/jingrow/api/local_app_installer.py @@ -1060,6 +1060,36 @@ async def get_my_published_apps( } +@router.post("/jingrow/delete-published-app") +async def delete_published_app(request: Request, payload: Dict[str, Any]): + """删除已发布的应用,根据记录的name字段删除""" + session_cookie = request.cookies.get('sid') + if not session_cookie: + raise HTTPException(status_code=401, detail="未提供认证信息") + + # 使用记录的name字段,不是app_name字段 + record_name = payload.get('name') + if not record_name: + raise HTTPException(status_code=400, detail="记录名称不能为空") + + url = f"{get_jingrow_cloud_url()}/api/action/jcloud.api.local_app.delete_local_app" + + headers = get_jingrow_cloud_api_headers() + headers['Cookie'] = f'sid={session_cookie}' + + # 传递记录的name字段到云端API + response = requests.post(url, json={'name': record_name}, headers=headers, timeout=20) + response.raise_for_status() + + data = response.json() + result = data.get('message', data) + + if result.get('success'): + return {"success": True, "message": result.get('message', '应用删除成功')} + else: + raise HTTPException(status_code=400, detail=result.get('message', '删除失败')) + + @router.post("/jingrow/upload-image") async def upload_image(file: UploadFile = File(...)): """上传应用图片"""