mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 07:57:21 +08:00
feat: 去掉没用的图标
This commit is contained in:
@@ -148,20 +148,17 @@ onMounted(() => {
|
||||
</n-alert>
|
||||
<n-flex>
|
||||
<n-button type="success" v-model:disabled="fetchingStatus" @click="handleStart">
|
||||
<the-icon :size="24" icon="material-symbols:play-arrow-outline-rounded" />
|
||||
{{ $gettext('Start') }}
|
||||
</n-button>
|
||||
<n-popconfirm @positive-click="handleStop">
|
||||
<template #trigger>
|
||||
<n-button type="error" v-model:disabled="fetchingStatus">
|
||||
<the-icon :size="24" icon="material-symbols:stop-outline-rounded" />
|
||||
{{ $gettext('Stop') }}
|
||||
</n-button>
|
||||
</template>
|
||||
{{ $gettext('Are you sure you want to stop %{ service }?', { service: props.service }) }}
|
||||
</n-popconfirm>
|
||||
<n-button type="warning" v-model:disabled="fetchingStatus" @click="handleRestart">
|
||||
<the-icon :size="18" icon="material-symbols:replay-rounded" />
|
||||
{{ $gettext('Restart') }}
|
||||
</n-button>
|
||||
<n-button
|
||||
@@ -170,7 +167,6 @@ onMounted(() => {
|
||||
v-model:disabled="fetchingStatus"
|
||||
@click="handleReload"
|
||||
>
|
||||
<the-icon :size="20" icon="material-symbols:refresh-rounded" />
|
||||
{{ $gettext('Reload') }}
|
||||
</n-button>
|
||||
</n-flex>
|
||||
|
||||
@@ -11,7 +11,6 @@ import { useGettext } from 'vue3-gettext'
|
||||
import app from '@/api/panel/app'
|
||||
import TheIcon from '@/components/custom/TheIcon.vue'
|
||||
import { router } from '@/router'
|
||||
import { renderIcon } from '@/utils'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
|
||||
@@ -93,10 +92,7 @@ const columns: any = [
|
||||
type: 'warning'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Update'),
|
||||
icon: renderIcon('material-symbols:arrow-circle-up-outline-rounded', {
|
||||
size: 14
|
||||
})
|
||||
default: () => $gettext('Update')
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -112,8 +108,7 @@ const columns: any = [
|
||||
onClick: () => handleManage(row.slug)
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Manage'),
|
||||
icon: renderIcon('material-symbols:settings-outline', { size: 14 })
|
||||
default: () => $gettext('Manage')
|
||||
}
|
||||
)
|
||||
: null,
|
||||
@@ -135,8 +130,7 @@ const columns: any = [
|
||||
type: 'error'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Uninstall'),
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
|
||||
default: () => $gettext('Uninstall')
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -156,8 +150,7 @@ const columns: any = [
|
||||
}
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Install'),
|
||||
icon: renderIcon('material-symbols:download-rounded', { size: 14 })
|
||||
default: () => $gettext('Install')
|
||||
}
|
||||
)
|
||||
: null
|
||||
@@ -220,7 +213,6 @@ onMounted(() => {
|
||||
<common-page show-footer>
|
||||
<template #action>
|
||||
<n-button type="primary" @click="handleUpdateCache">
|
||||
<the-icon :size="18" icon="material-symbols:refresh" />
|
||||
{{ $gettext('Update Cache') }}
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
@@ -35,7 +35,6 @@ const handleSaveConfig = () => {
|
||||
type="primary"
|
||||
@click="handleSaveConfig"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:save-outline" />
|
||||
{{ $gettext('Save') }}
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
@@ -35,7 +35,6 @@ const handleSaveConfig = () => {
|
||||
type="primary"
|
||||
@click="handleSaveConfig"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:save-outline" />
|
||||
{{ $gettext('Save') }}
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
@@ -10,7 +10,6 @@ import fail2ban from '@/api/apps/fail2ban'
|
||||
import app from '@/api/panel/app'
|
||||
import website from '@/api/panel/website'
|
||||
import ServiceStatus from '@/components/common/ServiceStatus.vue'
|
||||
import { renderIcon } from '@/utils'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
const currentTab = ref('status')
|
||||
@@ -75,8 +74,7 @@ const jailsColumns: any = [
|
||||
}
|
||||
},
|
||||
{
|
||||
default: () => $gettext('View'),
|
||||
icon: renderIcon('material-symbols:visibility', { size: 14 })
|
||||
default: () => $gettext('View')
|
||||
}
|
||||
),
|
||||
h(
|
||||
@@ -97,8 +95,7 @@ const jailsColumns: any = [
|
||||
style: 'margin-left: 15px'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Delete'),
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
|
||||
default: () => $gettext('Delete')
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -141,8 +138,7 @@ const banedIPColumns: any = [
|
||||
type: 'error'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Unban'),
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
|
||||
default: () => $gettext('Unban')
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -235,7 +231,6 @@ onMounted(() => {
|
||||
type="primary"
|
||||
@click="handleSaveWhiteList"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:save-outline" />
|
||||
{{ $gettext('Save Whitelist') }}
|
||||
</n-button>
|
||||
<n-button
|
||||
@@ -244,7 +239,6 @@ onMounted(() => {
|
||||
type="primary"
|
||||
@click="addJailModal = true"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:add" />
|
||||
{{ $gettext('Add Rule') }}
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
@@ -44,7 +44,6 @@ onMounted(() => {
|
||||
<n-card :title="$gettext('Modify Configuration')">
|
||||
<template #header-extra>
|
||||
<n-button type="primary" @click="handleSaveConfig('frps')">
|
||||
<the-icon :size="18" icon="material-symbols:save-outline-rounded" />
|
||||
{{ $gettext('Save') }}
|
||||
</n-button>
|
||||
</template>
|
||||
@@ -69,7 +68,6 @@ onMounted(() => {
|
||||
<n-card :title="$gettext('Modify Configuration')">
|
||||
<template #header-extra>
|
||||
<n-button type="primary" @click="handleSaveConfig('frpc')">
|
||||
<the-icon :size="18" icon="material-symbols:save-outline-rounded" />
|
||||
{{ $gettext('Save') }}
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
@@ -33,7 +33,6 @@ const handleSaveConfig = () => {
|
||||
type="primary"
|
||||
@click="handleSaveConfig"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:save-outline" />
|
||||
{{ $gettext('Save') }}
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
@@ -55,7 +55,6 @@ const handleSaveConfig = () => {
|
||||
type="primary"
|
||||
@click="handleSaveConfig"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:save-outline" />
|
||||
{{ $gettext('Save') }}
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
@@ -28,7 +28,6 @@ const handleSaveEnv = () => {
|
||||
<common-page show-footer>
|
||||
<template #action>
|
||||
<n-button v-if="currentTab == 'env'" class="ml-16" type="primary" @click="handleSaveEnv">
|
||||
<the-icon :size="18" icon="material-symbols:save-outline" />
|
||||
{{ $gettext('Save') }}
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
@@ -75,7 +75,6 @@ const handleSetRootPassword = async () => {
|
||||
type="primary"
|
||||
@click="handleSaveConfig"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:save-outline" />
|
||||
{{ $gettext('Save') }}
|
||||
</n-button>
|
||||
<n-button
|
||||
@@ -84,7 +83,6 @@ const handleSetRootPassword = async () => {
|
||||
type="primary"
|
||||
@click="handleClearErrorLog"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:delete-outline" />
|
||||
{{ $gettext('Clear Log') }}
|
||||
</n-button>
|
||||
<n-button
|
||||
@@ -93,7 +91,6 @@ const handleSetRootPassword = async () => {
|
||||
type="primary"
|
||||
@click="handleClearSlowLog"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:delete-outline" />
|
||||
{{ $gettext('Clear Slow Log') }}
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
@@ -61,7 +61,6 @@ const handleClearErrorLog = () => {
|
||||
type="primary"
|
||||
@click="handleSaveConfig"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:save-outline" />
|
||||
{{ $gettext('Save') }}
|
||||
</n-button>
|
||||
<n-button
|
||||
@@ -70,7 +69,6 @@ const handleClearErrorLog = () => {
|
||||
type="primary"
|
||||
@click="handleClearErrorLog"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:delete-outline" />
|
||||
{{ $gettext('Clear Log') }}
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
@@ -5,7 +5,6 @@ import { useGettext } from 'vue3-gettext'
|
||||
|
||||
import php from '@/api/apps/php'
|
||||
import ServiceStatus from '@/components/common/ServiceStatus.vue'
|
||||
import { renderIcon } from '@/utils'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
const props = defineProps({
|
||||
@@ -78,8 +77,7 @@ const extensionColumns: any = [
|
||||
type: 'info'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Install'),
|
||||
icon: renderIcon('material-symbols:download-rounded', { size: 14 })
|
||||
default: () => $gettext('Install')
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -106,8 +104,7 @@ const extensionColumns: any = [
|
||||
type: 'error'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Delete'),
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
|
||||
default: () => $gettext('Delete')
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -190,7 +187,6 @@ const handleUninstallExtension = async (name: string) => {
|
||||
type="primary"
|
||||
@click="handleSaveConfig"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:save-outline" />
|
||||
{{ $gettext('Save') }}
|
||||
</n-button>
|
||||
<n-button
|
||||
@@ -199,7 +195,6 @@ const handleUninstallExtension = async (name: string) => {
|
||||
type="primary"
|
||||
@click="handleSaveFPMConfig"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:save-outline" />
|
||||
{{ $gettext('Save') }}
|
||||
</n-button>
|
||||
<n-button
|
||||
@@ -208,7 +203,6 @@ const handleUninstallExtension = async (name: string) => {
|
||||
type="primary"
|
||||
@click="handleClearErrorLog"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:delete-outline" />
|
||||
{{ $gettext('Clear Error Log') }}
|
||||
</n-button>
|
||||
<n-button
|
||||
@@ -217,7 +211,6 @@ const handleUninstallExtension = async (name: string) => {
|
||||
type="primary"
|
||||
@click="handleClearSlowLog"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:delete-outline" />
|
||||
{{ $gettext('Clear Slow Log') }}
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
@@ -54,7 +54,6 @@ onMounted(() => {
|
||||
<common-page show-footer>
|
||||
<template #action>
|
||||
<n-button v-if="currentTab == 'status'" class="ml-16" type="primary" @click="handleSave">
|
||||
<the-icon :size="18" icon="material-symbols:save-outline" />
|
||||
{{ $gettext('Save') }}
|
||||
</n-button>
|
||||
<n-button
|
||||
@@ -63,7 +62,6 @@ onMounted(() => {
|
||||
type="primary"
|
||||
@click="handleSaveConfig"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:save-outline" />
|
||||
{{ $gettext('Save') }}
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
@@ -43,7 +43,6 @@ const handleSaveStorageConfig = () => {
|
||||
type="primary"
|
||||
@click="handleSaveRegistryConfig"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:save-outline" />
|
||||
{{ $gettext('Save') }}
|
||||
</n-button>
|
||||
<n-button
|
||||
@@ -52,7 +51,6 @@ const handleSaveStorageConfig = () => {
|
||||
type="primary"
|
||||
@click="handleSaveStorageConfig"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:save-outline" />
|
||||
{{ $gettext('Save') }}
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
@@ -67,7 +67,6 @@ const handleClearLog = async () => {
|
||||
type="primary"
|
||||
@click="handleSaveConfig"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:save-outline" />
|
||||
{{ $gettext('Save') }}
|
||||
</n-button>
|
||||
<n-button
|
||||
@@ -76,11 +75,9 @@ const handleClearLog = async () => {
|
||||
type="primary"
|
||||
@click="handleSaveUserConfig"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:save-outline" />
|
||||
{{ $gettext('Save') }}
|
||||
</n-button>
|
||||
<n-button v-if="currentTab == 'log'" class="ml-16" type="primary" @click="handleClearLog">
|
||||
<the-icon :size="18" icon="material-symbols:delete-outline" />
|
||||
{{ $gettext('Clear Log') }}
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useGettext } from 'vue3-gettext'
|
||||
|
||||
import pureftpd from '@/api/apps/pureftpd'
|
||||
import ServiceStatus from '@/components/common/ServiceStatus.vue'
|
||||
import { generateRandomString, renderIcon } from '@/utils'
|
||||
import { generateRandomString } from '@/utils'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
const currentTab = ref('status')
|
||||
@@ -62,8 +62,7 @@ const userColumns: any = [
|
||||
}
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Change Password'),
|
||||
icon: renderIcon('material-symbols:key-outline', { size: 14 })
|
||||
default: () => $gettext('Change Password')
|
||||
}
|
||||
),
|
||||
h(
|
||||
@@ -86,8 +85,7 @@ const userColumns: any = [
|
||||
style: 'margin-left: 15px'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Delete'),
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
|
||||
default: () => $gettext('Delete')
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -158,7 +156,6 @@ onMounted(() => {
|
||||
<common-page show-footer>
|
||||
<template #action>
|
||||
<n-button v-if="currentTab == 'status'" class="ml-16" type="primary" @click="handleSavePort">
|
||||
<the-icon :size="18" icon="material-symbols:save-outline" />
|
||||
{{ $gettext('Save') }}
|
||||
</n-button>
|
||||
<n-button
|
||||
@@ -167,7 +164,6 @@ onMounted(() => {
|
||||
type="primary"
|
||||
@click="addUserModal = true"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:add" />
|
||||
{{ $gettext('Add User') }}
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
@@ -52,7 +52,6 @@ const handleSaveConfig = () => {
|
||||
type="primary"
|
||||
@click="handleSaveConfig"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:save-outline" />
|
||||
{{ $gettext('Save') }}
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useGettext } from 'vue3-gettext'
|
||||
|
||||
import rsync from '@/api/apps/rsync'
|
||||
import ServiceStatus from '@/components/common/ServiceStatus.vue'
|
||||
import { generateRandomString, renderIcon } from '@/utils'
|
||||
import { generateRandomString } from '@/utils'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
const currentTab = ref('status')
|
||||
@@ -80,8 +80,7 @@ const processColumns: any = [
|
||||
onClick: () => handleModelEdit(row)
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Configure'),
|
||||
icon: renderIcon('material-symbols:settings-outline', { size: 14 })
|
||||
default: () => $gettext('Configure')
|
||||
}
|
||||
),
|
||||
h(
|
||||
@@ -104,8 +103,7 @@ const processColumns: any = [
|
||||
style: 'margin-left: 15px'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Delete'),
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
|
||||
default: () => $gettext('Delete')
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -197,7 +195,6 @@ onMounted(() => {
|
||||
type="primary"
|
||||
@click="handleSaveConfig"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:save-outline" />
|
||||
{{ $gettext('Save') }}
|
||||
</n-button>
|
||||
<n-button
|
||||
@@ -206,7 +203,6 @@ onMounted(() => {
|
||||
type="primary"
|
||||
@click="addModuleModal = true"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:add" />
|
||||
{{ $gettext('Add Module') }}
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
@@ -7,7 +7,6 @@ import { NButton, NDataTable, NInput, NPopconfirm } from 'naive-ui'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
|
||||
import s3fs from '@/api/apps/s3fs'
|
||||
import { renderIcon } from '@/utils'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
const addMountModal = ref(false)
|
||||
@@ -55,8 +54,7 @@ const columns: any = [
|
||||
type: 'error'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Unmount'),
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
|
||||
default: () => $gettext('Unmount')
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -101,7 +99,6 @@ onMounted(() => {
|
||||
<common-page show-footer>
|
||||
<template #action>
|
||||
<n-button class="ml-16" type="primary" @click="addMountModal = true">
|
||||
<the-icon :size="18" icon="material-symbols:add" />
|
||||
{{ $gettext('Add Mount') }}
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
@@ -9,7 +9,6 @@ import { useGettext } from 'vue3-gettext'
|
||||
|
||||
import supervisor from '@/api/apps/supervisor'
|
||||
import ServiceStatus from '@/components/common/ServiceStatus.vue'
|
||||
import { renderIcon } from '@/utils'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
const currentTab = ref('status')
|
||||
@@ -88,8 +87,7 @@ const processColumns: any = [
|
||||
onClick: () => handleShowProcessLog(row)
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Logs'),
|
||||
icon: renderIcon('material-symbols:visibility', { size: 14 })
|
||||
default: () => $gettext('Logs')
|
||||
}
|
||||
),
|
||||
h(
|
||||
@@ -101,8 +99,7 @@ const processColumns: any = [
|
||||
onClick: () => handleEditProcess(row.name)
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Configure'),
|
||||
icon: renderIcon('material-symbols:settings-outline', { size: 14 })
|
||||
default: () => $gettext('Configure')
|
||||
}
|
||||
),
|
||||
row.status != 'RUNNING'
|
||||
@@ -116,8 +113,7 @@ const processColumns: any = [
|
||||
onClick: () => handleProcessStart(row.name)
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Start'),
|
||||
icon: renderIcon('material-symbols:play-arrow-outline', { size: 18 })
|
||||
default: () => $gettext('Start')
|
||||
}
|
||||
)
|
||||
: null,
|
||||
@@ -142,8 +138,7 @@ const processColumns: any = [
|
||||
style: 'margin-left: 15px'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Stop'),
|
||||
icon: renderIcon('material-symbols:stop-outline', { size: 18 })
|
||||
default: () => $gettext('Stop')
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -171,8 +166,7 @@ const processColumns: any = [
|
||||
style: 'margin-left: 15px'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Restart'),
|
||||
icon: renderIcon('material-symbols:replay', { size: 18 })
|
||||
default: () => $gettext('Restart')
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -199,8 +193,7 @@ const processColumns: any = [
|
||||
style: 'margin-left: 15px'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Delete'),
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
|
||||
default: () => $gettext('Delete')
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -309,7 +302,6 @@ onUnmounted(() => {
|
||||
type="primary"
|
||||
@click="handleSaveConfig"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:save-outline" />
|
||||
{{ $gettext('Save') }}
|
||||
</n-button>
|
||||
<n-button
|
||||
@@ -318,11 +310,9 @@ onUnmounted(() => {
|
||||
type="primary"
|
||||
@click="createProcessModal = true"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:add" />
|
||||
{{ $gettext('Add Process') }}
|
||||
</n-button>
|
||||
<n-button v-if="currentTab == 'log'" class="ml-16" type="primary" @click="handleClearLog">
|
||||
<the-icon :size="18" icon="material-symbols:delete-outline" />
|
||||
{{ $gettext('Clear Log') }}
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import backup from '@/api/panel/backup'
|
||||
import { renderIcon } from '@/utils'
|
||||
import type { MessageReactive } from 'naive-ui'
|
||||
import { NButton, NDataTable, NFlex, NInput, NPopconfirm } from 'naive-ui'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
@@ -73,8 +72,7 @@ const columns: any = [
|
||||
}
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Restore'),
|
||||
icon: renderIcon('material-symbols:settings-backup-restore-rounded', { size: 14 })
|
||||
default: () => $gettext('Restore')
|
||||
}
|
||||
),
|
||||
h(
|
||||
@@ -95,8 +93,7 @@ const columns: any = [
|
||||
style: 'margin-left: 15px;'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Delete'),
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
|
||||
default: () => $gettext('Delete')
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -101,19 +101,15 @@ onUnmounted(() => {
|
||||
<n-flex vertical>
|
||||
<n-flex>
|
||||
<n-button v-if="currentTab == 'cert'" type="success" @click="uploadCert = true">
|
||||
<the-icon :size="18" icon="material-symbols:upload" />
|
||||
{{ $gettext('Upload Certificate') }}
|
||||
</n-button>
|
||||
<n-button v-if="currentTab == 'cert'" type="primary" @click="createCert = true">
|
||||
<the-icon :size="18" icon="material-symbols:add" />
|
||||
{{ $gettext('Create Certificate') }}
|
||||
</n-button>
|
||||
<n-button v-if="currentTab == 'account'" type="primary" @click="createAccount = true">
|
||||
<the-icon :size="18" icon="material-symbols:add" />
|
||||
{{ $gettext('Create Account') }}
|
||||
</n-button>
|
||||
<n-button v-if="currentTab == 'dns'" type="primary" @click="createDNS = true">
|
||||
<the-icon :size="18" icon="material-symbols:add" />
|
||||
{{ $gettext('Create DNS') }}
|
||||
</n-button>
|
||||
</n-flex>
|
||||
|
||||
@@ -55,7 +55,6 @@ const handleUpdate = () => {
|
||||
<template #action>
|
||||
<div>
|
||||
<n-button v-if="versions" class="ml-16" type="primary" @click="handleUpdate">
|
||||
<the-icon :size="18" icon="material-symbols:arrow-circle-up-outline-rounded" />
|
||||
{{ $gettext('Update Now') }}
|
||||
</n-button>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
import { renderIcon } from '@/utils'
|
||||
import { NButton, NInput, NPopconfirm, NTag } from 'naive-ui'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
|
||||
@@ -95,8 +94,7 @@ const columns: any = [
|
||||
style: 'margin-left: 15px;'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Delete'),
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
|
||||
default: () => $gettext('Delete')
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -36,11 +36,9 @@ const createServerModalShow = ref(false)
|
||||
type="primary"
|
||||
@click="createDatabaseModalShow = true"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:add" />
|
||||
{{ $gettext('Create Database') }}
|
||||
</n-button>
|
||||
<n-button v-if="currentTab === 'user'" type="primary" @click="createUserModalShow = true">
|
||||
<the-icon :size="18" icon="material-symbols:add" />
|
||||
{{ $gettext('Create User') }}
|
||||
</n-button>
|
||||
<n-button
|
||||
@@ -48,7 +46,6 @@ const createServerModalShow = ref(false)
|
||||
type="primary"
|
||||
@click="createServerModalShow = true"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:add" />
|
||||
{{ $gettext('Add Server') }}
|
||||
</n-button>
|
||||
</n-flex>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
import { renderIcon } from '@/utils'
|
||||
import copy2clipboard from '@vavt/copy2clipboard'
|
||||
import { NButton, NInput, NInputGroup, NPopconfirm, NTag } from 'naive-ui'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
@@ -162,8 +161,7 @@ const columns: any = [
|
||||
type: 'success'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Sync'),
|
||||
icon: renderIcon('material-symbols:sync', { size: 14 })
|
||||
default: () => $gettext('Sync')
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -181,8 +179,7 @@ const columns: any = [
|
||||
}
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Modify'),
|
||||
icon: renderIcon('material-symbols:edit-outline', { size: 14 })
|
||||
default: () => $gettext('Modify')
|
||||
}
|
||||
),
|
||||
h(
|
||||
@@ -214,8 +211,7 @@ const columns: any = [
|
||||
style: 'margin-left: 15px;'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Delete'),
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
|
||||
default: () => $gettext('Delete')
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
import { renderIcon } from '@/utils'
|
||||
import copy2clipboard from '@vavt/copy2clipboard'
|
||||
import { NButton, NFlex, NInput, NInputGroup, NPopconfirm, NTag } from 'naive-ui'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
@@ -166,8 +165,7 @@ const columns: any = [
|
||||
}
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Modify'),
|
||||
icon: renderIcon('material-symbols:edit-outline', { size: 14 })
|
||||
default: () => $gettext('Modify')
|
||||
}
|
||||
),
|
||||
h(
|
||||
@@ -188,8 +186,7 @@ const columns: any = [
|
||||
style: 'margin-left: 15px;'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Delete'),
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
|
||||
default: () => $gettext('Delete')
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { NButton, NDataTable, NPopconfirm, NTag } from 'naive-ui'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
|
||||
import firewall from '@/api/panel/firewall'
|
||||
import { renderIcon } from '@/utils'
|
||||
import CreateForwardModal from '@/views/firewall/CreateForwardModal.vue'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
@@ -101,8 +100,7 @@ const columns: any = [
|
||||
style: 'margin-left: 15px;'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Delete'),
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
|
||||
default: () => $gettext('Delete')
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -162,13 +160,11 @@ onMounted(() => {
|
||||
<n-flex vertical :size="20">
|
||||
<n-flex items-center>
|
||||
<n-button type="primary" @click="createModalShow = true">
|
||||
<the-icon :size="18" icon="material-symbols:add" />
|
||||
{{ $gettext('Create Forwarding') }}
|
||||
</n-button>
|
||||
<n-popconfirm @positive-click="batchDelete">
|
||||
<template #trigger>
|
||||
<n-button type="warning">
|
||||
<the-icon :size="18" icon="material-symbols:delete-outline" />
|
||||
{{ $gettext('Batch Delete') }}
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
@@ -3,7 +3,6 @@ import { NButton, NDataTable, NPopconfirm, NTag } from 'naive-ui'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
|
||||
import firewall from '@/api/panel/firewall'
|
||||
import { renderIcon } from '@/utils'
|
||||
import CreateIpModal from '@/views/firewall/CreateIpModal.vue'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
@@ -143,8 +142,7 @@ const columns: any = [
|
||||
style: 'margin-left: 15px;'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Delete'),
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
|
||||
default: () => $gettext('Delete')
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -204,13 +202,11 @@ onMounted(() => {
|
||||
<n-flex vertical :size="20">
|
||||
<n-flex items-center>
|
||||
<n-button type="primary" @click="createModalShow = true">
|
||||
<the-icon :size="18" icon="material-symbols:add" />
|
||||
{{ $gettext('Create Rule') }}
|
||||
</n-button>
|
||||
<n-popconfirm @positive-click="batchDelete">
|
||||
<template #trigger>
|
||||
<n-button type="warning">
|
||||
<the-icon :size="18" icon="material-symbols:delete-outline" />
|
||||
{{ $gettext('Batch Delete') }}
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
@@ -3,7 +3,6 @@ import { NButton, NDataTable, NPopconfirm, NTag } from 'naive-ui'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
|
||||
import firewall from '@/api/panel/firewall'
|
||||
import { renderIcon } from '@/utils'
|
||||
import CreateModal from '@/views/firewall/CreateModal.vue'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
@@ -180,8 +179,7 @@ const columns: any = [
|
||||
style: 'margin-left: 15px;'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Delete'),
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
|
||||
default: () => $gettext('Delete')
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -241,13 +239,11 @@ onMounted(() => {
|
||||
<n-flex vertical :size="20">
|
||||
<n-flex items-center>
|
||||
<n-button type="primary" @click="createModalShow = true">
|
||||
<the-icon :size="18" icon="material-symbols:add" />
|
||||
{{ $gettext('Create Rule') }}
|
||||
</n-button>
|
||||
<n-popconfirm @positive-click="batchDelete">
|
||||
<template #trigger>
|
||||
<n-button type="warning">
|
||||
<the-icon :size="18" icon="material-symbols:delete-outline" />
|
||||
{{ $gettext('Batch Delete') }}
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
@@ -471,7 +471,6 @@ watch(data, () => {
|
||||
<n-popconfirm @positive-click="handleClear">
|
||||
<template #trigger>
|
||||
<n-button type="error">
|
||||
<the-icon :size="16" icon="material-symbols:delete-outline" />
|
||||
{{ $gettext('Clear Monitoring Records') }}
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
@@ -6,7 +6,6 @@ import { NButton } from 'naive-ui'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
|
||||
import setting from '@/api/panel/setting'
|
||||
import TheIcon from '@/components/custom/TheIcon.vue'
|
||||
import { useThemeStore } from '@/store'
|
||||
import CreateModal from '@/views/setting/CreateModal.vue'
|
||||
import SettingBase from '@/views/setting/SettingBase.vue'
|
||||
@@ -69,7 +68,6 @@ const handleCreate = () => {
|
||||
<n-flex vertical>
|
||||
<n-flex>
|
||||
<n-button v-if="currentTab == 'user'" type="primary" @click="handleCreate">
|
||||
<the-icon :size="18" icon="material-symbols:add" />
|
||||
{{ $gettext('Create User') }}
|
||||
</n-button>
|
||||
</n-flex>
|
||||
@@ -78,7 +76,6 @@ const handleCreate = () => {
|
||||
<setting-user v-if="currentTab === 'user'" />
|
||||
<n-flex>
|
||||
<n-button v-if="currentTab != 'user'" type="primary" @click="handleSave">
|
||||
<the-icon :size="18" icon="material-symbols:save-outline" />
|
||||
{{ $gettext('Save') }}
|
||||
</n-button>
|
||||
</n-flex>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import user from '@/api/panel/user'
|
||||
import { formatDateTime, renderIcon } from '@/utils'
|
||||
import { formatDateTime } from '@/utils'
|
||||
import PasswordModal from '@/views/setting/PasswordModal.vue'
|
||||
import TokenModal from '@/views/setting/TokenModal.vue'
|
||||
import TwoFaModal from '@/views/setting/TwoFaModal.vue'
|
||||
@@ -100,8 +100,7 @@ const columns: any = [
|
||||
}
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Access Tokens'),
|
||||
icon: renderIcon('material-symbols:vpn-key-outline', { size: 14 })
|
||||
default: () => $gettext('Access Tokens')
|
||||
}
|
||||
),
|
||||
h(
|
||||
@@ -116,8 +115,7 @@ const columns: any = [
|
||||
}
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Change Password'),
|
||||
icon: renderIcon('material-symbols:edit-outline', { size: 14 })
|
||||
default: () => $gettext('Change Password')
|
||||
}
|
||||
),
|
||||
h(
|
||||
@@ -139,8 +137,7 @@ const columns: any = [
|
||||
style: 'margin-left: 15px;'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Delete'),
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
|
||||
default: () => $gettext('Delete')
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import user from '@/api/panel/user'
|
||||
import { formatDateTime, renderIcon } from '@/utils'
|
||||
import { formatDateTime } from '@/utils'
|
||||
import copy2clipboard from '@vavt/copy2clipboard'
|
||||
import { NAlert, NButton, NDataTable, NFlex, NInput, NPopconfirm } from 'naive-ui'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
@@ -66,8 +66,7 @@ const columns: any = [
|
||||
}
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Modify'),
|
||||
icon: renderIcon('material-symbols:edit-outline', { size: 14 })
|
||||
default: () => $gettext('Modify')
|
||||
}
|
||||
),
|
||||
h(
|
||||
@@ -89,8 +88,7 @@ const columns: any = [
|
||||
style: 'margin-left: 15px;'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Delete'),
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
|
||||
default: () => $gettext('Delete')
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ defineOptions({
|
||||
|
||||
import ssh from '@/api/panel/ssh'
|
||||
import ws from '@/api/ws'
|
||||
import TheIcon from '@/components/custom/TheIcon.vue'
|
||||
import CreateModal from '@/views/ssh/CreateModal.vue'
|
||||
import UpdateModal from '@/views/ssh/UpdateModal.vue'
|
||||
import '@fontsource-variable/jetbrains-mono/wght-italic.css'
|
||||
@@ -243,7 +242,6 @@ onUnmounted(() => {
|
||||
>
|
||||
<div class="text-center">
|
||||
<n-button type="primary" @click="create = true">
|
||||
<the-icon :size="18" icon="material-symbols:add" />
|
||||
{{ $gettext('Create Host') }}
|
||||
</n-button>
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useGettext } from 'vue3-gettext'
|
||||
|
||||
import cron from '@/api/panel/cron'
|
||||
import file from '@/api/panel/file'
|
||||
import { decodeBase64, formatDateTime, renderIcon } from '@/utils'
|
||||
import { decodeBase64, formatDateTime } from '@/utils'
|
||||
import { CronNaive } from '@vue-js-cron/naive-ui'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
@@ -117,8 +117,7 @@ const columns: any = [
|
||||
}
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Logs'),
|
||||
icon: renderIcon('majesticons:eye-line', { size: 14 })
|
||||
default: () => $gettext('Logs')
|
||||
}
|
||||
),
|
||||
h(
|
||||
@@ -130,8 +129,7 @@ const columns: any = [
|
||||
onClick: () => handleEdit(row)
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Edit'),
|
||||
icon: renderIcon('material-symbols:edit-outline', { size: 14 })
|
||||
default: () => $gettext('Edit')
|
||||
}
|
||||
),
|
||||
h(
|
||||
@@ -152,8 +150,7 @@ const columns: any = [
|
||||
style: 'margin-left: 15px;'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Delete'),
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
|
||||
default: () => $gettext('Delete')
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ defineOptions({
|
||||
name: 'task-index'
|
||||
})
|
||||
|
||||
import TheIcon from '@/components/custom/TheIcon.vue'
|
||||
import CreateModal from '@/views/task/CreateModal.vue'
|
||||
import CronView from '@/views/task/CronView.vue'
|
||||
import SystemView from '@/views/task/SystemView.vue'
|
||||
@@ -29,7 +28,6 @@ const create = ref(false)
|
||||
<n-flex vertical>
|
||||
<n-flex>
|
||||
<n-button v-if="current == 'cron'" type="primary" @click="create = true">
|
||||
<the-icon :size="18" icon="material-symbols:add" />
|
||||
{{ $gettext('Create Task') }}
|
||||
</n-button>
|
||||
</n-flex>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { NButton, NDataTable, NPopconfirm, NTag } from 'naive-ui'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
|
||||
import process from '@/api/panel/process'
|
||||
import { formatBytes, formatDateTime, formatPercent, renderIcon } from '@/utils'
|
||||
import { formatBytes, formatDateTime, formatPercent } from '@/utils'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
|
||||
@@ -124,8 +124,7 @@ const columns: any = [
|
||||
type: 'error'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Terminate'),
|
||||
icon: renderIcon('material-symbols:stop-circle-outline-rounded', { size: 14 })
|
||||
default: () => $gettext('Terminate')
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useGettext } from 'vue3-gettext'
|
||||
|
||||
import task from '@/api/panel/task'
|
||||
import RealtimeLogModal from '@/components/common/RealtimeLogModal.vue'
|
||||
import { formatDateTime, renderIcon } from '@/utils'
|
||||
import { formatDateTime } from '@/utils'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
const logModal = ref(false)
|
||||
@@ -71,8 +71,7 @@ const columns: any = [
|
||||
}
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Logs'),
|
||||
icon: renderIcon('material-symbols:visibility', { size: 14 })
|
||||
default: () => $gettext('Logs')
|
||||
}
|
||||
)
|
||||
: null,
|
||||
@@ -95,8 +94,7 @@ const columns: any = [
|
||||
style: 'margin-left: 15px;'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Delete'),
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
|
||||
default: () => $gettext('Delete')
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -184,7 +184,6 @@ const onCreateListen = () => {
|
||||
<n-popconfirm v-if="current === 'config'" @positive-click="handleReset">
|
||||
<template #trigger>
|
||||
<n-button type="success">
|
||||
<the-icon :size="18" icon="material-symbols:refresh" />
|
||||
{{ $gettext('Reset Configuration') }}
|
||||
</n-button>
|
||||
</template>
|
||||
@@ -196,7 +195,6 @@ const onCreateListen = () => {
|
||||
type="success"
|
||||
@click="proxyBuilderModal = true"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:build-outline-rounded" />
|
||||
{{ $gettext('Generate Reverse Proxy Configuration') }}
|
||||
</n-button>
|
||||
<n-button
|
||||
@@ -207,17 +205,14 @@ const onCreateListen = () => {
|
||||
type="success"
|
||||
@click="handleObtainCert"
|
||||
>
|
||||
<the-icon :size="18" icon="material-symbols:done-rounded" />
|
||||
{{ $gettext('One-click Certificate Issuance') }}
|
||||
</n-button>
|
||||
<n-button v-if="current !== 'log'" class="ml-16" type="primary" @click="handleSave">
|
||||
<the-icon :size="18" icon="material-symbols:save-outline" />
|
||||
{{ $gettext('Save') }}
|
||||
</n-button>
|
||||
<n-popconfirm v-if="current === 'log'" @positive-click="clearLog">
|
||||
<template #trigger>
|
||||
<n-button type="primary">
|
||||
<the-icon :size="18" icon="material-symbols:delete-outline" />
|
||||
{{ $gettext('Clear Logs') }}
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
@@ -3,612 +3,23 @@ defineOptions({
|
||||
name: 'website-index'
|
||||
})
|
||||
|
||||
import Editor from '@guolao/vue-monaco-editor'
|
||||
import { NButton, NCheckbox, NDataTable, NFlex, NInput, NPopconfirm, NSwitch, NTag } from 'naive-ui'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
import PhpView from '@/views/website/PhpView.vue'
|
||||
import SettingView from '@/views/website/SettingView.vue'
|
||||
|
||||
import dashboard from '@/api/panel/dashboard'
|
||||
import website from '@/api/panel/website'
|
||||
import { useFileStore } from '@/store'
|
||||
import { generateRandomString, isNullOrUndef, renderIcon } from '@/utils'
|
||||
import BulkCreate from '@/views/website/BulkCreate.vue'
|
||||
|
||||
const fileStore = useFileStore()
|
||||
const { $gettext } = useGettext()
|
||||
const router = useRouter()
|
||||
const selectedRowKeys = ref<any>([])
|
||||
|
||||
const columns: any = [
|
||||
{ type: 'selection', fixed: 'left' },
|
||||
{
|
||||
title: $gettext('Website Name'),
|
||||
key: 'name',
|
||||
width: 200,
|
||||
resizable: true,
|
||||
ellipsis: { tooltip: true }
|
||||
},
|
||||
{
|
||||
title: $gettext('Running'),
|
||||
key: 'status',
|
||||
width: 150,
|
||||
render(row: any) {
|
||||
return h(NSwitch, {
|
||||
size: 'small',
|
||||
rubberBand: false,
|
||||
value: row.status,
|
||||
onUpdateValue: () => handleStatusChange(row)
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
title: $gettext('Directory'),
|
||||
key: 'path',
|
||||
minWidth: 200,
|
||||
resizable: true,
|
||||
render(row: any) {
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
class: 'cursor-pointer hover:opacity-60',
|
||||
type: 'info',
|
||||
onClick: () => {
|
||||
fileStore.path = row.path
|
||||
router.push({ name: 'file-index' })
|
||||
}
|
||||
},
|
||||
{ default: () => row.path }
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'HTTPS',
|
||||
key: 'https',
|
||||
width: 150,
|
||||
render(row: any) {
|
||||
return h(NSwitch, {
|
||||
size: 'small',
|
||||
rubberBand: false,
|
||||
value: row.https,
|
||||
onClick: () => handleEdit(row)
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
title: $gettext('Certificate expiration'),
|
||||
key: 'cert_expire',
|
||||
width: 200,
|
||||
render(row: any) {
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
type: row.cert_expire == 0 ? 'default' : row.cert_expire > 0 ? 'success' : 'error',
|
||||
class: 'cursor-pointer hover:opacity-60',
|
||||
onClick: () => handleEdit(row)
|
||||
},
|
||||
{
|
||||
default: () => {
|
||||
if (row.cert_expire == 0) {
|
||||
return $gettext('Not configured')
|
||||
}
|
||||
if (row.cert_expire < 0) {
|
||||
return $gettext('Expired %{ days } days ago', {
|
||||
days: Math.abs(row.cert_expire)
|
||||
})
|
||||
}
|
||||
if (row.cert_expire > 0) {
|
||||
return $gettext('Expires in %{ days } days', {
|
||||
days: row.cert_expire
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: $gettext('Remark'),
|
||||
key: 'remark',
|
||||
minWidth: 200,
|
||||
resizable: true,
|
||||
ellipsis: { tooltip: true },
|
||||
render(row: any) {
|
||||
return h(NInput, {
|
||||
size: 'small',
|
||||
value: row.remark,
|
||||
onBlur: () => handleRemark(row),
|
||||
onUpdateValue(v) {
|
||||
row.remark = v
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
title: $gettext('Actions'),
|
||||
key: 'actions',
|
||||
width: 220,
|
||||
hideInExcel: true,
|
||||
render(row: any) {
|
||||
return [
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'primary',
|
||||
style: 'margin-left: 15px;',
|
||||
onClick: () => handleEdit(row)
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Edit'),
|
||||
icon: renderIcon('material-symbols:edit-outline', { size: 14 })
|
||||
}
|
||||
),
|
||||
h(
|
||||
NPopconfirm,
|
||||
{
|
||||
showIcon: false,
|
||||
onPositiveClick: () => handleDelete(row.id)
|
||||
},
|
||||
{
|
||||
default: () => {
|
||||
return h(
|
||||
NFlex,
|
||||
{
|
||||
vertical: true
|
||||
},
|
||||
{
|
||||
default: () => [
|
||||
h(
|
||||
'strong',
|
||||
{},
|
||||
{
|
||||
default: () =>
|
||||
$gettext('Are you sure you want to delete website %{ name }?', {
|
||||
name: row.name
|
||||
})
|
||||
}
|
||||
),
|
||||
h(
|
||||
NCheckbox,
|
||||
{
|
||||
checked: deleteModel.value.path,
|
||||
onUpdateChecked: (v) => (deleteModel.value.path = v)
|
||||
},
|
||||
{ default: () => $gettext('Delete website directory') }
|
||||
),
|
||||
h(
|
||||
NCheckbox,
|
||||
{
|
||||
checked: deleteModel.value.db,
|
||||
onUpdateChecked: (v) => (deleteModel.value.db = v)
|
||||
},
|
||||
{ default: () => $gettext('Delete local database with the same name') }
|
||||
)
|
||||
]
|
||||
}
|
||||
)
|
||||
},
|
||||
trigger: () => {
|
||||
return h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'error',
|
||||
style: 'margin-left: 15px;'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Delete'),
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const createModal = ref(false)
|
||||
const bulkCreateModal = ref(false)
|
||||
const editDefaultPageModal = ref(false)
|
||||
|
||||
const createModel = ref({
|
||||
name: '',
|
||||
listens: [] as Array<string>,
|
||||
domains: [] as Array<string>,
|
||||
path: '',
|
||||
php: 0,
|
||||
db: false,
|
||||
db_type: '0',
|
||||
db_name: '',
|
||||
db_user: '',
|
||||
db_password: '',
|
||||
remark: ''
|
||||
})
|
||||
const deleteModel = ref({
|
||||
path: true,
|
||||
db: false
|
||||
})
|
||||
const editDefaultPageModel = ref({
|
||||
index: '',
|
||||
stop: ''
|
||||
})
|
||||
|
||||
const { data: installedDbAndPhp } = useRequest(dashboard.installedDbAndPhp, {
|
||||
initialData: {
|
||||
php: [
|
||||
{
|
||||
label: $gettext('Not used'),
|
||||
value: 0
|
||||
}
|
||||
],
|
||||
db: [
|
||||
{
|
||||
label: '',
|
||||
value: ''
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
const { loading, data, page, total, pageSize, pageCount, refresh } = usePagination(
|
||||
(page, pageSize) => website.list(page, pageSize),
|
||||
{
|
||||
initialData: { total: 0, list: [] },
|
||||
initialPageSize: 20,
|
||||
total: (res: any) => res.total,
|
||||
data: (res: any) => res.items
|
||||
}
|
||||
)
|
||||
|
||||
// 修改运行状态
|
||||
const handleStatusChange = (row: any) => {
|
||||
if (isNullOrUndef(row.id)) return
|
||||
|
||||
useRequest(website.status(row.id, !row.status)).onSuccess(() => {
|
||||
row.status = !row.status
|
||||
if (row.status) {
|
||||
window.$message.success($gettext('Started successfully'))
|
||||
} else {
|
||||
window.$message.success($gettext('Stopped successfully'))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const getDefaultPage = async () => {
|
||||
editDefaultPageModel.value = await website.defaultConfig()
|
||||
}
|
||||
|
||||
const handleRemark = (row: any) => {
|
||||
useRequest(website.updateRemark(row.id, row.remark)).onSuccess(() => {
|
||||
window.$message.success($gettext('Modified successfully'))
|
||||
})
|
||||
}
|
||||
|
||||
const handleEdit = (row: any) => {
|
||||
router.push({
|
||||
name: 'website-edit',
|
||||
params: {
|
||||
id: row.id
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleDelete = (id: number) => {
|
||||
useRequest(website.delete(id, deleteModel.value.path, deleteModel.value.db)).onSuccess(() => {
|
||||
refresh()
|
||||
deleteModel.value.path = true
|
||||
window.$message.success($gettext('Deleted successfully'))
|
||||
})
|
||||
}
|
||||
|
||||
const handleSaveDefaultPage = () => {
|
||||
useRequest(
|
||||
website.saveDefaultConfig(editDefaultPageModel.value.index, editDefaultPageModel.value.stop)
|
||||
).onSuccess(() => {
|
||||
editDefaultPageModal.value = false
|
||||
window.$message.success($gettext('Modified successfully'))
|
||||
})
|
||||
}
|
||||
|
||||
const handleCreate = async () => {
|
||||
// 去除空的域名和端口
|
||||
createModel.value.domains = createModel.value.domains.filter((item) => item !== '')
|
||||
createModel.value.listens = createModel.value.listens.filter((item) => item !== '')
|
||||
// 端口为空自动添加 80 端口
|
||||
if (createModel.value.listens.length === 0) {
|
||||
createModel.value.listens.push('80')
|
||||
}
|
||||
// 端口中去掉 443 端口,nginx 不允许在未配置证书下监听 443 端口
|
||||
createModel.value.listens = createModel.value.listens.filter((item) => item !== '443')
|
||||
useRequest(website.create(createModel.value)).onSuccess(() => {
|
||||
refresh()
|
||||
window.$message.success(
|
||||
$gettext('Website %{ name } created successfully', { name: createModel.value.name })
|
||||
)
|
||||
createModal.value = false
|
||||
createModel.value = {
|
||||
name: '',
|
||||
domains: [] as Array<string>,
|
||||
listens: [] as Array<string>,
|
||||
php: 0,
|
||||
db: false,
|
||||
db_type: '0',
|
||||
db_name: '',
|
||||
db_user: '',
|
||||
db_password: '',
|
||||
path: '',
|
||||
remark: ''
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const bulkDelete = async () => {
|
||||
if (selectedRowKeys.value.length === 0) {
|
||||
window.$message.info($gettext('Please select the websites to delete'))
|
||||
return
|
||||
}
|
||||
|
||||
const promises = selectedRowKeys.value.map((id: any) => website.delete(id, true, false))
|
||||
await Promise.all(promises)
|
||||
|
||||
selectedRowKeys.value = []
|
||||
refresh()
|
||||
window.$message.success($gettext('Deleted successfully'))
|
||||
}
|
||||
|
||||
const formatDbValue = (value: string) => {
|
||||
value = value.replace(/\./g, '_')
|
||||
value = value.replace(/-/g, '_')
|
||||
if (value.length > 16) {
|
||||
value = value.substring(0, 16)
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
refresh()
|
||||
getDefaultPage()
|
||||
window.$bus.on('website:refresh', refresh)
|
||||
})
|
||||
const currentTab = ref('proxy')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<common-page show-footer>
|
||||
<n-flex vertical>
|
||||
<n-flex>
|
||||
<n-button type="primary" @click="createModal = true">
|
||||
<the-icon :size="18" icon="material-symbols:add" />
|
||||
{{ $gettext('Create Website') }}
|
||||
</n-button>
|
||||
<n-button type="primary" @click="bulkCreateModal = true">
|
||||
<the-icon :size="18" icon="material-symbols:add" />
|
||||
{{ $gettext('Bulk Create Website') }}
|
||||
</n-button>
|
||||
<n-button type="warning" @click="editDefaultPageModal = true">
|
||||
<the-icon :size="18" icon="material-symbols:edit-document-outline" />
|
||||
{{ $gettext('Modify Default Page') }}
|
||||
</n-button>
|
||||
<n-popconfirm @positive-click="bulkDelete">
|
||||
<template #trigger>
|
||||
<n-button type="error">
|
||||
<the-icon :size="18" icon="material-symbols:delete-outline" />
|
||||
{{ $gettext('Batch Delete') }}
|
||||
</n-button>
|
||||
</template>
|
||||
{{
|
||||
$gettext(
|
||||
'This will delete the website directory but not the database with the same name. Are you sure you want to delete the selected websites?'
|
||||
)
|
||||
}}
|
||||
</n-popconfirm>
|
||||
</n-flex>
|
||||
<n-data-table
|
||||
striped
|
||||
remote
|
||||
:loading="loading"
|
||||
:scroll-x="1400"
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
:row-key="(row: any) => row.id"
|
||||
v-model:checked-row-keys="selectedRowKeys"
|
||||
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>
|
||||
<common-page show-header show-footer>
|
||||
<template #tabbar>
|
||||
<n-tabs v-model:value="currentTab" animated>
|
||||
<n-tab name="proxy" :tab="$gettext('Reverse Proxy')" />
|
||||
<n-tab name="php" :tab="$gettext('Classic PHP')" />
|
||||
<n-tab name="static" :tab="$gettext('Pure Static')" />
|
||||
<n-tab name="setting" :tab="$gettext('Settings')" />
|
||||
</n-tabs>
|
||||
</template>
|
||||
<php-view v-if="currentTab === 'php'" />
|
||||
<setting-view v-if="currentTab === 'setting'" />
|
||||
</common-page>
|
||||
<n-modal
|
||||
v-model:show="createModal"
|
||||
:title="$gettext('Create Website')"
|
||||
preset="card"
|
||||
style="width: 60vw"
|
||||
size="huge"
|
||||
:bordered="false"
|
||||
:segmented="false"
|
||||
@close="createModal = false"
|
||||
>
|
||||
<n-form :model="createModel">
|
||||
<n-form-item path="name" :label="$gettext('Website Name')">
|
||||
<n-input
|
||||
v-model:value="createModel.name"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
:placeholder="
|
||||
$gettext(
|
||||
'Recommended to use English for the website name, it cannot be modified after setting'
|
||||
)
|
||||
"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-row :gutter="[0, 24]">
|
||||
<n-col :span="11">
|
||||
<n-form-item :label="$gettext('Domain')">
|
||||
<n-dynamic-input
|
||||
v-model:value="createModel.domains"
|
||||
placeholder="example.com"
|
||||
:min="1"
|
||||
show-sort-button
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
<n-col :span="2"></n-col>
|
||||
<n-col :span="11">
|
||||
<n-form-item :label="$gettext('Port')">
|
||||
<n-dynamic-input
|
||||
v-model:value="createModel.listens"
|
||||
placeholder="80"
|
||||
:min="1"
|
||||
show-sort-button
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
</n-row>
|
||||
<n-row :gutter="[0, 24]">
|
||||
<n-col :span="11">
|
||||
<n-form-item path="php" :label="$gettext('PHP Version')">
|
||||
<n-select
|
||||
v-model:value="createModel.php"
|
||||
:options="installedDbAndPhp.php"
|
||||
:placeholder="$gettext('Select PHP Version')"
|
||||
@keydown.enter.prevent
|
||||
>
|
||||
</n-select>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
<n-col :span="2"></n-col>
|
||||
<n-col :span="11">
|
||||
<n-form-item path="db" :label="$gettext('Database')">
|
||||
<n-select
|
||||
v-model:value="createModel.db_type"
|
||||
:options="installedDbAndPhp.db"
|
||||
:placeholder="$gettext('Select Database')"
|
||||
@keydown.enter.prevent
|
||||
@update:value="
|
||||
() => {
|
||||
createModel.db = createModel.db_type != '0'
|
||||
createModel.db_name = formatDbValue(createModel.name)
|
||||
createModel.db_user = formatDbValue(createModel.name)
|
||||
createModel.db_password = generateRandomString(16)
|
||||
}
|
||||
"
|
||||
>
|
||||
</n-select>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
</n-row>
|
||||
<n-row :gutter="[0, 24]">
|
||||
<n-col :span="7">
|
||||
<n-form-item v-if="createModel.db" path="db_name" :label="$gettext('Database Name')">
|
||||
<n-input
|
||||
v-model:value="createModel.db_name"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
:placeholder="$gettext('Database Name')"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
<n-col :span="1"></n-col>
|
||||
<n-col :span="7">
|
||||
<n-form-item v-if="createModel.db" path="db_user" :label="$gettext('Database User')">
|
||||
<n-input
|
||||
v-model:value="createModel.db_user"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
:placeholder="$gettext('Database User')"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
<n-col :span="1"></n-col>
|
||||
<n-col :span="8">
|
||||
<n-form-item
|
||||
v-if="createModel.db"
|
||||
path="db_password"
|
||||
:label="$gettext('Database Password')"
|
||||
>
|
||||
<n-input
|
||||
v-model:value="createModel.db_password"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
:placeholder="$gettext('Database Password')"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
</n-row>
|
||||
<n-form-item path="path" :label="$gettext('Directory')">
|
||||
<n-input
|
||||
v-model:value="createModel.path"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
:placeholder="
|
||||
$gettext(
|
||||
'Website root directory (if left empty, defaults to website directory/website name)'
|
||||
)
|
||||
"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item path="remark" :label="$gettext('Remark')">
|
||||
<n-input
|
||||
v-model:value="createModel.remark"
|
||||
type="textarea"
|
||||
@keydown.enter.prevent
|
||||
:placeholder="$gettext('Remark')"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<n-button type="info" block @click="handleCreate">
|
||||
{{ $gettext('Create') }}
|
||||
</n-button>
|
||||
</n-modal>
|
||||
<n-modal
|
||||
v-model:show="editDefaultPageModal"
|
||||
preset="card"
|
||||
:title="$gettext('Modify Default Page')"
|
||||
style="width: 80vw"
|
||||
size="huge"
|
||||
:bordered="false"
|
||||
:segmented="false"
|
||||
@close="handleSaveDefaultPage"
|
||||
>
|
||||
<n-tabs type="line" animated>
|
||||
<n-tab-pane :name="$gettext('Default Page')" :tab="$gettext('Default Page')">
|
||||
<Editor
|
||||
v-model:value="editDefaultPageModel.index"
|
||||
language="html"
|
||||
theme="vs-dark"
|
||||
height="60vh"
|
||||
mt-8
|
||||
:options="{
|
||||
automaticLayout: true,
|
||||
formatOnType: true,
|
||||
formatOnPaste: true
|
||||
}"
|
||||
/>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane :name="$gettext('Stop Page')" :tab="$gettext('Stop Page')">
|
||||
<Editor
|
||||
v-model:value="editDefaultPageModel.stop"
|
||||
language="html"
|
||||
theme="vs-dark"
|
||||
height="60vh"
|
||||
mt-8
|
||||
:options="{
|
||||
automaticLayout: true,
|
||||
formatOnType: true,
|
||||
formatOnPaste: true
|
||||
}"
|
||||
/>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</n-modal>
|
||||
<bulk-create v-model:show="bulkCreateModal" />
|
||||
</template>
|
||||
|
||||
538
web/src/views/website/PhpView.vue
Normal file
538
web/src/views/website/PhpView.vue
Normal file
@@ -0,0 +1,538 @@
|
||||
<script lang="ts" setup>
|
||||
import { NButton, NCheckbox, NDataTable, NFlex, NInput, NPopconfirm, NSwitch, NTag } from 'naive-ui'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
|
||||
import dashboard from '@/api/panel/dashboard'
|
||||
import website from '@/api/panel/website'
|
||||
import { useFileStore } from '@/store'
|
||||
import { generateRandomString, isNullOrUndef } from '@/utils'
|
||||
import BulkCreate from '@/views/website/BulkCreate.vue'
|
||||
|
||||
const fileStore = useFileStore()
|
||||
const { $gettext } = useGettext()
|
||||
const router = useRouter()
|
||||
const selectedRowKeys = ref<any>([])
|
||||
|
||||
const columns: any = [
|
||||
{ type: 'selection', fixed: 'left' },
|
||||
{
|
||||
title: $gettext('Website Name'),
|
||||
key: 'name',
|
||||
width: 200,
|
||||
resizable: true,
|
||||
ellipsis: { tooltip: true }
|
||||
},
|
||||
{
|
||||
title: $gettext('Running'),
|
||||
key: 'status',
|
||||
width: 150,
|
||||
render(row: any) {
|
||||
return h(NSwitch, {
|
||||
size: 'small',
|
||||
rubberBand: false,
|
||||
value: row.status,
|
||||
onUpdateValue: () => handleStatusChange(row)
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
title: $gettext('Directory'),
|
||||
key: 'path',
|
||||
minWidth: 200,
|
||||
resizable: true,
|
||||
render(row: any) {
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
class: 'cursor-pointer hover:opacity-60',
|
||||
type: 'info',
|
||||
onClick: () => {
|
||||
fileStore.path = row.path
|
||||
router.push({ name: 'file-index' })
|
||||
}
|
||||
},
|
||||
{ default: () => row.path }
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'HTTPS',
|
||||
key: 'https',
|
||||
width: 150,
|
||||
render(row: any) {
|
||||
return h(NSwitch, {
|
||||
size: 'small',
|
||||
rubberBand: false,
|
||||
value: row.https,
|
||||
onClick: () => handleEdit(row)
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
title: $gettext('Certificate expiration'),
|
||||
key: 'cert_expire',
|
||||
width: 200,
|
||||
render(row: any) {
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
type: row.cert_expire == 0 ? 'default' : row.cert_expire > 0 ? 'success' : 'error',
|
||||
class: 'cursor-pointer hover:opacity-60',
|
||||
onClick: () => handleEdit(row)
|
||||
},
|
||||
{
|
||||
default: () => {
|
||||
if (row.cert_expire == 0) {
|
||||
return $gettext('Not configured')
|
||||
}
|
||||
if (row.cert_expire < 0) {
|
||||
return $gettext('Expired %{ days } days ago', {
|
||||
days: Math.abs(row.cert_expire)
|
||||
})
|
||||
}
|
||||
if (row.cert_expire > 0) {
|
||||
return $gettext('Expires in %{ days } days', {
|
||||
days: row.cert_expire
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: $gettext('Remark'),
|
||||
key: 'remark',
|
||||
minWidth: 200,
|
||||
resizable: true,
|
||||
ellipsis: { tooltip: true },
|
||||
render(row: any) {
|
||||
return h(NInput, {
|
||||
size: 'small',
|
||||
value: row.remark,
|
||||
onBlur: () => handleRemark(row),
|
||||
onUpdateValue(v) {
|
||||
row.remark = v
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
title: $gettext('Actions'),
|
||||
key: 'actions',
|
||||
width: 220,
|
||||
hideInExcel: true,
|
||||
render(row: any) {
|
||||
return [
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'primary',
|
||||
style: 'margin-left: 15px;',
|
||||
onClick: () => handleEdit(row)
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Edit')
|
||||
}
|
||||
),
|
||||
h(
|
||||
NPopconfirm,
|
||||
{
|
||||
showIcon: false,
|
||||
onPositiveClick: () => handleDelete(row.id)
|
||||
},
|
||||
{
|
||||
default: () => {
|
||||
return h(
|
||||
NFlex,
|
||||
{
|
||||
vertical: true
|
||||
},
|
||||
{
|
||||
default: () => [
|
||||
h(
|
||||
'strong',
|
||||
{},
|
||||
{
|
||||
default: () =>
|
||||
$gettext('Are you sure you want to delete website %{ name }?', {
|
||||
name: row.name
|
||||
})
|
||||
}
|
||||
),
|
||||
h(
|
||||
NCheckbox,
|
||||
{
|
||||
checked: deleteModel.value.path,
|
||||
onUpdateChecked: (v) => (deleteModel.value.path = v)
|
||||
},
|
||||
{ default: () => $gettext('Delete website directory') }
|
||||
),
|
||||
h(
|
||||
NCheckbox,
|
||||
{
|
||||
checked: deleteModel.value.db,
|
||||
onUpdateChecked: (v) => (deleteModel.value.db = v)
|
||||
},
|
||||
{ default: () => $gettext('Delete local database with the same name') }
|
||||
)
|
||||
]
|
||||
}
|
||||
)
|
||||
},
|
||||
trigger: () => {
|
||||
return h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'error',
|
||||
style: 'margin-left: 15px;'
|
||||
},
|
||||
{
|
||||
default: () => $gettext('Delete')
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const createModal = ref(false)
|
||||
const bulkCreateModal = ref(false)
|
||||
|
||||
const createModel = ref({
|
||||
name: '',
|
||||
listens: [] as Array<string>,
|
||||
domains: [] as Array<string>,
|
||||
path: '',
|
||||
php: 0,
|
||||
db: false,
|
||||
db_type: '0',
|
||||
db_name: '',
|
||||
db_user: '',
|
||||
db_password: '',
|
||||
remark: ''
|
||||
})
|
||||
const deleteModel = ref({
|
||||
path: true,
|
||||
db: false
|
||||
})
|
||||
|
||||
const { data: installedDbAndPhp } = useRequest(dashboard.installedDbAndPhp, {
|
||||
initialData: {
|
||||
php: [
|
||||
{
|
||||
label: $gettext('Not used'),
|
||||
value: 0
|
||||
}
|
||||
],
|
||||
db: [
|
||||
{
|
||||
label: '',
|
||||
value: ''
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
const { loading, data, page, total, pageSize, pageCount, refresh } = usePagination(
|
||||
(page, pageSize) => website.list(page, pageSize),
|
||||
{
|
||||
initialData: { total: 0, list: [] },
|
||||
initialPageSize: 20,
|
||||
total: (res: any) => res.total,
|
||||
data: (res: any) => res.items
|
||||
}
|
||||
)
|
||||
|
||||
// 修改运行状态
|
||||
const handleStatusChange = (row: any) => {
|
||||
if (isNullOrUndef(row.id)) return
|
||||
|
||||
useRequest(website.status(row.id, !row.status)).onSuccess(() => {
|
||||
row.status = !row.status
|
||||
if (row.status) {
|
||||
window.$message.success($gettext('Started successfully'))
|
||||
} else {
|
||||
window.$message.success($gettext('Stopped successfully'))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleRemark = (row: any) => {
|
||||
useRequest(website.updateRemark(row.id, row.remark)).onSuccess(() => {
|
||||
window.$message.success($gettext('Modified successfully'))
|
||||
})
|
||||
}
|
||||
|
||||
const handleEdit = (row: any) => {
|
||||
router.push({
|
||||
name: 'website-edit',
|
||||
params: {
|
||||
id: row.id
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleDelete = (id: number) => {
|
||||
useRequest(website.delete(id, deleteModel.value.path, deleteModel.value.db)).onSuccess(() => {
|
||||
refresh()
|
||||
deleteModel.value.path = true
|
||||
window.$message.success($gettext('Deleted successfully'))
|
||||
})
|
||||
}
|
||||
|
||||
const handleCreate = async () => {
|
||||
// 去除空的域名和端口
|
||||
createModel.value.domains = createModel.value.domains.filter((item) => item !== '')
|
||||
createModel.value.listens = createModel.value.listens.filter((item) => item !== '')
|
||||
// 端口为空自动添加 80 端口
|
||||
if (createModel.value.listens.length === 0) {
|
||||
createModel.value.listens.push('80')
|
||||
}
|
||||
// 端口中去掉 443 端口,nginx 不允许在未配置证书下监听 443 端口
|
||||
createModel.value.listens = createModel.value.listens.filter((item) => item !== '443')
|
||||
useRequest(website.create(createModel.value)).onSuccess(() => {
|
||||
refresh()
|
||||
window.$message.success(
|
||||
$gettext('Website %{ name } created successfully', { name: createModel.value.name })
|
||||
)
|
||||
createModal.value = false
|
||||
createModel.value = {
|
||||
name: '',
|
||||
domains: [] as Array<string>,
|
||||
listens: [] as Array<string>,
|
||||
php: 0,
|
||||
db: false,
|
||||
db_type: '0',
|
||||
db_name: '',
|
||||
db_user: '',
|
||||
db_password: '',
|
||||
path: '',
|
||||
remark: ''
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const bulkDelete = async () => {
|
||||
if (selectedRowKeys.value.length === 0) {
|
||||
window.$message.info($gettext('Please select the websites to delete'))
|
||||
return
|
||||
}
|
||||
|
||||
const promises = selectedRowKeys.value.map((id: any) => website.delete(id, true, false))
|
||||
await Promise.all(promises)
|
||||
|
||||
selectedRowKeys.value = []
|
||||
refresh()
|
||||
window.$message.success($gettext('Deleted successfully'))
|
||||
}
|
||||
|
||||
const formatDbValue = (value: string) => {
|
||||
value = value.replace(/\./g, '_')
|
||||
value = value.replace(/-/g, '_')
|
||||
if (value.length > 16) {
|
||||
value = value.substring(0, 16)
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
refresh()
|
||||
window.$bus.on('website:refresh', refresh)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-flex vertical>
|
||||
<n-flex>
|
||||
<n-button type="primary" @click="createModal = true">
|
||||
{{ $gettext('Create Website') }}
|
||||
</n-button>
|
||||
<n-button type="primary" @click="bulkCreateModal = true">
|
||||
{{ $gettext('Bulk Create Website') }}
|
||||
</n-button>
|
||||
<n-popconfirm @positive-click="bulkDelete">
|
||||
<template #trigger>
|
||||
<n-button type="error">
|
||||
{{ $gettext('Batch Delete') }}
|
||||
</n-button>
|
||||
</template>
|
||||
{{
|
||||
$gettext(
|
||||
'This will delete the website directory but not the database with the same name. Are you sure you want to delete the selected websites?'
|
||||
)
|
||||
}}
|
||||
</n-popconfirm>
|
||||
</n-flex>
|
||||
<n-data-table
|
||||
striped
|
||||
remote
|
||||
:loading="loading"
|
||||
:scroll-x="1400"
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
:row-key="(row: any) => row.id"
|
||||
v-model:checked-row-keys="selectedRowKeys"
|
||||
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>
|
||||
<n-modal
|
||||
v-model:show="createModal"
|
||||
:title="$gettext('Create Website')"
|
||||
preset="card"
|
||||
style="width: 60vw"
|
||||
size="huge"
|
||||
:bordered="false"
|
||||
:segmented="false"
|
||||
@close="createModal = false"
|
||||
>
|
||||
<n-form :model="createModel">
|
||||
<n-form-item path="name" :label="$gettext('Website Name')">
|
||||
<n-input
|
||||
v-model:value="createModel.name"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
:placeholder="
|
||||
$gettext(
|
||||
'Recommended to use English for the website name, it cannot be modified after setting'
|
||||
)
|
||||
"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-row :gutter="[0, 24]">
|
||||
<n-col :span="11">
|
||||
<n-form-item :label="$gettext('Domain')">
|
||||
<n-dynamic-input
|
||||
v-model:value="createModel.domains"
|
||||
placeholder="example.com"
|
||||
:min="1"
|
||||
show-sort-button
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
<n-col :span="2"></n-col>
|
||||
<n-col :span="11">
|
||||
<n-form-item :label="$gettext('Port')">
|
||||
<n-dynamic-input
|
||||
v-model:value="createModel.listens"
|
||||
placeholder="80"
|
||||
:min="1"
|
||||
show-sort-button
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
</n-row>
|
||||
<n-row :gutter="[0, 24]">
|
||||
<n-col :span="11">
|
||||
<n-form-item path="php" :label="$gettext('PHP Version')">
|
||||
<n-select
|
||||
v-model:value="createModel.php"
|
||||
:options="installedDbAndPhp.php"
|
||||
:placeholder="$gettext('Select PHP Version')"
|
||||
@keydown.enter.prevent
|
||||
>
|
||||
</n-select>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
<n-col :span="2"></n-col>
|
||||
<n-col :span="11">
|
||||
<n-form-item path="db" :label="$gettext('Database')">
|
||||
<n-select
|
||||
v-model:value="createModel.db_type"
|
||||
:options="installedDbAndPhp.db"
|
||||
:placeholder="$gettext('Select Database')"
|
||||
@keydown.enter.prevent
|
||||
@update:value="
|
||||
() => {
|
||||
createModel.db = createModel.db_type != '0'
|
||||
createModel.db_name = formatDbValue(createModel.name)
|
||||
createModel.db_user = formatDbValue(createModel.name)
|
||||
createModel.db_password = generateRandomString(16)
|
||||
}
|
||||
"
|
||||
>
|
||||
</n-select>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
</n-row>
|
||||
<n-row :gutter="[0, 24]">
|
||||
<n-col :span="7">
|
||||
<n-form-item v-if="createModel.db" path="db_name" :label="$gettext('Database Name')">
|
||||
<n-input
|
||||
v-model:value="createModel.db_name"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
:placeholder="$gettext('Database Name')"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
<n-col :span="1"></n-col>
|
||||
<n-col :span="7">
|
||||
<n-form-item v-if="createModel.db" path="db_user" :label="$gettext('Database User')">
|
||||
<n-input
|
||||
v-model:value="createModel.db_user"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
:placeholder="$gettext('Database User')"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
<n-col :span="1"></n-col>
|
||||
<n-col :span="8">
|
||||
<n-form-item
|
||||
v-if="createModel.db"
|
||||
path="db_password"
|
||||
:label="$gettext('Database Password')"
|
||||
>
|
||||
<n-input
|
||||
v-model:value="createModel.db_password"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
:placeholder="$gettext('Database Password')"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-col>
|
||||
</n-row>
|
||||
<n-form-item path="path" :label="$gettext('Directory')">
|
||||
<n-input
|
||||
v-model:value="createModel.path"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
:placeholder="
|
||||
$gettext(
|
||||
'Website root directory (if left empty, defaults to website directory/website name)'
|
||||
)
|
||||
"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item path="remark" :label="$gettext('Remark')">
|
||||
<n-input
|
||||
v-model:value="createModel.remark"
|
||||
type="textarea"
|
||||
@keydown.enter.prevent
|
||||
:placeholder="$gettext('Remark')"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<n-button type="info" block @click="handleCreate">
|
||||
{{ $gettext('Create') }}
|
||||
</n-button>
|
||||
</n-modal>
|
||||
<bulk-create v-model:show="bulkCreateModal" />
|
||||
</template>
|
||||
117
web/src/views/website/SettingView.vue
Normal file
117
web/src/views/website/SettingView.vue
Normal file
@@ -0,0 +1,117 @@
|
||||
<script setup lang="ts">
|
||||
import website from '@/api/panel/website'
|
||||
import Editor from '@guolao/vue-monaco-editor'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
|
||||
const currentTab = ref('default-page')
|
||||
|
||||
const defaultPageModel = ref({
|
||||
index: '',
|
||||
not_found: '',
|
||||
stop: ''
|
||||
})
|
||||
|
||||
const defaultSettingModel = ref({
|
||||
tls_version: ['TLSv1.2', 'TLSv1.3'],
|
||||
cipher_suites: ''
|
||||
})
|
||||
|
||||
const getDefaultPage = async () => {
|
||||
defaultPageModel.value = await website.defaultConfig()
|
||||
}
|
||||
|
||||
const handleSaveDefaultPage = () => {
|
||||
useRequest(
|
||||
website.saveDefaultConfig(defaultPageModel.value.index, defaultPageModel.value.stop)
|
||||
).onSuccess(() => {
|
||||
window.$message.success($gettext('Modified successfully'))
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getDefaultPage()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-tabs v-model:value="currentTab" type="line" placement="left" animated>
|
||||
<n-tab-pane name="default-page" :tab="$gettext('Default Page')">
|
||||
<Editor
|
||||
v-model:value="defaultPageModel.index"
|
||||
language="html"
|
||||
theme="vs-dark"
|
||||
height="60vh"
|
||||
mt-8
|
||||
:options="{
|
||||
automaticLayout: true,
|
||||
formatOnType: true,
|
||||
formatOnPaste: true
|
||||
}"
|
||||
/>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="404-page" :tab="$gettext('404 Page')">
|
||||
<Editor
|
||||
v-model:value="defaultPageModel.not_found"
|
||||
language="html"
|
||||
theme="vs-dark"
|
||||
height="60vh"
|
||||
mt-8
|
||||
:options="{
|
||||
automaticLayout: true,
|
||||
formatOnType: true,
|
||||
formatOnPaste: true
|
||||
}"
|
||||
/>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="stop-page" :tab="$gettext('Stop Page')">
|
||||
<Editor
|
||||
v-model:value="defaultPageModel.stop"
|
||||
language="html"
|
||||
theme="vs-dark"
|
||||
height="60vh"
|
||||
mt-8
|
||||
:options="{
|
||||
automaticLayout: true,
|
||||
formatOnType: true,
|
||||
formatOnPaste: true
|
||||
}"
|
||||
/>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="default-site" :tab="$gettext('Default Site')">
|
||||
<n-alert type="info">待开发</n-alert>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="default-setting" :tab="$gettext('Default Settings')">
|
||||
<n-form>
|
||||
<n-form-item :label="$gettext('Default TLS Version')">
|
||||
<n-select
|
||||
v-model:value="defaultSettingModel.tls_version"
|
||||
:options="[
|
||||
{ label: 'TLS 1.0', value: 'TLSv1.0' },
|
||||
{ label: 'TLS 1.1', value: 'TLSv1.1' },
|
||||
{ label: 'TLS 1.2', value: 'TLSv1.2' },
|
||||
{ label: 'TLS 1.3', value: 'TLSv1.3' }
|
||||
]"
|
||||
multiple
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item :label="$gettext('Default Cipher Suites')">
|
||||
<n-input
|
||||
type="textarea"
|
||||
v-model:value="defaultSettingModel.cipher_suites"
|
||||
:placeholder="
|
||||
$gettext('Enter the default cipher suite, leave blank to reset to default')
|
||||
"
|
||||
rows="4"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-button type="primary">
|
||||
{{ $gettext('Save Changes') }}
|
||||
</n-button>
|
||||
</n-form>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
Reference in New Issue
Block a user