dashboard增加Jsite服务器菜单并实现列表页功能

This commit is contained in:
jingrow 2025-07-29 00:20:55 +08:00
parent 2087285c00
commit ad2929e2fa
6 changed files with 337 additions and 2 deletions

View File

@ -109,6 +109,16 @@ export default {
condition: onboardingComplete && !isSaasUser && this.$team.pg.is_pro,
disabled: enforce2FA,
},
{
name: 'Jsite服务器',
icon: () => h(Server),
route: '/jsite-servers',
isActive:
['Jsite Servers', 'New Jsite Server'].includes(routeName) ||
routeName.startsWith('Jsite Server'),
condition: onboardingComplete && !isSaasUser && this.$team.pg.is_pro,
disabled: enforce2FA,
},
{
name: '应用市场',
icon: () => h(App),

View File

@ -3,6 +3,7 @@ import group from './group';
import bench from './bench';
import marketplace from './marketplace';
import server from './server';
import jsite_server from './jsite_server';
import notification from './notification';
let objects = {
@ -11,6 +12,7 @@ let objects = {
Bench: bench,
Marketplace: marketplace,
Server: server,
'Jsite Server': jsite_server,
Notification: notification
};

View File

@ -0,0 +1,275 @@
import { defineAsyncComponent, h } from 'vue';
import LucideServer from '~icons/lucide/server';
import { getTeam } from '../data/team';
import router from '../router';
import { icon } from '../utils/components';
import { duration, planTitle, userCurrency } from '../utils/format';
import { trialDays } from '../utils/site';
import { getJobsTab } from './common/jobs';
import { tagTab } from './common/tags';
export default {
pagetype: 'Jsite Server',
whitelistedMethods: {
reboot: 'reboot',
rename: 'rename',
dropServer: 'drop_server',
addTag: 'add_resource_tag',
removeTag: 'remove_resource_tag'
},
list: {
route: '/jsite-servers',
title: 'Jsite服务器',
fields: [
'name',
'title',
'status',
'region',
'cpu',
'memory',
'disk_size',
'public_ip',
'end_date',
'bandwidth',
'team',
'instance_id',
'order_id',
'planid',
'image_id',
'system'
],
filterControls() {
return [
{
type: 'select',
label: '状态',
fieldname: 'status',
options: [
{ label: '', value: '' },
{ label: '待定', value: 'Pending' },
{ label: '启动中', value: 'Starting' },
{ label: '运行中', value: 'Running' },
{ label: '停止中', value: 'Stopping' },
{ label: '已停止', value: 'Stopped' },
{ label: '重置中', value: 'Resetting' },
{ label: '升级中', value: 'Upgrading' },
{ label: '已禁用', value: 'Disabled' }
]
},
{
type: 'select',
label: '区域',
fieldname: 'region',
options: [
{ label: '', value: '' },
{ label: '中国大陆', value: '中国大陆' },
{ label: '中国香港', value: '中国香港' },
{ label: '美国-洛杉矶', value: '美国-洛杉矶' },
{ label: '新加坡', value: '新加坡' },
{ label: '英国-伦敦', value: '英国-伦敦' },
{ label: '德国-法兰克福', value: '德国-法兰克福' },
{ label: '阿联酋-迪拜', value: '阿联酋-迪拜' }
]
}
];
},
orderBy: 'creation desc',
searchField: 'title',
columns: [
{
label: '服务器',
fieldname: 'name',
width: 1.5,
class: 'font-medium',
format(value, row) {
return row.title || value;
}
},
{
label: '状态',
fieldname: 'status',
type: 'Badge',
width: 0.8
},
{
label: '配置',
fieldname: 'cpu',
format(value, row) {
const cpu = row.cpu || '未知';
const memory = row.memory || '未知';
const disk = row.disk_size || '未知';
return `${cpu}核/${memory}GB/${disk}GB`;
}
},
{
label: '公网IP',
fieldname: 'public_ip',
format(value) {
return value || '-';
}
},
{
label: '区域',
fieldname: 'region',
format(value) {
return value || '-';
}
},
{
label: '到期时间',
fieldname: 'end_date',
format(value) {
if (!value) return '-';
return new Date(value).toLocaleDateString('zh-CN');
}
}
],
primaryAction({ listResource: jsiteServers }) {
return {
label: '新建Jsite服务器',
icon: 'plus',
onClick() {
router.push('/jsite-servers/new');
}
};
},
statusBadge({ documentResource: jsiteServer }) {
const status = jsiteServer.pg?.status;
const statusConfig = {
Pending: { label: '待定', color: 'gray' },
Starting: { label: '启动中', color: 'yellow' },
Running: { label: '运行中', color: 'green' },
Stopping: { label: '停止中', color: 'orange' },
Stopped: { label: '已停止', color: 'red' },
Resetting: { label: '重置中', color: 'blue' },
Upgrading: { label: '升级中', color: 'purple' },
Disabled: { label: '已禁用', color: 'gray' }
};
return statusConfig[status] || { label: status, color: 'gray' };
},
breadcrumbs({ documentResource: jsiteServer }) {
return [
{
label: 'Jsite服务器',
route: '/jsite-servers'
},
{
label: jsiteServer.pg?.title || jsiteServer.pg?.name,
route: `/jsite-servers/${jsiteServer.pg?.name}`
}
];
},
actions({ documentResource: jsiteServer }) {
if (!jsiteServer) return [];
const actions = [
{
label: '重启',
icon: 'refresh-cw',
onClick() {
jsiteServer.reboot.submit();
},
condition: () => jsiteServer.pg?.status === 'Running'
},
{
label: '重命名',
icon: 'edit-3',
onClick() {
jsiteServer.rename.submit();
}
},
{
label: '删除',
icon: 'trash-2',
onClick() {
jsiteServer.dropServer.submit();
},
condition: () => jsiteServer.pg?.status !== 'Running'
}
];
return actions.filter(action => !action.condition || action.condition());
}
},
detail: {
route: '/jsite-servers/:name',
title: 'Jsite服务器详情',
tabs: [
{
label: '概览',
route: '',
type: 'fields'
}
],
fields: [
{
label: '基本信息',
fields: [
'title',
'status',
'region',
'instance_id',
'order_id'
]
},
{
label: '服务器配置',
fields: [
'cpu',
'memory',
'disk_size',
'bandwidth',
'public_ip',
'image_id',
'planid',
'system'
]
},
{
label: 'SSH连接',
fields: [
'ssh_user',
'ssh_port',
'password',
'key_pair_name',
'private_key'
]
},
{
label: '其他信息',
fields: [
'end_date',
'period'
]
}
],
actions({ documentResource: jsiteServer }) {
if (!jsiteServer) return [];
return [
{
label: '重启',
icon: 'refresh-cw',
onClick() {
jsiteServer.reboot.submit();
},
condition: () => jsiteServer.pg?.status === 'Running'
},
{
label: '重命名',
icon: 'edit-3',
onClick() {
jsiteServer.rename.submit();
}
},
{
label: '删除',
icon: 'trash-2',
onClick() {
jsiteServer.dropServer.submit();
},
condition: () => jsiteServer.pg?.status !== 'Running'
}
].filter(action => !action.condition || action.condition());
}
}
};

View File

@ -110,6 +110,27 @@ let router = createRouter({
path: '/servers/new',
component: () => import('./pages/NewJsiteServer.vue'),
},
{
name: 'Jsite Servers',
path: '/jsite-servers',
component: () => import('./pages/ListPage.vue'),
props: route => {
return { objectType: 'Jsite Server', ...route.params };
}
},
{
name: 'New Jsite Server',
path: '/jsite-servers/new',
component: () => import('./pages/NewJsiteServer.vue'),
},
{
name: 'Jsite Server Detail',
path: '/jsite-servers/:name',
component: () => import('./pages/DetailPage.vue'),
props: route => {
return { objectType: 'Jsite Server', ...route.params };
}
},
{
name: 'Billing',
path: '/billing',

View File

@ -77,6 +77,7 @@ ALLOWED_PAGETYPES = [
"Site Database User",
"Jcloud Settings",
"Mpesa Payment Record",
"Jsite Server",
]
ALLOWED_PAGETYPES_FOR_SUPPORT = [

View File

@ -1,7 +1,7 @@
# Copyright (c) 2025, Jingrow and contributors
# For license information, please see license.txt
# import jingrow
import jingrow
from jingrow.model.document import Document
@ -36,4 +36,30 @@ class JsiteServer(Document):
team: DF.Link | None
title: DF.Data | None
# end: auto-generated types
pass
dashboard_fields = (
"title",
"status",
"region",
"cpu",
"memory",
"disk_size",
"public_ip",
"end_date",
"bandwidth",
"team",
"instance_id",
"order_id",
"planid",
"image_id",
"system"
)
@staticmethod
def get_list_query(query):
JsiteServer = jingrow.qb.PageType("Jsite Server")
query = query.where(JsiteServer.team == jingrow.local.team().name)
return query.run(as_dict=True)
def get_pg(self, pg):
return pg