Merge pull request #1014 from MCSManager/chino

Feat: Consumer Page
This commit is contained in:
unitwk 2023-09-17 00:28:04 +08:00 committed by GitHub
commit 0148c58e44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 296 additions and 39 deletions

View File

@ -29,6 +29,8 @@ import LinkCard from "@/widgets/others/LinkCard.vue";
import QuickStartFlow from "@/widgets/setupApp/QuickStartFlow.vue";
import IframeCard from "@/widgets/others/IframeCard.vue";
import ClockCard from "@/widgets/others/ClockCard.vue";
import UserStatusBlock from "@/widgets/UserStatusBlock.vue";
import UserInstanceList from "@/widgets/UserInstanceList.vue";
import { NEW_CARD_TYPE } from "../types/index";
@ -60,7 +62,9 @@ export const LAYOUT_CARD_TYPES: { [key: string]: any } = {
IframeCard,
TextCard,
LinkCard,
ClockCard
ClockCard,
UserStatusBlock,
UserInstanceList
};
export interface NewCardItem extends LayoutCard {

View File

@ -307,6 +307,66 @@ export const ORIGIN_LAYOUT_CONFIG: PageLayoutConfig[] = [
}
]
},
{
page: "/consumer",
items: [
{
id: getRandomId(),
type: "UserStatusBlock",
title: t("实例总计"),
meta: {
type: "instance_all"
},
width: 3,
height: LayoutCardHeight.SMALL,
disableDelete: true
},
{
id: getRandomId(),
type: "UserStatusBlock",
title: t("正在运行"),
meta: {
type: "instance_running"
},
width: 3,
height: LayoutCardHeight.SMALL,
disableDelete: true
},
{
id: getRandomId(),
type: "UserStatusBlock",
title: t("未运行"),
meta: {
type: "instance_stop"
},
width: 3,
height: LayoutCardHeight.SMALL,
disableDelete: true
},
{
id: getRandomId(),
type: "UserStatusBlock",
title: t("维护中"),
meta: {
type: "instance_error"
},
width: 3,
height: LayoutCardHeight.SMALL,
disableDelete: true
},
{
id: getRandomId(),
type: "UserInstanceList",
title: t("实例列表"),
meta: {
type: "instance_error"
},
width: 12,
height: LayoutCardHeight.AUTO,
disableDelete: true
}
]
},
{
page: "/404",

View File

@ -1,8 +1,4 @@
import {
createRouter,
createWebHashHistory,
type RouteRecordRaw,
} from "vue-router";
import { createRouter, createWebHashHistory, type RouteRecordRaw } from "vue-router";
import LayoutContainer from "@/views/LayoutContainer.vue";
import { $t as t } from "@/lang/i18n";
import LoginVue from "@/views/Login.vue";
@ -31,15 +27,15 @@ let originRouterConfig = [
name: t("TXT_CODE_16d71239"),
component: LayoutContainer,
meta: {
mainMenu: true,
},
mainMenu: true
}
},
{
path: "/instances",
name: t("TXT_CODE_e21473bc"),
component: LayoutContainer,
meta: {
mainMenu: true,
mainMenu: true
},
children: [
{
@ -52,82 +48,90 @@ let originRouterConfig = [
path: "/instances/terminal/files",
name: t("TXT_CODE_ae533703"),
component: LayoutContainer,
meta: {},
meta: {}
},
{
path: "/instances/terminal/serverConfig",
name: t("TXT_CODE_d07742fe"),
component: LayoutContainer,
meta: {},
},
],
},
],
meta: {}
}
]
}
]
},
{
path: "/users",
name: t("TXT_CODE_1deaa2dd"),
component: LayoutContainer,
meta: {
mainMenu: true,
mainMenu: true
},
children: [
{
path: "/users/config",
name: t("TXT_CODE_236f70aa"),
component: LayoutContainer,
meta: {},
},
],
meta: {}
}
]
},
{
path: "/node",
name: t("TXT_CODE_e076d90b"),
component: LayoutContainer,
meta: {
mainMenu: true,
},
mainMenu: true
}
},
{
path: "/settings",
name: t("TXT_CODE_b5c7b82d"),
component: LayoutContainer,
meta: {
mainMenu: true,
},
mainMenu: true
}
},
{
path: "/user",
name: t("TXT_CODE_8c3164c9"),
component: LayoutContainer,
meta: {
mainMenu: false,
},
mainMenu: false
}
},
{
path: "/404",
name: "404",
component: LayoutContainer,
meta: {
mainMenu: false,
},
mainMenu: false
}
},
{
path: "/login",
name: "login",
component: LoginVue,
meta: {
mainMenu: false,
},
mainMenu: false
}
},
{
path: "/quickstart",
name: t("TXT_CODE_2799a1dd"),
component: LayoutContainer,
meta: {
mainMenu: false,
},
mainMenu: false
}
},
{
path: "/consumer",
name: t("用户主页"),
component: LayoutContainer,
meta: {
mainMenu: false
}
}
];
function routersConfigOptimize(
@ -147,7 +151,7 @@ function routersConfigOptimize(
const router = createRouter({
history: createWebHashHistory(),
routes: routersConfigOptimize(originRouterConfig) as RouteRecordRaw[],
routes: routersConfigOptimize(originRouterConfig) as RouteRecordRaw[]
});
export { router, originRouterConfig };

View File

@ -0,0 +1,105 @@
<script setup lang="ts">
import { t } from "@/lang/i18n";
import { onMounted } from "vue";
import type { LayoutCard } from "@/types";
import { userInfoApi } from "@/services/apis/index";
import { useRouter } from "vue-router";
defineProps<{
card: LayoutCard;
}>();
const router = useRouter();
const { execute, state } = userInfoApi();
const status = {
"-1": t("状态未知"),
"0": t("已停止"),
"1": t("正在停止"),
"2": t("正在启动"),
"3": t("正在运行")
};
const columns = [
{
title: t("实例名称"),
dataIndex: "nickname",
key: "nickname"
},
{
title: t("运行状态"),
dataIndex: "status",
key: "status",
customRender: (e: { text: "-1" | "1" | "2" | "3" }) => {
return status[e.text] || e.text;
}
},
{
title: t("字节流编码"),
dataIndex: "ie",
customRender: (e: { text: string; record: { oe: string; ie: string } }) => {
return `${e.text}/${e.record?.oe}`;
}
},
{
title: t("最后启动"),
dataIndex: "lastDatetime",
key: "lastDatetime"
},
{
title: t("到期时间"),
dataIndex: "endTime",
key: "endTime",
customRender: (e: { text: string }) => {
return e.text || t("无期限");
}
},
{
title: t("操作"),
key: "operate"
}
];
const getInstanceList = async () => {
await execute({
params: {
advanced: true
}
});
};
const operate = (serviceUuid: string, instanceUuid: string) => {
router.push({
path: "/instances/terminal",
query: {
serviceUuid,
instanceUuid
}
});
};
onMounted(() => {
getInstanceList();
});
</script>
<template>
<CardPanel>
<template #title>{{ card.title }}</template>
<template #body>
<a-table
:data-source="state?.instances"
:columns="columns"
:pagination="false"
:scroll="{ x: 'max-content' }"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'operate'">
<a-button @click="operate(record.serviceUuid, record.uuid)">{{ t("管理") }}</a-button>
</template>
</template>
</a-table>
</template>
</CardPanel>
</template>

View File

@ -217,7 +217,10 @@ onMounted(async () => {
{{ t("TXT_CODE_1987587b") }}
</a-typography-text>
</a-typography-paragraph>
<a-input v-model:value="newUserDialog.data.username" :placeholder="t('TXT_CODE_4ea93630')" />
<a-input
v-model:value="newUserDialog.data.username"
:placeholder="t('TXT_CODE_4ea93630')"
/>
</div>
<div class="mb-20">
@ -227,7 +230,10 @@ onMounted(async () => {
{{ t("TXT_CODE_1f2062c7") }}
</a-typography-text>
</a-typography-paragraph>
<a-input v-model:value="newUserDialog.data.password" :placeholder="t('TXT_CODE_4ea93630')" />
<a-input
v-model:value="newUserDialog.data.password"
:placeholder="t('TXT_CODE_4ea93630')"
/>
</div>
<div class="mb-20">
@ -252,11 +258,7 @@ onMounted(async () => {
<a-typography-title :level="5">{{ t("TXT_CODE_ef0ce2e") }}</a-typography-title>
<a-typography-paragraph>
<a-typography-text type="secondary">
{{
t(
"TXT_CODE_9e9d3767"
)
}}
{{ t("TXT_CODE_9e9d3767") }}
<br />
<a href="https://docs.mcsmanager.com/" target="_blank">
{{ t("TXT_CODE_b01f8383") }}

View File

@ -0,0 +1,82 @@
<script setup lang="ts">
import { useLayoutCardTools } from "@/hooks/useCardTools";
import { t } from "@/lang/i18n";
import type { LayoutCard } from "@/types";
import { computed, onMounted } from "vue";
import { userInfoApi } from "@/services/apis/index";
const props = defineProps<{
card: LayoutCard;
}>();
const { execute, state } = userInfoApi();
const getInstanceList = async () => {
await execute({
params: {
advanced: true
}
});
};
const { getMetaValue } = useLayoutCardTools(props.card);
const type = getMetaValue<string>("type");
const computedStatusList = computed(() => {
if (!state.value) return [];
return [
{
type: "instance_all",
title: t("管理员所分配给您的所有实例总数"),
value: state.value.instances.length
},
{
type: "instance_running",
title: t("实例正在运行中的数量"),
value: state.value.instances.filter((e) => e.status == 3).length
},
{
type: "instance_stop",
title: t("实例未处于运行中的数量"),
value: state.value.instances.filter((e) => e.status == 0).length
},
{
type: "instance_error",
title: t("暂时不可使用的实例数"),
value: state.value.instances.filter((e) => e.status == -1 || e.status == 1 || e.status == 2)
.length
}
];
});
const realStatus = computed(() => computedStatusList.value.find((v) => v.type === type));
onMounted(() => {
getInstanceList();
});
</script>
<template>
<CardPanel class="StatusBlock" style="height: 100%">
<template #title>{{ card.title }}</template>
<template #body>
<a-typography-text class="color-info">
{{ realStatus?.title }}
</a-typography-text>
<div class="value">{{ realStatus?.value }}</div>
</template>
</CardPanel>
</template>
<style lang="scss" scoped>
.StatusBlock {
.value {
font-weight: 800;
font-size: var(--font-h1);
margin-top: 4px;
}
}
</style>