jcloud/dashboard/src2/components/SiteActionCell.vue
2025-04-12 17:39:38 +08:00

312 lines
7.8 KiB
Vue

<template>
<div class="flex items-center justify-between gap-1">
<div>
<h3 class="text-base font-medium">{{ props.actionLabel }}</h3>
<p class="mt-1 text-p-base text-gray-600">{{ props.description }}</p>
</div>
<Button
v-if="site?.pg"
class="whitespace-nowrap"
@click="getSiteActionHandler(props.actionLabel)"
>
<p
:class="
group === 'Dangerous Actions' ? 'text-red-600' : 'text-gray-800'
"
>
{{ props.buttonLabel }}
</p>
</Button>
</div>
</template>
<script setup>
import { getCachedDocumentResource } from 'jingrow-ui';
import { defineAsyncComponent, h } from 'vue';
import { toast } from 'vue-sonner';
import { confirmDialog, renderDialog } from '../utils/components';
import { getToastErrorMessage } from '../utils/toast';
import router from '../router';
import { isLastSite } from '../data/team';
const props = defineProps({
siteName: { type: String, required: true },
actionLabel: { type: String, required: true },
method: { type: String, required: true },
description: { type: String, required: true },
buttonLabel: { type: String, required: true },
group: { type: String, required: false }
});
const site = getCachedDocumentResource('Site', props.siteName);
function getSiteActionHandler(action) {
const actionDialogs = {
'使用文件恢复': defineAsyncComponent(() =>
import('./SiteDatabaseRestoreDialog.vue')
),
'从现有站点恢复': defineAsyncComponent(() =>
import('./site/SiteDatabaseRestoreFromURLDialog.vue')
),
'管理数据库用户': defineAsyncComponent(() =>
import('./SiteDatabaseAccessDialog.vue')
),
'版本升级': defineAsyncComponent(() =>
import('./site/SiteVersionUpgradeDialog.vue')
),
'更改工作台组': defineAsyncComponent(() =>
import('./site/SiteChangeGroupDialog.vue')
),
'更改区域': defineAsyncComponent(() =>
import('./site/SiteChangeRegionDialog.vue')
),
'更改服务器': defineAsyncComponent(() =>
import('./site/SiteChangeServerDialog.vue')
)
};
if (actionDialogs[action]) {
const dialog = h(actionDialogs[action], { site: site.pg.name });
renderDialog(dialog);
return;
}
const actionHandlers = {
'激活站点': onActivateSite,
'停用站点': onDeactivateSite,
'删除站点': onDropSite,
'更新数据库': onMigrateSite,
'计划备份': onScheduleBackup,
'转移站点': onTransferSite,
'重置站点': onSiteReset,
'清除缓存': onClearCache
};
if (actionHandlers[action]) {
actionHandlers[action].call(this);
}
}
function onDeactivateSite() {
return confirmDialog({
title: '停用站点',
message: `
您确定要停用此站点吗?<br><br>
<div class="text-bg-base bg-gray-100 p-2 rounded-md">
站点将进入<strong>停用</strong>状态。它将无法访问,后台作业也不会运行。
<br><br>
<div class="text-red-600">您仍将为此付费。</div>
</div>
`,
primaryAction: {
label: '停用',
variant: 'solid',
theme: 'red',
onClick({ hide }) {
return site.deactivate.submit().then(hide);
}
}
});
}
function onActivateSite() {
return confirmDialog({
title: '激活站点',
message: `
您确定要激活此站点吗?
<br><br>
<strong>注意:仅在站点损坏且无法访问时作为最后手段使用</strong>
`,
primaryAction: {
label: '激活',
variant: 'solid',
onClick({ hide }) {
return site.activate.submit().then(hide);
}
}
});
}
function onDropSite() {
return confirmDialog({
title: '删除站点',
message: `
您确定要删除您的站点吗?站点将被归档,
其所有文件和异地备份将被删除。此操作无法撤销。
`,
fields: [
{
label: '请输入站点名称以确认。',
fieldname: 'confirmSiteName'
},
{
label: '强制删除站点',
fieldname: 'force',
type: 'checkbox'
}
],
primaryAction: {
label: '删除站点',
variant: 'solid',
theme: 'red',
onClick: async ({ hide, values }) => {
if (
![site.pg.name, site.pg.host_name].includes(values.confirmSiteName)
) {
throw new Error('站点名称不匹配。');
}
const val = await isLastSite(site.pg.team);
const FeedbackDialog = defineAsyncComponent(() =>
import('./ChurnFeedbackDialog.vue')
);
return site.archive.submit({ force: values.force }).then(() => {
hide();
if (val) {
renderDialog(
h(FeedbackDialog, {
team: site.pg.team,
onUpdated() {
router.replace({ name: 'Site List' });
toast.success('站点删除成功');
}
})
);
} else {
router.replace({ name: 'Site List' });
}
});
}
}
});
}
function onMigrateSite() {
return confirmDialog({
title: '更新数据库',
message: `将在您的站点上执行更新数据库命令。您确定要运行此命令吗?我们建议您在继续之前进行数据库备份。
`,
fields: [
{
label: '如果更新期间补丁失败则跳过(不推荐)',
fieldname: 'skipFailingPatches',
type: 'checkbox'
},
{
label: '请输入站点名称以确认。',
fieldname: 'confirmSiteName'
}
],
primaryAction: {
label: '更新',
variant: 'solid',
theme: 'red',
onClick: ({ hide, values }) => {
if (values.confirmSiteName !== site.pg.name) {
throw new Error('站点名称不匹配');
}
return site.migrate
.submit({ skip_failing_patches: values.skipFailingPatches })
.then(hide);
}
}
});
}
function onSiteReset() {
return confirmDialog({
title: '重置站点',
message: `
您的站点中的所有数据都将丢失。您确定要重置数据库吗?
`,
fields: [
{
label: '请输入站点名称以确认。',
fieldname: 'confirmSiteName'
}
],
primaryAction: {
label: '重置',
variant: 'solid',
theme: 'red',
onClick: ({ hide, values }) => {
if (values.confirmSiteName !== site.pg.name) {
throw new Error('站点名称不匹配。');
}
return site.reinstall.submit().then(hide);
}
}
});
}
function onScheduleBackup() {
return confirmDialog({
title: '计划备份',
message:
'您确定要计划备份吗?这将创建一个现场备份。',
onSuccess({ hide }) {
toast.promise(
site.backup.submit({
with_files: true
}),
{
loading: '正在计划备份...',
success: () => {
hide();
router.push({
name: 'Site Jobs',
params: { name: site.name }
});
return '备份计划成功';
},
error: e => getToastErrorMessage(e)
}
);
}
});
}
function onTransferSite() {
return confirmDialog({
title: '转移站点所有权',
fields: [
{
label: '输入要转移站点所有权的团队的电子邮件地址',
fieldname: 'email'
},
{
label: '转移原因',
fieldname: 'reason',
type: 'textarea'
}
],
primaryAction: {
label: '转移',
variant: 'solid',
onClick: ({ hide, values }) => {
return site.sendTransferRequest
.submit({ team_mail_id: values.email, reason: values.reason || '' })
.then(() => {
hide();
toast.success(
`转移请求已成功发送至 ${values.email}`
);
});
}
}
});
}
function onClearCache() {
return confirmDialog({
title: '清除缓存',
message: `将在您的站点上执行清除缓存的命令。您确定要执行吗?`,
primaryAction: {
label: '清除缓存',
variant: 'solid',
onClick: ({ hide }) => {
return site.clearSiteCache.submit().then(hide);
}
}
});
}
</script>