2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 07:57:21 +08:00

feat: 优化菜单排序

This commit is contained in:
2025-12-01 18:40:15 +08:00
parent 6c2b0c2490
commit a64a1ba5c8
11 changed files with 317 additions and 250 deletions

View File

@@ -1,251 +1,23 @@
<script setup lang="ts">
<script lang="ts" setup>
defineOptions({
name: 'app-index'
})
import VersionModal from '@/views/app/VersionModal.vue'
import InstallView from '@/views/app/InstallView.vue'
import { NButton, NDataTable, NFlex, NPopconfirm, NSwitch } from 'naive-ui'
import { useGettext } from 'vue3-gettext'
import app from '@/api/panel/app'
import TheIcon from '@/components/custom/TheIcon.vue'
import { router } from '@/router'
const { $gettext } = useGettext()
const versionModalShow = ref(false)
const versionModalOperation = ref($gettext('Install'))
const versionModalInfo = ref<any>({})
const columns: any = [
{
key: 'icon',
fixed: 'left',
width: 80,
align: 'center',
render(row: any) {
return h(TheIcon, {
icon: row.icon,
size: 26
})
}
},
{
title: $gettext('App Name'),
key: 'name',
width: 200,
ellipsis: { tooltip: true }
},
{
title: $gettext('Description'),
key: 'description',
minWidth: 300,
ellipsis: { tooltip: true }
},
{
title: $gettext('Installed Version'),
key: 'installed_version',
width: 160,
ellipsis: { tooltip: true }
},
{
title: $gettext('Show in Home'),
key: 'show',
width: 140,
render(row: any) {
return h(NSwitch, {
size: 'small',
rubberBand: false,
value: row.show,
onUpdateValue: () => handleShowChange(row)
})
}
},
{
title: $gettext('Actions'),
key: 'actions',
width: 350,
hideInExcel: true,
render(row: any) {
return h(NFlex, null, {
default: () => [
row.installed && row.update_exist
? h(
NPopconfirm,
{
onPositiveClick: () => handleUpdate(row.slug)
},
{
default: () => {
return $gettext(
'Updating app %{ app } may reset related configurations to default state, are you sure to continue?',
{ app: row.name }
)
},
trigger: () => {
return h(
NButton,
{
size: 'small',
type: 'warning'
},
{
default: () => $gettext('Update')
}
)
}
}
)
: null,
row.installed
? h(
NButton,
{
size: 'small',
type: 'success',
onClick: () => handleManage(row.slug)
},
{
default: () => $gettext('Manage')
}
)
: null,
row.installed
? h(
NPopconfirm,
{
onPositiveClick: () => handleUninstall(row.slug)
},
{
default: () => {
return $gettext('Are you sure to uninstall app %{ app }?', { app: row.name })
},
trigger: () => {
return h(
NButton,
{
size: 'small',
type: 'error'
},
{
default: () => $gettext('Uninstall')
}
)
}
}
)
: null,
!row.installed
? h(
NButton,
{
size: 'small',
type: 'info',
onClick: () => {
versionModalShow.value = true
versionModalOperation.value = $gettext('Install')
versionModalInfo.value = row
}
},
{
default: () => $gettext('Install')
}
)
: null
]
})
}
}
]
const { loading, data, page, total, pageSize, pageCount, refresh } = usePagination(
(page, pageSize) => app.list(page, pageSize),
{
initialData: { total: 0, list: [] },
initialPageSize: 20,
total: (res: any) => res.total,
data: (res: any) => res.items
}
)
const handleShowChange = (row: any) => {
useRequest(app.updateShow(row.slug, !row.show)).onSuccess(() => {
row.show = !row.show
window.$message.success($gettext('Setup successfully'))
})
}
const handleUpdate = (slug: string) => {
useRequest(app.update(slug)).onSuccess(() => {
window.$message.success(
$gettext('Task submitted, please check the progress in background tasks')
)
})
}
const handleUninstall = (slug: string) => {
useRequest(app.uninstall(slug)).onSuccess(() => {
window.$message.success(
$gettext('Task submitted, please check the progress in background tasks')
)
})
}
const handleManage = (slug: string) => {
router.push({ name: 'apps-' + slug + '-index' })
}
const handleUpdateCache = () => {
useRequest(app.updateCache()).onSuccess(() => {
refresh()
window.$message.success($gettext('Cache updated successfully'))
})
}
onMounted(() => {
refresh()
})
const currentTab = ref('installed')
</script>
<template>
<common-page show-footer>
<n-flex vertical>
<n-flex>
<n-button type="primary" @click="handleUpdateCache">
{{ $gettext('Update Cache') }}
</n-button>
</n-flex>
<n-alert type="warning">{{
$gettext(
'Before updating apps, it is strongly recommended to backup/snapshot first, so you can roll back immediately if there are any issues!'
)
}}</n-alert>
<n-data-table
striped
remote
:scroll-x="1200"
:loading="loading"
:columns="columns"
:data="data"
:row-key="(row: any) => row.slug"
v-model:page="page"
v-model:pageSize="pageSize"
:pagination="{
page: page,
pageCount: pageCount,
pageSize: pageSize,
itemCount: total,
showQuickJumper: true,
showSizePicker: true,
pageSizes: [20, 50, 100, 200]
}"
/>
</n-flex>
<version-modal
v-model:show="versionModalShow"
v-model:operation="versionModalOperation"
v-model:info="versionModalInfo"
/>
<common-page show-header show-footer>
<template #tabbar>
<n-tabs v-model:value="currentTab" animated>
<n-tab name="installed" :tab="$gettext('Installed')" />
<n-tab name="install" :tab="$gettext('Install')" />
<n-tab name="environment" :tab="$gettext('Environment')" />
<n-tab name="compose" :tab="$gettext('Compose Templates')" />
</n-tabs>
</template>
<install-view v-if="currentTab === 'install'" />
</common-page>
</template>

