添加主机身份页面.

This commit is contained in:
lijiahang
2023-09-21 13:50:42 +08:00
parent fbc40116f0
commit b13cbd8cca
39 changed files with 444 additions and 649 deletions

View File

@@ -17,6 +17,7 @@ export interface HostIdentityCreateRequest {
*/
export interface HostIdentityUpdateRequest extends HostIdentityCreateRequest {
id: number;
useNewPassword?: boolean;
}
/**
@@ -66,23 +67,11 @@ export function getHostIdentity(id: number) {
return axios.get<HostIdentityQueryResponse>('/asset/host-identity/get', { params: { id } });
}
/**
* 通过 id 批量查询主机身份
*/
export function getHostIdentityList(idList: Array<number>) {
return axios.get<HostIdentityQueryResponse[]>('/asset/host-identity/list', {
params: { idList },
paramsSerializer: params => {
return qs.stringify(params, { arrayFormat: 'comma' });
}
});
}
/**
* 查询主机身份
*/
export function getHostIdentityListAll(request: HostIdentityQueryRequest) {
return axios.post<Array<HostIdentityQueryResponse>>('/asset/host-identity/list-all', request);
export function getHostIdentityList() {
return axios.post<Array<HostIdentityQueryResponse>>('/asset/host-identity/list');
}
/**
@@ -99,14 +88,3 @@ export function deleteHostIdentity(id: number) {
return axios.delete('/asset/host-identity/delete', { params: { id } });
}
/**
* 通过 id 批量删除主机身份
*/
export function batchDeleteHostIdentity(idList: Array<number>) {
return axios.delete('/asset/host-identity/delete-batch', {
params: { idList },
paramsSerializer: params => {
return qs.stringify(params, { arrayFormat: 'comma' });
}
});
}

View File

@@ -9,7 +9,6 @@ export interface HostKeyCreateRequest {
publicKey?: string;
privateKey?: string;
password?: string;
useNewPassword?: boolean;
}
/**
@@ -17,6 +16,7 @@ export interface HostKeyCreateRequest {
*/
export interface HostKeyUpdateRequest extends HostKeyCreateRequest {
id: number;
useNewPassword?: boolean;
}
/**
@@ -66,8 +66,8 @@ export function getHostKey(id: number) {
/**
* 查询主机秘钥
*/
export function getHostKeyListAll() {
return axios.post<Array<HostKeyQueryResponse>>('/asset/host-key/list-all');
export function getHostKeyList() {
return axios.post<Array<HostKeyQueryResponse>>('/asset/host-key/list');
}
/**

View File

@@ -13,7 +13,7 @@ export interface TagCreateRequest {
/**
* tag 响应对象
*/
export interface TagResponse {
export interface TagQueryResponse {
id: number;
name: string;
}
@@ -29,5 +29,5 @@ export function createTag(request: TagCreateRequest) {
* 查询标签
*/
export function getTagList(type: TagType) {
return axios.get<TagResponse>('/infra/tag/list', { params: { type } });
return axios.get<TagQueryResponse>('/infra/tag/list', { params: { type } });
}

View File

@@ -115,6 +115,10 @@ body {
}
}
.span-blue {
color: rgb(var(--arcoblue-6));
}
#app {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;

View File

@@ -0,0 +1,52 @@
<template>
<a-select v-model:model-value="value"
:options="optionData()"
placeholder="请选择主机秘钥"
allow-clear />
</template>
<script lang="ts">
export default {
name: 'host-key-selector'
};
</script>
<script lang="ts" setup>
import { computed } from 'vue';
import { useCacheStore } from '@/store';
import { SelectOptionData } from '@arco-design/web-vue';
const props = defineProps({
modelValue: Number,
});
const emits = defineEmits(['update:modelValue']);
const value = computed({
get() {
return props.modelValue;
},
set(e) {
if (e) {
emits('update:modelValue', e);
} else {
emits('update:modelValue', null);
}
}
});
// 选项数据
const cacheStore = useCacheStore();
const optionData = (): SelectOptionData[] => {
return cacheStore.hostKeys.map(s => {
return {
label: s.name,
value: s.id,
};
});
};
</script>
<style scoped>
</style>

View File

@@ -1,13 +1,14 @@
import { defineStore } from 'pinia';
import { CacheState } from './types';
export type CacheType = 'menus' | 'roles' | 'tags'
export type CacheType = 'menus' | 'roles' | 'tags' | 'hostKeys'
const useCacheStore = defineStore('cache', {
state: (): CacheState => ({
menus: [],
roles: [],
tags: []
tags: [],
hostKeys: [],
}),
getters: {},

View File

@@ -1,9 +1,13 @@
import { MenuQueryResponse } from '@/api/system/menu';
import { RoleQueryResponse } from '@/api/user/role';
import { TagResponse } from '@/api/meta/tag';
import { TagQueryResponse } from '@/api/meta/tag';
import { HostKeyQueryResponse } from '@/api/asset/host-key';
export interface CacheState {
menus: MenuQueryResponse[];
roles: RoleQueryResponse[];
tags: TagResponse[];
tags: TagQueryResponse[];
hostKeys: HostKeyQueryResponse[];
[key: string]: unknown;
}

View File

@@ -1,145 +0,0 @@
<template>
<a-drawer :visible="visible"
:title="title"
:width="430"
:mask-closable="false"
:unmount-on-close="true"
:on-before-ok="handlerOk"
@cancel="handleClose">
<a-spin :loading="loading">
<a-form :model="formModel"
ref="formRef"
label-align="right"
:style="{ width: '380px' }"
:label-col-props="{ span: 6 }"
:wrapper-col-props="{ span: 18 }"
:rules="formRules">
<!-- 名称 -->
<a-form-item field="name" label="名称">
<a-input v-model="formModel.name" placeholder="请输入名称" />
</a-form-item>
<!-- 用户名 -->
<a-form-item field="username" label="用户名">
<a-input v-model="formModel.username" placeholder="请输入用户名" />
</a-form-item>
<!-- 用户密码 -->
<a-form-item field="password" label="用户密码">
<a-input v-model="formModel.password" placeholder="请输入用户密码" />
</a-form-item>
<!-- 秘钥id -->
<a-form-item field="keyId" label="秘钥id">
<a-input-number v-model="formModel.keyId" placeholder="请输入秘钥id" />
</a-form-item>
</a-form>
</a-spin>
</a-drawer>
</template>
<script lang="ts">
export default {
name: 'asset-host-identity-form-drawer'
};
</script>
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import useLoading from '@/hooks/loading';
import useVisible from '@/hooks/visible';
import formRules from '../types/form.rules';
import { createHostIdentity, updateHostIdentity } from '@/api/asset/host-identity';
import { Message } from '@arco-design/web-vue';
import {} from '../types/enum.types';
import {} from '../types/const';
import { toOptions } from '@/utils/enum';
const { visible, setVisible } = useVisible();
const { loading, setLoading } = useLoading();
const title = ref<string>();
const isAddHandle = ref<boolean>(true);
const defaultForm = () => {
return {
id: undefined,
name: undefined,
username: undefined,
password: undefined,
keyId: undefined,
};
};
const formRef = ref<any>();
const formModel = reactive<Record<string, any>>(defaultForm());
const emits = defineEmits(['added', 'updated']);
// 打开新增
const openAdd = () => {
title.value = '添加主机身份';
isAddHandle.value = true;
renderForm({ ...defaultForm() });
setVisible(true);
};
// 打开修改
const openUpdate = (record: any) => {
title.value = '修改主机身份';
isAddHandle.value = false;
renderForm({ ...defaultForm(), ...record });
setVisible(true);
};
// 渲染表单
const renderForm = (record: any) => {
Object.keys(formModel).forEach(k => {
formModel[k] = record[k];
});
};
defineExpose({ openAdd, openUpdate });
// 确定
const handlerOk = async () => {
setLoading(true);
try {
// 验证参数
const error = await formRef.value.validate();
if (error) {
return false;
}
if (isAddHandle.value) {
// 新增
await createHostIdentity(formModel as any);
Message.success('创建成功');
emits('added');
} else {
// 修改
await updateHostIdentity(formModel as any);
Message.success('修改成功');
emits('updated');
}
// 清空
handlerClear();
} catch (e) {
return false;
} finally {
setLoading(false);
}
};
// 关闭
const handleClose = () => {
handlerClear();
};
// 清空
const handlerClear = () => {
setLoading(false);
setVisible(false);
};
</script>
<style lang="less" scoped>
</style>

View File

@@ -29,12 +29,23 @@
<a-input v-model="formModel.username" placeholder="请输入用户名" />
</a-form-item>
<!-- 用户密码 -->
<a-form-item field="password" label="用户密码">
<a-input v-model="formModel.password" placeholder="请输入用户密码" />
<a-form-item field="password"
label="用户密码"
style="justify-content: space-between;">
<a-input-password v-model="formModel.password"
:disabled="!formModel.useNewPassword"
class="password-input"
placeholder="请输入用户密码" />
<a-switch v-model="formModel.useNewPassword"
class="password-switch"
type="round"
size="large"
checked-text="使用新密码"
unchecked-text="使用原密码" />
</a-form-item>
<!-- 秘钥id -->
<a-form-item field="keyId" label="秘钥id">
<a-input-number v-model="formModel.keyId" placeholder="请输入秘钥id" />
<a-form-item field="keyId" label="主机秘钥">
<host-key-selector v-model="formModel.keyId" />
</a-form-item>
</a-form>
</a-spin>
@@ -54,9 +65,7 @@
import formRules from '../types/form.rules';
import { createHostIdentity, updateHostIdentity } from '@/api/asset/host-identity';
import { Message } from '@arco-design/web-vue';
import {} from '../types/enum.types';
import {} from '../types/const';
import { toOptions } from '@/utils/enum';
import HostKeySelector from '@/components/asset/host-key/host-key-selector.vue';
const { visible, setVisible } = useVisible();
const { loading, setLoading } = useLoading();
@@ -70,6 +79,7 @@
name: undefined,
username: undefined,
password: undefined,
useNewPassword: false,
keyId: undefined,
};
};
@@ -147,5 +157,11 @@
</script>
<style lang="less" scoped>
.password-input {
width: 240px;
}
.password-switch {
margin-left: 16px;
}
</style>

View File

@@ -7,23 +7,19 @@
@reset="fetchTableData">
<!-- id -->
<a-form-item field="id" label="id" label-col-flex="50px">
<a-input-number v-model="formModel.id" placeholder="请输入id" allow-clear/>
<a-input-number v-model="formModel.id" placeholder="请输入id" allow-clear />
</a-form-item>
<!-- 名称 -->
<a-form-item field="name" label="名称" label-col-flex="50px">
<a-input v-model="formModel.name" placeholder="请输入名称" allow-clear/>
<a-input v-model="formModel.name" placeholder="请输入名称" allow-clear />
</a-form-item>
<!-- 用户名 -->
<a-form-item field="username" label="用户名" label-col-flex="50px">
<a-input v-model="formModel.username" placeholder="请输入用户名" allow-clear/>
<a-input v-model="formModel.username" placeholder="请输入用户名" allow-clear />
</a-form-item>
<!-- 用户密码 -->
<a-form-item field="password" label="用户密码" label-col-flex="50px">
<a-input v-model="formModel.password" placeholder="请输入用户密码" allow-clear/>
</a-form-item>
<!-- 秘钥id -->
<a-form-item field="keyId" label="秘钥id" label-col-flex="50px">
<a-input-number v-model="formModel.keyId" placeholder="请输入秘钥id" allow-clear/>
<!-- 主机秘钥 -->
<a-form-item field="keyId" label="主机秘钥" label-col-flex="50px">
<host-key-selector v-model="formModel.keyId" allow-clear />
</a-form-item>
</a-query-header>
</a-card>
@@ -32,7 +28,7 @@
<template #title>
<!-- 左侧标题 -->
<div class="table-title">
主机身份列表
身份列表
</div>
<!-- 右侧按钮 -->
<div class="table-bar-handle">
@@ -46,21 +42,6 @@
<icon-plus />
</template>
</a-button>
<!-- 删除 -->
<a-popconfirm position="br"
type="warning"
:content="`确认删除选中的${selectedKeys.length}条记录吗?`"
@ok="deleteSelectRows">
<a-button v-permission="['asset:host-identity:delete']"
type="secondary"
status="danger"
:disabled="selectedKeys.length === 0">
删除
<template #icon>
<icon-delete />
</template>
</a-button>
</a-popconfirm>
</a-space>
</div>
</template>
@@ -71,14 +52,16 @@
label-align="left"
:loading="loading"
:columns="columns"
v-model:selectedKeys="selectedKeys"
:row-selection="rowSelection"
:data="tableRenderData"
:pagination="pagination"
@page-change="(page) => fetchTableData(page, pagination.pageSize)"
@page-size-change="(size) => fetchTableData(pagination.current, size)"
:bordered="false">
<!-- 操作 -->
<template #keyId="{ record }">
{{ record.keyName }}
</template>
<!-- 操作 -->
<template #handle="{ record }">
<div class="table-handle-wrapper">
<!-- 修改 -->
@@ -114,46 +97,29 @@
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import { batchDeleteHostIdentity, deleteHostIdentity, getHostIdentityPage, HostIdentityQueryRequest, HostIdentityQueryResponse } from '@/api/asset/host-identity';
import { deleteHostIdentity, getHostIdentityPage, HostIdentityQueryRequest, HostIdentityQueryResponse } from '@/api/asset/host-identity';
import { Message } from '@arco-design/web-vue';
import useLoading from '@/hooks/loading';
import columns from '../types/table.columns';
import { defaultPagination, defaultRowSelection } from '@/types/table';
import {} from '../types/enum.types';
import {} from '../types/const';
import { toOptions } from '@/utils/enum';
import { defaultPagination } from '@/types/table';
import { getHostKeyList } from '@/api/asset/host-key';
import { useCacheStore } from '@/store';
import HostKeySelector from '@/components/asset/host-key/host-key-selector.vue';
const tableRenderData = ref<HostIdentityQueryResponse[]>();
const { loading, setLoading } = useLoading();
const emits = defineEmits(['openAdd', 'openUpdate']);
const cacheStore = useCacheStore();
const pagination = reactive(defaultPagination());
const selectedKeys = ref<number[]>([]);
const rowSelection = reactive(defaultRowSelection());
const formModel = reactive<HostIdentityQueryRequest>({
id: undefined,
name: undefined,
username: undefined,
password: undefined,
keyId: undefined,
});
// 删除选中行
const deleteSelectRows = async () => {
try {
setLoading(true);
// 调用删除接口
await batchDeleteHostIdentity(selectedKeys.value);
Message.success(`成功删除${selectedKeys.value.length}条数据`);
selectedKeys.value = [];
// 重新加载数据
await fetchTableData();
} finally {
setLoading(false);
}
};
// 删除当前行
const deleteRow = async ({ id }: { id: number }) => {
try {
@@ -202,6 +168,13 @@
};
fetchTableData();
// 获取主机秘钥列表
const fetchHostKeyList = async () => {
const { data } = await getHostKeyList();
cacheStore.set('hostKeys', data);
};
fetchHostKeyList();
</script>
<style lang="less" scoped>

View File

@@ -2,30 +2,36 @@
<div class="layout-container">
<!-- 表格 -->
<host-identity-table ref="table"
@openAdd="() => drawer.openAdd()"
@openUpdate="(e) => drawer.openUpdate(e)" />
@openAdd="() => modal.openAdd()"
@openUpdate="(e) => modal.openUpdate(e)" />
<!-- 添加修改模态框 -->
<host-identity-form-drawer ref="drawer"
@added="() => table.addedCallback()"
@updated="() => table.updatedCallback()" />
<host-identity-form-modal ref="modal"
@added="() => table.addedCallback()"
@updated="() => table.updatedCallback()" />
</div>
</template>
<script lang="ts">
export default {
name: 'asset-host-identity'
name: 'assetHostIdentity'
};
</script>
<script lang="ts" setup>
import HostIdentityTable from './components/host-identity-table.vue';
import HostIdentityFormDrawer from './components/host-identity-form-drawer.vue';
import HostIdentityFormModal from './components/host-identity-form-modal.vue';
import { ref } from 'vue';
import { onUnmounted, ref } from 'vue';
import { useCacheStore } from '@/store';
const table = ref();
const drawer = ref();
const modal = ref();
// 卸载时清除 tags cache
onUnmounted(() => {
const cacheStore = useCacheStore();
cacheStore.set('hostKeys', []);
});
</script>

View File

@@ -13,28 +13,14 @@ const columns = [
title: '名称',
dataIndex: 'name',
slotName: 'name',
align: 'center',
ellipsis: true,
tooltip: true,
}, {
title: '用户名',
dataIndex: 'username',
slotName: 'username',
align: 'center',
ellipsis: true,
tooltip: true,
}, {
title: '用户密码',
dataIndex: 'password',
slotName: 'password',
align: 'center',
ellipsis: true,
tooltip: true,
}, {
title: '秘钥id',
title: '主机秘钥',
dataIndex: 'keyId',
slotName: 'keyId',
align: 'center',
}, {
title: '创建时间',
dataIndex: 'createTime',
@@ -53,14 +39,6 @@ const columns = [
render: ({ record }) => {
return dateFormat(new Date(record.updateTime));
},
}, {
title: '创建人',
dataIndex: 'creator',
slotName: 'creator',
}, {
title: '修改人',
dataIndex: 'updater',
slotName: 'updater',
}, {
title: '操作',
slotName: 'handle',

View File

@@ -20,7 +20,7 @@
<template #title>
<!-- 左侧标题 -->
<div class="table-title">
主机秘钥列表
秘钥列表
</div>
<!-- 右侧按钮 -->
<div class="table-bar-handle">
@@ -49,6 +49,10 @@
@page-change="(page) => fetchTableData(page, pagination.pageSize)"
@page-size-change="(size) => fetchTableData(pagination.current, size)"
:bordered="false">
<!-- 名称 -->
<template #name="{ record }">
<span class="span-blue">{{ record.name }}</span>
</template>
<!-- 操作 -->
<template #handle="{ record }">
<div class="table-handle-wrapper">

View File

@@ -14,7 +14,7 @@
<script lang="ts">
export default {
name: 'asset-host-key'
name: 'assetHostKey'
};
</script>

View File

@@ -78,6 +78,14 @@
@page-change="(page) => fetchTableData(page, pagination.pageSize)"
@page-size-change="(size) => fetchTableData(pagination.current, size)"
:bordered="false">
<!-- 名称 -->
<template #name="{ record }">
<span class="span-blue">{{ record.name }}</span>
</template>
<!-- 编码 -->
<template #code="{ record }">
<a-tag>{{ record.code }}</a-tag>
</template>
<!-- 地址 -->
<template #address="{ record }">
<span class="host-address" title="点击复制" @click="copy(record.address)">

View File

@@ -56,6 +56,10 @@
@page-change="(page) => fetchTableData(page, pagination.pageSize)"
@page-size-change="(size) => fetchTableData(pagination.current, size)"
:bordered="false">
<!-- 名称 -->
<template #name="{ record }">
<span class="span-blue">{{ record.name }}</span>
</template>
<!-- 编码 -->
<template #code="{ record }">
<a-tag>{{ record.code }}</a-tag>

View File

@@ -72,7 +72,7 @@
:bordered="false">
<!-- 用户名 -->
<template #username="{ record }">
<span class="username-text">{{ record.username }}</span>
<span class="span-blue">{{ record.username }}</span>
</template>
<!-- 状态 -->
<template #status="{ record }">
@@ -248,7 +248,5 @@
</script>
<style lang="less" scoped>
.username-text {
color: rgb(var(--arcoblue-6));
}
</style>

View File

@@ -25,7 +25,6 @@ const columns = [
title: '手机号',
dataIndex: 'mobile',
slotName: 'mobile',
align: 'center',
ellipsis: true,
tooltip: true,
}, {