Merge pull request #1012 from Bluemangoo/instance-list

Instance list
This commit is contained in:
unitwk 2023-09-17 13:53:23 +08:00 committed by GitHub
commit fa3c5adbc3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 199 additions and 72 deletions

View File

@ -44,6 +44,7 @@ declare module 'vue' {
CardPanel: typeof import('./src/components/CardPanel.vue')['default']
CopyButton: typeof import('./src/components/CopyButton.vue')['default']
DataStatistic: typeof import('./src/components/DataStatistic.vue')['default']
ErrorCard: typeof import('./src/components/ErrorCard.vue')['default']
IconBtn: typeof import('./src/components/IconBtn.vue')['default']
InnerCard: typeof import('./src/components/InnerCard.vue')['default']
InputDialogProvider: typeof import('./src/components/InputDialogProvider.vue')['default']

View File

@ -0,0 +1,53 @@
<script setup lang="ts">
import CardPanel from "@/components/CardPanel.vue";
const props = defineProps<{
title: string,
details?: string
}>();
</script>
<template>
<div class="h-100 w-100">
<CardPanel>
<template #body>
<div class="error-panel">
<div class="error-card">
<div class="error-title">
<a-typography-title :level="3">
{{ props.title }}
</a-typography-title>
</div>
<div v-if="props.details" class="error-details">
<a-typography>
{{ props.details }}
</a-typography>
</div>
</div>
</div>
</template>
</CardPanel>
</div>
</template>
<style scoped lang="scss">
.error-panel {
height: 100% !important;
justify-content: center;
display: flex;
align-items: center;
}
.error-card {
background-color: #fff2f0;
border: 1px solid #ffccc7;
box-sizing: border-box;
padding: 32px 32px;
color: rgba(0, 0, 0, 0.88);
border-radius: 8px;
width: 400px;
height: 247.2px;
gap: 6px;
}
</style>

View File

@ -1,5 +1,5 @@
import { useDefineApi } from "@/stores/useDefineApi";
import type { InstanceDetail, NodeStatus, Settings, UserInfo } from "@/types";
import type { InstanceDetail, NodeStatus, Settings, UserInfo, UserInstance } from "@/types";
import type { BaseUserInfo } from "@/types/user";
import type { IPanelOverviewResponse } from "../../../../common/global";
@ -8,12 +8,12 @@ import type { IPanelOverviewResponse } from "../../../../common/global";
// 用户登录
export const loginUser = useDefineApi<
| {
// Post
data: {
username: string;
password: string;
};
}
// Post
data: {
username: string;
password: string;
};
}
| undefined,
// Response
{
@ -35,6 +35,16 @@ export const userInfoApi = useDefineApi<any, BaseUserInfo>({
url: "/api/auth/"
});
export const userInfoApiAdvanced = useDefineApi<
{
params: {
uuid: string;
advanced: boolean;
};
}, BaseUserInfo>({
url: "/api/auth/"
});
// 获取远程服务列表
export const remoteNodeList = useDefineApi<any, NodeStatus[]>({
url: "/api/service/remote_services_list"
@ -68,8 +78,8 @@ export const settingInfo = useDefineApi<any, Settings>({
// 提交设置信息
export const setSettingInfo = useDefineApi<
| {
data: Settings;
}
data: Settings;
}
| undefined,
string
>({
@ -119,6 +129,22 @@ export const addUser = useDefineApi<
method: "POST"
});
// 用户管理 用户配置
export const updateUserInstance = useDefineApi<
{
data: {
config: {
instances: UserInstance[];
}
uuid: string
};
},
boolean
>({
url: "/api/auth",
method: "PUT"
});
// 获取总览
// 获取设置信息

View File

@ -1,5 +1,4 @@
/* eslint-disable no-unused-vars */
import type { IGlobalInstanceConfig } from "./../../../common/global";
import type { IGlobalInstanceConfig } from "../../../common/global";
import type { LayoutCardHeight } from "@/config/originLayoutConfig";
export interface JsonData {
@ -95,6 +94,20 @@ export interface UserInfo {
passWordType: number;
loginTime: string;
isInit: boolean;
instances: any[];
instances: UserInstance[];
apiKey: string;
}
export interface UserInstance {
endTime: string;
hostIp: string;
ie: string;
instanceUuid: string;
lastDatetime: string;
nickname: string;
oe: string;
remarks: string;
serviceUuid: string;
status: number;
stopCommand: string;
}

View File

@ -1,9 +1,11 @@
import type { UserInstance } from "@/types/index";
export interface BaseUserInfo {
uuid: string;
userName: string;
loginTime: string;
registerTime: string;
instances: any[];
instances: UserInstance[];
permission: number;
token: string;
apiKey: string;

View File

@ -1,69 +1,95 @@
<script setup lang="ts">
import CardPanel from "@/components/CardPanel.vue";
import type { LayoutCard } from "@/types/index";
import { computed } from "vue";
import type { LayoutCard, UserInstance } from "@/types";
import { computed, ref } from "vue";
import type { Ref } from "vue";
import { t } from "@/lang/i18n";
import BetweenMenus from "@/components/BetweenMenus.vue";
import { useScreen } from "@/hooks/useScreen";
import { arrayFilter } from "@/tools/array";
import { useAppRouters } from "@/hooks/useAppRouters";
interface InstanceBaseInfo {
key?: number | string;
uuid: string;
name: string;
daemon: string;
limitTime: string;
status: number;
}
import { userInfoApiAdvanced } from "@/services/apis";
import { useLayoutCardTools } from "@/hooks/useCardTools";
import ErrorCard from "@/components/ErrorCard.vue";
import { updateUserInstance } from "@/services/apis";
const props = defineProps<{
card: LayoutCard;
uuid: string;
}>();
const { getRouteParamsUrl, toPage } = useAppRouters();
const screen = useScreen();
const handleDelete = (user: InstanceBaseInfo) => {};
let dataSource: Ref<UserInstance[]> = ref([]);
const { getMetaOrRouteValue } = useLayoutCardTools(props.card);
let userUuid: string | undefined = getMetaOrRouteValue("uuid");
const dataSource: InstanceBaseInfo[] = [
{
key: "1",
name: "实例名称1",
daemon: "守护进程1",
limitTime: "限制时间1",
status: 1,
uuid: "1"
const handleDelete = async (deletedInstance: UserInstance) => {
for (let valueKey = 0; valueKey < dataSource.value.length; valueKey++) {
const instance = dataSource.value[valueKey];
if (deletedInstance.serviceUuid == instance.serviceUuid && deletedInstance.instanceUuid == instance.instanceUuid) {
dataSource.value.splice(valueKey, 1);
await updateUserInstance().execute({
data: {
config: {
instances: dataSource.value
},
uuid: <string>userUuid
}
});
break;
}
}
];
};
async function refreshChart() {
if (userUuid == null) {
return;
}
const rawUserInfo = (await userInfoApiAdvanced().execute({
params: {
uuid: <string>userUuid,
advanced: true
}
})).value;
if (!rawUserInfo) {
return;
}
const newDataSource: UserInstance[] = [];
for (const instance of rawUserInfo.instances) {
newDataSource.push(instance);
}
dataSource.value = newDataSource;
}
refreshChart();
const columns = computed(() => {
return arrayFilter([
{
align: "center",
title: "所属节点",
dataIndex: "daemon",
title: t("所属节点"),
dataIndex: "serviceUuid",
key: "daemon",
minWidth: "200px"
},
{
align: "center",
title: "实例名称",
dataIndex: "name",
title: t("实例名称"),
dataIndex: "nickname",
key: "name",
minWidth: "200px"
},
{
align: "center",
title: "到期时间",
dataIndex: "limitTime",
title: t("到期时间"),
dataIndex: "lastDatetime",
key: "limitTime",
minWidth: "200px",
condition: () => !screen.isPhone.value
},
{
align: "center",
title: "实例状态",
title: t("实例状态"),
dataIndex: "status",
key: "status",
minWidth: "200px",
@ -71,9 +97,10 @@ const columns = computed(() => {
},
{
align: "center",
title: "操作",
key: "action",
minWidth: "200px"
title: t("操作"),
key: "operation",
minWidth: "200px",
scopedSlots: { customRender: "operation" }
}
]);
});
@ -82,34 +109,39 @@ const columns = computed(() => {
<template>
<div style="height: 100%" class="container">
<a-row :gutter="[24, 24]" style="height: 100%">
<a-col :span="24">
<BetweenMenus>
<template #left>
<a-typography-title class="mb-0" :level="4">
{{ t("TXT_CODE_e1c9a6ac") }}
</a-typography-title>
</template>
<template #right>
<a-button type="primary">{{ t("TXT_CODE_a60466a1") }}</a-button>
</template>
</BetweenMenus>
</a-col>
<div v-if="userUuid" class="h-100 w-100">
<a-col :span="24">
<BetweenMenus>
<template #left>
<a-typography-title class="mb-0" :level="4">
{{ t("TXT_CODE_e1c9a6ac") }}
</a-typography-title>
</template>
<template #right>
<a-button type="primary">{{ t("TXT_CODE_a60466a1") }}</a-button>
</template>
</BetweenMenus>
</a-col>
<a-col :span="24">
<CardPanel style="height: 100%">
<template #body>
<a-table :data-source="dataSource" :columns="columns">
<template #bodyCell="{ column }">
<template v-if="column.key === 'action'">
<a-button danger>
{{ t("TXT_CODE_ecbd7449") }}
</a-button>
<a-col :span="24">
<CardPanel class="h-100">
<template #body>
<a-table :data-source="dataSource" :columns="columns">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'operation'">
<a-button danger @click="handleDelete(record)">
{{ t("TXT_CODE_ecbd7449") }}
</a-button>
</template>
</template>
</template>
</a-table>
</template>
</CardPanel>
</a-col>
</a-table>
</template>
</CardPanel>
</a-col>
</div>
<div v-else class="h-100 w-100">
<ErrorCard :title="t('缺少参数,组件无法运作')" :details="t('请添加 get 参数 uuid')" style="min-height: 600px" />
</div>
</a-row>
</div>
</template>