View File

@@ -0,0 +1,249 @@
<script setup lang="ts">
defineOptions({
name: 'app-index'
})
import VersionModal from '@/views/app/VersionModal.vue'
import { NButton, NDataTable, NFlex, NPopconfirm, NSwitch } from 'naive-ui'
import { useGettext } from 'vue3-gettext'
import app from '@/api/panel/app'
import TheIcon from '@/components/custom/TheIcon.vue'
import { router } from '@/router'
const { $gettext } = useGettext()
const versionModalShow = ref(false)
const versionModalOperation = ref($gettext('Install'))
const versionModalInfo = ref<any>({})
const columns: any = [
{
key: 'icon',
fixed: 'left',
width: 80,
align: 'center',
render(row: any) {
return h(TheIcon, {
icon: row.icon,
size: 26
})
}
},
{
title: $gettext('App Name'),
key: 'name',
width: 200,
ellipsis: { tooltip: true }
},
{
title: $gettext('Description'),
key: 'description',
minWidth: 300,
ellipsis: { tooltip: true }
},
{
title: $gettext('Installed Version'),
key: 'installed_version',
width: 160,
ellipsis: { tooltip: true }
},
{
title: $gettext('Show in Home'),
key: 'show',
width: 140,
render(row: any) {
return h(NSwitch, {
size: 'small',
rubberBand: false,
value: row.show,
onUpdateValue: () => handleShowChange(row)
})
}
},
{
title: $gettext('Actions'),
key: 'actions',
width: 350,
hideInExcel: true,
render(row: any) {
return h(NFlex, null, {
default: () => [
row.installed && row.update_exist
? h(
NPopconfirm,
{
onPositiveClick: () => handleUpdate(row.slug)
},
{
default: () => {
return $gettext(
'Updating app %{ app } may reset related configurations to default state, are you sure to continue?',
{ app: row.name }
)
},
trigger: () => {
return h(
NButton,
{
size: 'small',
type: 'warning'
},
{
default: () => $gettext('Update')
}
)
}
}
)
: null,
row.installed
? h(
NButton,
{
size: 'small',
type: 'success',
onClick: () => handleManage(row.slug)
},
{
default: () => $gettext('Manage')
}
)
: null,
row.installed
? h(
NPopconfirm,
{
onPositiveClick: () => handleUninstall(row.slug)
},
{
default: () => {
return $gettext('Are you sure to uninstall app %{ app }?', { app: row.name })
},
trigger: () => {
return h(
NButton,
{
size: 'small',
type: 'error'
},
{
default: () => $gettext('Uninstall')
}
)
}
}
)
: null,
!row.installed
? h(
NButton,
{
size: 'small',
type: 'info',
onClick: () => {
versionModalShow.value = true
versionModalOperation.value = $gettext('Install')
versionModalInfo.value = row
}
},
{
default: () => $gettext('Install')
}
)
: null
]
})
}
}
]
const { loading, data, page, total, pageSize, pageCount, refresh } = usePagination(
(page, pageSize) => app.list(page, pageSize),
{
initialData: { total: 0, list: [] },
initialPageSize: 20,
total: (res: any) => res.total,
data: (res: any) => res.items
}
)
const handleShowChange = (row: any) => {
useRequest(app.updateShow(row.slug, !row.show)).onSuccess(() => {
row.show = !row.show
window.$message.success($gettext('Setup successfully'))
})
}
const handleUpdate = (slug: string) => {
useRequest(app.update(slug)).onSuccess(() => {
window.$message.success(
$gettext('Task submitted, please check the progress in background tasks')
)
})
}
const handleUninstall = (slug: string) => {
useRequest(app.uninstall(slug)).onSuccess(() => {
window.$message.success(
$gettext('Task submitted, please check the progress in background tasks')
)
})
}
const handleManage = (slug: string) => {
router.push({ name: 'apps-' + slug + '-index' })
}
const handleUpdateCache = () => {
useRequest(app.updateCache()).onSuccess(() => {
refresh()
window.$message.success($gettext('Cache updated successfully'))
})
}
onMounted(() => {
refresh()
})
</script>
<template>
<n-flex vertical>
<n-flex>
<n-button type="primary" @click="handleUpdateCache">
{{ $gettext('Update Cache') }}
</n-button>
</n-flex>
<n-alert type="warning">{{
$gettext(
'Before updating apps, it is strongly recommended to backup/snapshot first, so you can roll back immediately if there are any issues!'
)
}}</n-alert>
<n-data-table
striped
remote
:scroll-x="1200"
:loading="loading"
:columns="columns"
:data="data"
:row-key="(row: any) => row.slug"
v-model:page="page"
v-model:pageSize="pageSize"
:pagination="{
page: page,
pageCount: pageCount,
pageSize: pageSize,
itemCount: total,
showQuickJumper: true,
showSizePicker: true,
pageSizes: [20, 50, 100, 200]
}"
/>
</n-flex>
<version-modal
v-model:show="versionModalShow"
v-model:operation="versionModalOperation"
v-model:info="versionModalInfo"
/>
</template>

