2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 05:31:44 +08:00
Files
panel/web/src/views/setting/SettingBase.vue
2026-01-29 23:37:49 +08:00

185 lines
6.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import type { TreeSelectOption } from 'naive-ui'
import PathSelector from '@/components/common/PathSelector.vue'
import { translateTitle } from '@/locales/menu'
import { usePermissionStore } from '@/store'
import { locales as availableLocales } from '@/utils'
import { useGettext } from 'vue3-gettext'
import type { RouteType } from '~/types/router'
const { $gettext } = useGettext()
const permissionStore = usePermissionStore()
const model = defineModel<any>('model', { type: Object, required: true })
// 目录选择器
const showPathSelector = ref(false)
const pathSelectorPath = ref('/opt/ace')
const pathSelectorTarget = ref<'website' | 'backup' | 'project'>('website')
const handleSelectPath = (target: 'website' | 'backup' | 'project') => {
pathSelectorTarget.value = target
if (target === 'website') {
pathSelectorPath.value = model.value.website_path || '/opt/ace/sites'
} else if (target === 'backup') {
pathSelectorPath.value = model.value.backup_path || '/opt/ace/backup'
} else {
pathSelectorPath.value = model.value.project_path || '/opt/ace/projects'
}
showPathSelector.value = true
}
watch(showPathSelector, (val) => {
if (!val && pathSelectorPath.value) {
if (pathSelectorTarget.value === 'website') {
model.value.website_path = pathSelectorPath.value
} else if (pathSelectorTarget.value === 'backup') {
model.value.backup_path = pathSelectorPath.value
} else {
model.value.project_path = pathSelectorPath.value
}
}
})
const locales = computed(() => {
return Object.entries(availableLocales).map(([code, name]: [string, string]) => {
return {
label: name,
value: code
}
})
})
const channels = [
{
label: $gettext('Stable'),
value: 'stable'
},
{
label: $gettext('Beta'),
value: 'beta'
}
]
// 不允许隐藏的菜单项(首页 home/home-index 和设置页 setting/setting-index
const forbiddenHiddenMenus = ['home', 'home-index', 'setting', 'setting-index']
// 获取菜单选项
const getOption = (route: RouteType): TreeSelectOption => {
const isDisabled = forbiddenHiddenMenus.includes(route.name as string)
let menuItem: TreeSelectOption = {
label: route.meta?.title ? translateTitle(route.meta.title) : route.name,
key: route.name,
disabled: isDisabled
}
const visibleChildren = route.children
? route.children.filter((item: RouteType) => item.name && !item.isHidden)
: []
if (!visibleChildren.length) return menuItem
if (visibleChildren.length === 1) {
// 单个子路由处理
const singleRoute = visibleChildren[0]
if (!singleRoute) return menuItem
const isSingleDisabled = forbiddenHiddenMenus.includes(singleRoute.name as string)
menuItem.label = singleRoute.meta?.title
? translateTitle(singleRoute.meta.title)
: singleRoute.name
// 父路由或子路由任一被禁止则禁用该菜单项
menuItem.disabled = isDisabled || isSingleDisabled
const visibleItems = singleRoute.children
? singleRoute.children.filter((item: RouteType) => item.name && !item.isHidden)
: []
const firstVisibleItem = visibleItems[0]
if (visibleItems.length === 1 && firstVisibleItem) menuItem = getOption(firstVisibleItem)
else if (visibleItems.length > 1)
menuItem.children = visibleItems.map((item) => getOption(item))
} else {
menuItem.children = visibleChildren.map((item) => getOption(item))
}
return menuItem
}
const menus = computed<TreeSelectOption[]>(() => {
return permissionStore.allMenus.map((item) => getOption(item))
})
</script>
<template>
<n-flex vertical>
<n-form>
<n-form-item :label="$gettext('Panel Name')">
<n-input v-model:value="model.name" :placeholder="$gettext('Panel Name')" />
</n-form-item>
<n-form-item :label="$gettext('Language')">
<n-select v-model:value="model.locale" :options="locales"> </n-select>
</n-form-item>
<n-form-item :label="$gettext('Update Channel')">
<n-select v-model:value="model.channel" :options="channels"> </n-select>
</n-form-item>
<n-form-item :label="$gettext('Port')">
<n-input-number v-model:value="model.port" :placeholder="$gettext('8888')" w-full />
</n-form-item>
<n-form-item :label="$gettext('Default Website Directory')">
<n-input-group>
<n-input v-model:value="model.website_path" :placeholder="$gettext('/opt/ace/sites')" />
<n-button @click="handleSelectPath('website')">
<template #icon>
<i-mdi-folder-open />
</template>
</n-button>
</n-input-group>
</n-form-item>
<n-form-item :label="$gettext('Default Backup Directory')">
<n-input-group>
<n-input v-model:value="model.backup_path" :placeholder="$gettext('/opt/ace/backup')" />
<n-button @click="handleSelectPath('backup')">
<template #icon>
<i-mdi-folder-open />
</template>
</n-button>
</n-input-group>
</n-form-item>
<n-form-item :label="$gettext('Default Project Directory')">
<n-input-group>
<n-input
v-model:value="model.project_path"
:placeholder="$gettext('/opt/ace/projects')"
/>
<n-button @click="handleSelectPath('project')">
<template #icon>
<i-mdi-folder-open />
</template>
</n-button>
</n-input-group>
</n-form-item>
<n-form-item :label="$gettext('Custom Logo')">
<n-input
v-model:value="model.custom_logo"
:placeholder="$gettext('Please enter the complete URL')"
/>
</n-form-item>
<n-form-item :label="$gettext('Hide Menu')">
<n-tree-select
cascade
checkable
clearable
multiple
:options="menus"
v-model:value="model.hidden_menu"
/>
</n-form-item>
</n-form>
</n-flex>
<!-- 目录选择器 -->
<path-selector v-model:show="showPathSelector" v-model:path="pathSelectorPath" :dir="true" />
</template>
<style scoped lang="scss"></style>