增加删除已发布应用的功能

This commit is contained in:
jingrow 2025-11-03 00:39:38 +08:00
parent d5970329a4
commit 647772a7ab
2 changed files with 67 additions and 161 deletions

View File

@ -100,18 +100,10 @@
{{ t('View Details') }} {{ t('View Details') }}
</n-button> </n-button>
<n-button <n-button
v-if="isAppInstalled(app.app_name || app.name)" type="error"
type="warning" @click="deleteApp(app)"
@click="installApp(app)"
> >
{{ t('Update') }} {{ t('Delete') }}
</n-button>
<n-button
v-else
type="primary"
@click="installApp(app)"
>
{{ t('Install') }}
</n-button> </n-button>
</div> </div>
</div> </div>
@ -151,15 +143,6 @@
</n-empty> </n-empty>
</div> </div>
</div> </div>
<!-- 安装进度弹窗 -->
<InstallProgressModal
v-model="showProgressModal"
:progress="installProgress"
:message="installMessage"
:status="installStatus"
:installing="installing"
/>
</div> </div>
</template> </template>
@ -170,8 +153,6 @@ import { NInput, NButton, NIcon, NSpin, NEmpty, NSelect, NPagination, useMessage
import { Icon } from '@iconify/vue' import { Icon } from '@iconify/vue'
import axios from 'axios' import axios from 'axios'
import { t } from '@/shared/i18n' import { t } from '@/shared/i18n'
import { get_session_api_headers } from '@/shared/api/auth'
import InstallProgressModal from './InstallProgressModal.vue'
const message = useMessage() const message = useMessage()
const dialog = useDialog() const dialog = useDialog()
@ -185,16 +166,6 @@ const page = ref(1)
const pageSize = ref(parseInt(localStorage.getItem('itemsPerPage') || '20')) const pageSize = ref(parseInt(localStorage.getItem('itemsPerPage') || '20'))
const sortBy = ref('creation desc') 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<Set<string>>(new Set())
// //
const sortOptions = computed(() => [ const sortOptions = computed(() => [
{ label: t('Latest'), value: 'creation desc' }, { label: t('Latest'), value: 'creation desc' },
@ -287,151 +258,56 @@ function getStatusClass(status: string): string {
return status.toLowerCase().replace(/\s+/g, '-') return status.toLowerCase().replace(/\s+/g, '-')
} }
async function installApp(app: any) { async function deleteApp(app: any) {
if (!app.file_url && !app.repository_url) { // 使name
message.error(t('应用文件URL或仓库地址不存在')) const recordName = app.name
if (!recordName) {
message.error(t('应用名称不存在'))
return return
} }
// //
try { const appTitle = app.title || app.app_name || recordName
const appName = app.app_name || app.name dialog.warning({
if (appName) { title: t('确认删除'),
const checkResponse = await axios.get(`/jingrow/check-app/${appName}`) content: t('确定要删除应用 "{0}" 吗?此操作不可恢复。').replace('{0}', appTitle),
positiveText: t('确认删除'),
if (checkResponse.data.exists) { negativeText: t('取消'),
// onPositiveClick: async () => {
dialog.warning({ await performDelete(recordName)
title: t('应用已存在'),
content: t('应用 "{0}" 已安装,是否覆盖安装?').replace('{0}', appName),
positiveText: t('确认覆盖'),
negativeText: t('取消'),
onPositiveClick: () => {
performInstall(app)
}
})
return
}
} }
} catch (error) { })
console.error('Check app exists error:', error)
}
performInstall(app)
} }
async function performInstall(app: any) { async function performDelete(appName: string) {
try { try {
installing.value = true // API
installProgress.value = 0 const response = await axios.post('/jingrow/delete-published-app', {
installMessage.value = t('正在准备安装...') name: appName
installStatus.value = 'info' }, {
showProgressModal.value = true withCredentials: true
})
let response if (response.data && response.data.success) {
message.success(response.data.message || t('应用删除成功'))
// 使URL使git //
if (app.file_url) { loadApps()
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)
} else { } else {
throw new Error(response.data.error || t('安装失败')) const errorMsg = response.data?.message || response.data?.error || t('删除失败')
message.error(errorMsg)
} }
} catch (error: any) { } catch (error: any) {
console.error('Install app error:', error) console.error('Delete app error:', error)
installing.value = false const errorMsg = error.response?.data?.detail ||
installStatus.value = 'error' error.response?.data?.message ||
installMessage.value = error.response?.data?.detail || error.message || t('安装失败') error.message ||
message.error(error.response?.data?.detail || t('安装失败')) t('删除失败')
message.error(errorMsg)
setTimeout(() => {
showProgressModal.value = false
}, 3000)
} }
} }
//
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(() => { onMounted(() => {
loadApps() loadApps()
loadInstalledApps()
//
window.addEventListener('installedAppsUpdated', () => {
loadInstalledApps()
})
}) })
// //

View File

@ -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") @router.post("/jingrow/upload-image")
async def upload_image(file: UploadFile = File(...)): async def upload_image(file: UploadFile = File(...)):
"""上传应用图片""" """上传应用图片"""