View File

@@ -7,7 +7,7 @@ export default {
path: '/app',
component: Layout,
meta: {
order: 90
order: 1
},
children: [
{

View File

@@ -93,9 +93,9 @@ onUnmounted(() => {
<common-page show-header show-footer>
<template #tabbar>
<n-tabs v-model:value="currentTab" animated>
<n-tab name="cert" :tab="$gettext('Certificate List')" />
<n-tab name="account" :tab="$gettext('Account List')" />
<n-tab name="dns" :tab="$gettext('DNS List')" />
<n-tab name="cert" :tab="$gettext('Certificate')" />
<n-tab name="account" :tab="$gettext('Account')" />
<n-tab name="dns" :tab="$gettext('DNS')" />
</n-tabs>
</template>
<n-flex vertical>

View File

@@ -7,7 +7,7 @@ export default {
path: '/container',
component: Layout,
meta: {
order: 40
order: 20
},
children: [
{

View File

@@ -7,7 +7,7 @@ export default {
path: '/database',
component: Layout,
meta: {
order: 2
order: 4
},
children: [
{

View File

@@ -7,7 +7,7 @@ export default {
path: '/firewall',
component: Layout,
meta: {
order: 30
order: 40
},
children: [
{

View File

@@ -7,7 +7,7 @@ export default {
path: '/monitor',
component: Layout,
meta: {
order: 20
order: 30
},
children: [
{

View File

@@ -0,0 +1,21 @@
<script lang="ts" setup>
defineOptions({
name: 'project-index'
})
const currentTab = ref('general')
</script>
<template>
<common-page show-header show-footer>
<template #tabbar>
<n-tabs v-model:value="currentTab" animated>
<n-tab name="general" :tab="$gettext('General')" />
<n-tab name="php" :tab="$gettext('PHP')" />
<n-tab name="java" :tab="$gettext('Java')" />
<n-tab name="python" :tab="$gettext('Python')" />
<n-tab name="nodejs" :tab="$gettext('Node.js')" />
</n-tabs>
</template>
</common-page>
</template>

View File

@@ -0,0 +1,25 @@
import type { RouteType } from '~/types/router'
const Layout = () => import('@/layout/IndexView.vue')
export default {
name: 'project',
path: '/project',
component: Layout,
meta: {
order: 3
},
children: [
{
name: 'project-index',
path: '',
component: () => import('./IndexView.vue'),
meta: {
title: 'Projects',
icon: 'mdi:folder-multiple',
role: ['admin'],
requireAuth: true
}
}
]
} as RouteType

View File

@@ -7,7 +7,7 @@ export default {
path: '/website',
component: Layout,
meta: {
order: 1
order: 2
},
children: [
{