Commit b361c818 authored by liwei's avatar liwei

新增了实时同步的任务运维界面

parent f51c6e20
<template> <template>
<div>任务运维</div> <div style="width: 100%;height: 100%;background-color: white">
<div style="width: 100%;height:30%">
<div style="display: flex;height: 30px;margin-left: 20px">
<div style="display: flex;align-items: center;font-weight: bold">实时同步运维</div>
<div class="selectCss">
<BasicForm @register="registerForm"/>
</div>
</div>
<div class="homePage_module">
<Row :gutter="16" class="homePage_left">
<Col :span="24">
<Card>
<div class="card_content">
<Col :span="4">
<div class="card_item">
<div class="card_itemInfo">
<div ref="chartRef" :style="{ width, height }"></div>
</div>
</div>
</Col>
<Col :span="4" @click="clickButton(1)" :class="{ 'listItemClass': selectedItem === 1 }">
<div class="card_item">
<Icon icon="eos-icons:database" :size="30" :color="'#1091FE'" />
<div class="card_itemInfo">
<div class="dataNum">50</div>
<div class="dataTitle">所有任务</div>
</div>
</div>
</Col>
<Col :span="4" @click="clickButton(2)" :class="{ 'listItemClass': selectedItem === 2 }">
<div class="card_item">
<Icon icon="ri:hourglass-fill" :size="30" :color="'rgb(147, 140, 206)'" />
<div class="card_itemInfo">
<div class="dataNum">1</div>
<div class="dataTitle">初始化</div>
</div>
</div>
</Col>
<Col :span="4" @click="clickButton(3)" :class="{ 'listItemClass': selectedItem === 3 }">
<div class="card_item">
<Icon icon="line-md:loading-twotone-loop" :size="30" :color="'rgb(81, 160, 248)'" />
<div class="card_itemInfo">
<div class="dataNum">10</div>
<div class="dataTitle">进行中</div>
</div>
</div>
</Col>
<Col :span="4" @click="clickButton(4)" :class="{ 'listItemClass': selectedItem === 4 }">
<div class="card_item">
<Icon icon="line-md:close-circle-filled" :size="30" :color="'rgb(212, 115, 113)'" />
<div class="card_itemInfo">
<div class="dataNum">9</div>
<div class="dataTitle">失败</div>
</div>
</div>
</Col>
<Col :span="4" @click="clickButton(5)" :class="{ 'listItemClass': selectedItem === 5 }">
<div class="card_item">
<Icon icon="line-md:minus-circle-filled" :size="30" :color="'rgb(234, 150, 0)'" />
<div class="card_itemInfo">
<div class="dataNum">30</div>
<div class="dataTitle">停止</div>
</div>
</div>
</Col>
</div>
</Card>
</Col>
</Row>
</div>
</div>
<div style="width: 100%;height:70%">
<div style="display:flex;justify-content: space-between">
<div style="margin-left: 10px">
<a-input-search
v-model:value="value"
placeholder="请输入关键字"
style="width: 200px"
@search="onSearch"
/>
</div>
<div>
<a-button type="primary" @click="" style="margin-right: 20px">启动</a-button>
<a-button type="primary" @click="" style="margin-right: 20px">暂停</a-button>
</div>
</div>
<BasicTable @register="registerTable">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{
icon: 'ant-design:form-outlined',
// label: '查看详情',
onClick: resetNameButton.bind(null, record),
},
{
icon: 'ant-design:edit-outlined',
// label: '编辑',
onClick: resetNameButton.bind(null, record),
},
{
icon: 'ant-design:form-outlined',
// label: '启动',
onClick: resetNameButton.bind(null, record),
},
{
icon: 'ant-design:form-outlined',
// label: '暂停',
onClick: resetNameButton.bind(null, record),
},
]"
/>
</template>
</template>
</BasicTable>
</div>
</div>
</template> </template>
<script> <script lang="ts" setup>
export default { import { Card, Col, Row} from 'ant-design-vue';
name: "index" import Icon from '@/components/Icon/Icon.vue';
} import { reactive,unref,onDeactivated,onMounted,ref,watch,Ref } from 'vue';
import { BasicTable, useTable, TableAction } from '@/components/Table';
import { PageWrapper } from '@/components/Page';
import { useMessage } from '@/hooks/web/useMessage';
import { useModal } from '@/components/Modal';
import { useGo } from '@/hooks/web/usePage';
import { useRoute } from 'vue-router';
import { router } from '@/router';
import {downloadByData} from "@/utils/file/download";
import { columns,searchFormSchema,selectFormSchema } from './maintenance.data';
import { tableData,TreeData } from './maintenanceData'
import { BasicForm, useForm } from '@/components/Form';
import { useECharts } from '@/hooks/web/useECharts';
defineOptions({ name: 'AccountManagement' });
const props = defineProps({
loading: Boolean,
width: {
type: String as PropType<string>,
default: '100%',
},
height: {
type: String as PropType<string>,
default: '300px',
},
});
const chartRef = ref<HTMLDivElement | null>(null);
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
const { createMessage, createConfirm } = useMessage();
const route = useRoute();
const go = useGo();
const searchInfo = ref('');
const selectedItem = ref()
watch(
() => props.loading,
() => {
if (props.loading) {
return;
}
setOptions({
tooltip: {
trigger: 'item',
},
legend: {
bottom: '1%',
left: 'center',
},
series: [
{
color: ['rgb(147, 140, 206)', 'rgb(81, 160, 248)', 'rgb(212, 115, 113)', 'rgb(234, 150, 0)'],
name: '访问来源',
type: 'pie',
radius: ['50%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 0,
borderColor: '#fff',
borderWidth: 2,
},
label: {
show: false,
position: 'center',
},
emphasis: {
label: {
show: true,
fontSize: '12',
fontWeight: 'bold',
},
},
labelLine: {
show: false,
},
data: [
{ value: 1 },
{ value: 10 },
{ value: 9 },
{ value: 30 },
],
animationType: 'scale',
animationEasing: 'exponentialInOut',
animationDelay: function () {
return Math.random() * 100;
},
},
],
});
},
{ immediate: true },
);
const [registerTable, { reload,getForm,getRowSelection }] = useTable({
title: '',
api: async (params) => {
const response = {
pageNu: "1",
pageSize: "10",
pages: "1",
total: tableData.length,
code:'',
message:'',
data: [],
};
return { ...response,data: tableData };
},
rowKey: 'businessId',
columns,
rowSelection: true,
useSearchForm: false,
showTableSetting: false,
showIndexColumn:false,
bordered: true,
actionColumn: {
width: 200,
title: '操作',
dataIndex: 'action',
},
});
//初始化表单
const [registerForm, { setFieldsValue, updateSchema, resetFields, validate }] = useForm({
labelWidth: 100,
schemas: selectFormSchema,
showActionButtonGroup: false,
actionColOptions: {
span: 23,
},
});
function clickButton(item){
selectedItem.value = item;
}
function resetNameButton(record){
}
/**删除 按钮*/
function deleteButton(record){
}
/** 转成树 */
function handleTree(data, id, parentId, children, rootId) {
id = id || 'id'
parentId = parentId || 'parentId'
children = children || 'children'
rootId = rootId || Math.min.apply(Math, data.map(item => { return item[parentId] })) || 0
// 对源数据深度克隆
const cloneData = JSON.parse(JSON.stringify(data))
// 循环所有项
const treeData = cloneData.filter(father => {
const branchArr = cloneData.filter(child => {
// 返回每一项的子级数组
return father[id] === child[parentId]
})
branchArr.length > 0 ? father.children = branchArr : ''
// 返回第一层
return father[parentId] === rootId
})
return treeData !== '' ? treeData : data
}
onMounted(() => {
const treeData = handleTree(TreeData, 'businessId',undefined,undefined,undefined)
updateSchema([
{
field: 'tree',
componentProps: {
treeData: treeData
},
},
]);
});
</script> </script>
<style scoped> <style lang="less" scoped>
.selectCss{
margin-left: 30px;
::v-deep(.ant-select-selector){
width:200px!important;
}
//::v-deep(.ant-select){
// width:200px!important;
//}
}
.homePage_module {
margin-top: 20px;
::v-deep(.ant-card-body) {
display: flex;
justify-content: center;
height: 180px;
}
.homePage_left {
::v-deep(.ant-tabs-tab) {
font-size: 18px;
font-weight: 600;
}
.card_content {
width: 70%;
display: flex;
justify-content: center;
align-items: center;
.card_item {
padding-left: 20px;
display: flex;
align-items: center;
width: 120px;
height: 80px;
border-radius: 10px;
.card_itemInfo {
padding-left: 7px;
.dataNum {
font-size: 20px;
line-height: 26px;
font-weight: bold;
}
.dataTitle {
font-size: 12px;
line-height: 12px;
}
}
}
}
}
}
.listItemClass{
background-color: rgb(230, 243, 255);
}
</style> </style>
import {getAllRoleList} from '@/api/system/role/role';
import { BasicColumn, FormSchema } from '@/components/Table';
import {h} from "vue";
import {Input, Select, Tag} from "ant-design-vue";
import { Switch } from 'ant-design-vue';
import {useMessage} from "@/hooks/web/useMessage";
import {changeFlagApi} from "@/api/system/user/user";
import {DescItem} from "@/components/Description";
import {uploadApi} from "@/api/sys/upload"; // 引入开关组件
type CheckedType = boolean | string | number;
/**首页-table列表*/
export const columns: BasicColumn[] = [
{
title: '任务名称',
dataIndex: 'taskName',
width: 120
},
{
title: '启动时间',
dataIndex: 'startTime',
width: 120
},
{
title: '源端',
dataIndex: 'source',
width: 120
},
{
title: '目标端',
dataIndex: 'target',
width: 120
},
{
title: '拥有者',
dataIndex: 'owner',
width: 120
},
{
title: '任务版本',
dataIndex: 'taskVersion',
width: 120
},
{
title: '任务状态',
dataIndex: 'taskStatus',
width: 120
},
{
title: '时延',
dataIndex: 'latency',
width: 120
},
{
title: '24小时数据检查有差异',
dataIndex: 'dataCheckDifference24h',
width: 120
},
{
title: '最新事件时间',
dataIndex: 'latestEventTime',
width: 120
},
{
title: '未处理异常',
dataIndex: 'unhandledExceptions',
width: 120
},
{
title: 'DDL',
dataIndex: 'ddl',
width: 120
}
];
/**首页-搜索表单*/
export const searchFormSchema: FormSchema[] = [
{
field: 'taskName',
label: '',
component: 'Input',
rules: [
{
required: true,
message: '请输入关键字搜索',
},
],
componentProps: {
palceholder: '请输入关键字搜索',
}
},
];
/**首页-下拉框表单*/
export const selectFormSchema: FormSchema[] = [
{
field: 'tree',
label: '',
component: 'TreeSelect',
colProps: { span: 3 },
componentProps: {
treeCheckable: true,
fieldNames: {
label: 'treeName',
value: 'businessId',
},
getPopupContainer: () => document.body,
},
},
];
/**table列表*/
export const apiDocColumns: BasicColumn[] = [
{
"title": "参数code",
"dataIndex": "parameterCode",
"width": 120
},
{
"title": "参数位置",
"dataIndex": "parameterPosition",
"width": 120
},
{
"title": "Xpath",
"dataIndex": "xpath",
"width": 120
},
{
"title": "参数类型",
"dataIndex": "parameterType",
"width": 120
},
{
"title": "注释",
"dataIndex": "annotation",
"width": 120
},
{
"title": "是否必填",
"dataIndex": "isRequired",
"width": 120
},
{
"title": "参数值",
"dataIndex": "parameterValue",
"width": 120,
"editable": true,
"edit": true
}
];
/**form表单*/
export const guideModeFormSchema2: FormSchema[] = [
{
field: 'apiName',
label: 'API名称',
component: 'Input',
defaultValue: 'API_ONE',
rules: [
{
required: true,
message: '请输入API名称',
},
],
},
{
field: 'apiDescription',
label: 'API描述',
component: 'InputTextArea',
componentProps: {
placeholder: '请输入API描述',
rows: 4,
},
colProps: { lg: 24, md: 24 },
},
{
field: 'timeOut',
label: '超时时间',
component: 'Input',
defaultValue: '5',
rules: [
{
required: true,
message: '请输入超时时间',
},
],
suffix: '秒',
},
{
field: 'label',
label: 'Label',
component: 'Select',
rules: [
{
required: true,
message: '请选择Label',
},
],
defaultValue: '1',
componentProps: {
mode: 'multiple',
options:[
{
label:'用户',
value:'1'
},
{
label:'查询',
value:'2'
},
],
placeholder: '请选择Label',
},
},
{
field: 'type',
label: '类别',
component: 'Select',
rules: [
{
required: true,
message: '请选择类别',
},
],
defaultValue: '1',
componentProps: {
options:[
{
label:'API类别1',
value:'1'
},
{
label:'API类别2',
value:'2'
},
{
label:'API类别3',
value:'3'
},
],
placeholder: '请选择类别',
},
},
];
/**编辑列表*/
export const guideModeTableColumns1: BasicColumn[] = [
{
title: '列名',
dataIndex: 'column',
edit: true,
editable: true,
editComponent: 'Select',
editComponentProps: {
options: [
{
label: 'id',
value: '1',
},
{
label: '列名2',
value: '2',
},
],
},
width: 200,
},
{
title: '参数code',
dataIndex: 'parameterCode',
width: 200,
edit: true,
editable: true,
},
{
title: '参数类型',
dataIndex: 'parameterType',
width: 150,
},
{
title: '注释',
dataIndex: 'comment',
width: 200,
edit: true,
editable: true,
},
{
title: '是否必填',
dataIndex: 'required',
edit: true,
editable: true,
editComponent: 'Checkbox',
editValueMap: (value) => {
return value ? '是' : '否';
},
width: 200,
},
{
title: '默认值',
dataIndex: 'defaultValue',
width: 150
},
{
title: '示例值',
dataIndex: 'exampleValue',
width: 200,
edit: true,
editable: true,
},
];
import {getAllRoleList} from '@/api/system/role/role';
import { BasicColumn, FormSchema } from '@/components/Table';
import {h} from "vue";
import {Tag} from "ant-design-vue";
import { Switch } from 'ant-design-vue';
import {useMessage} from "@/hooks/web/useMessage";
import {changeFlagApi} from "@/api/system/user/user";
import {relatedQualityColumns} from "@/views/dataStandards/basicStandards/basicStandards.data"; // 引入开关组件
type CheckedType = boolean | string | number;
/**主页面树/列表 数据*/
export const TreeData: any[] = [
{
businessId: 100,
treeName: '全选',
anotherName: '全选',
parentId: 0,
},
{
businessId: 201,
treeName: '共享工作区',
anotherName: '共享工作区',
parentId: 100,
},
{
businessId: 202,
treeName: '商城工作区',
anotherName: '商城工作区',
parentId: 100,
},
{
businessId: 203,
treeName: 'admin个人工作区',
anotherName: 'admin个人工作区',
parentId: 100,
},
];
/**主页面列表 数据*/
export const tableData: any[] =[
{
businessId:'1',
taskName: 'wirp-322',
startTime: '2024-01-01 08:00:00',
source: 'KUNDB',
target: 'KUNDB',
owner: 'admin',
taskVersion: '5.0',
taskStatus: '运行中',
latency: '0.00s',
dataCheckDifference24h: '是',
latestEventTime: '2024-01-01 08:10:00',
unhandledExceptions: '37439',
ddl: '0'
},
{
businessId:'2',
taskName: 'WARP-108477',
startTime: '2024-01-01 08:00:00',
source: 'KUNDB',
target: 'KUNDB',
owner: 'admin',
taskVersion: '4.0',
taskStatus: '运行中',
latency: '0.00s',
dataCheckDifference24h: '是',
latestEventTime: '2024-01-01 08:10:00',
unhandledExceptions: '37439',
ddl: '0'
},
{
businessId:'3',
taskName: 'test8988',
startTime: '2024-01-01 08:00:00',
source: 'KUNDB',
target: 'KUNDB',
owner: 'admin',
taskVersion: '2.0',
taskStatus: '运行中',
latency: '0.00s',
dataCheckDifference24h: '是',
latestEventTime: '2024-01-01 08:10:00',
unhandledExceptions: '37439',
ddl: '0'
},
{
businessId:'4',
taskName: 'test start slowly',
startTime: '2024-01-01 08:00:00',
source: 'KUNDB',
target: 'KUNDB',
owner: 'admin',
taskVersion: '2.0',
taskStatus: '运行中',
latency: '0.00s',
dataCheckDifference24h: '是',
latestEventTime: '2024-01-01 08:10:00',
unhandledExceptions: '37439',
ddl: '0'
},
{
businessId:'5',
taskName: 'gd',
startTime: '2024-01-01 08:00:00',
source: 'KUNDB',
target: 'MYSQL',
owner: 'admin',
taskVersion: '1.0',
taskStatus: '运行中',
latency: '0.00s',
dataCheckDifference24h: '是',
latestEventTime: '2024-01-01 08:10:00',
unhandledExceptions: '37439',
ddl: '0'
},
{
businessId:'6',
taskName: 'dffhjg',
startTime: '2024-01-01 08:00:00',
source: 'KUNDB',
target: 'MYSQL',
owner: 'admin',
taskVersion: '2.0',
taskStatus: '运行中',
latency: '0.00s',
dataCheckDifference24h: '是',
latestEventTime: '2024-01-01 08:10:00',
unhandledExceptions: '37439',
ddl: '0'
},
{
businessId:'7',
taskName: 'pc-8712',
startTime: '2024-01-01 08:00:00',
source: 'KUNDB',
target: 'MYSQL',
owner: 'admin',
taskVersion: '1.0',
taskStatus: '运行中',
latency: '0.00s',
dataCheckDifference24h: '是',
latestEventTime: '2024-01-01 08:10:00',
unhandledExceptions: '37439',
ddl: '0'
},
{
businessId:'8',
taskName: '忽略通知',
startTime: '2024-01-01 08:00:00',
source: 'MYSQL',
target: 'MYSQL',
owner: 'admin',
taskVersion: '2.0',
taskStatus: '运行中',
latency: '0.00s',
dataCheckDifference24h: '是',
latestEventTime: '2024-01-01 08:10:00',
unhandledExceptions: '37439',
ddl: '0'
},
{
businessId:'9',
taskName: 'pg2',
startTime: '2024-01-01 08:00:00',
source: 'POSTGRE_SQL',
target: 'MYSQL',
owner: 'admin',
taskVersion: '1.0',
taskStatus: '运行中',
latency: '0.00s',
dataCheckDifference24h: '是',
latestEventTime: '2024-01-01 08:10:00',
unhandledExceptions: '37439',
ddl: '0'
},
]
<template>
<div class="m-4 mr-0 overflow-hidden bg-white">
<BasicTree
ref="treeRef"
treeWrapperClassName="h-[calc(100%-35px)] overflow-auto"
:clickRowToExpand="false"
:defaultExpandAll="true"
:treeData="treeData"
:fieldNames="{ key: 'businessId', title: 'treeName' }"
@select="handleSelect"
/>
</div>
</template>
<script lang="ts" setup>
import {h, nextTick, onMounted, ref, unref} from 'vue';
import {BasicTree, ContextMenuItem, TreeActionType, TreeItem} from '@/components/Tree';
import {Nullable} from "packages/types/src/index";
import { TreeData } from "./maintenanceData";
import {EventDataNode} from "ant-design-vue/es/vc-tree/interface";
import {PlusOutlined,EllipsisOutlined} from "@ant-design/icons-vue";
import {useMessage} from "@/hooks/web/useMessage";
import {Modal} from "ant-design-vue";
import {useModal} from "@/components/Modal";
import {router} from "@/router";
defineOptions({ name: 'DeptTree' });
const emit = defineEmits(['select']);
const treeData = ref<TreeItem[]>([]);
const treeRef = ref<Nullable<TreeActionType>>(null);
const { createMessage, createConfirm } = useMessage();
const [registerMoveModal, { openModal: openMoveModal }] = useModal();
const [registerCopyModal, { openModal: openCopyModal }] = useModal();
const [registerCreatePublicCodeModal, { openModal: openCreatePublicCodeModal }] = useModal();
const [registerCreateStandardsTypeModal, { openModal: openCreateStandardsTypeModal }] = useModal();
function getTree() {
const tree = unref(treeRef);
if (!tree) {
throw new Error('tree is null!');
}
return tree;
}
async function fetch() {
const data = TreeData
treeData.value = handleTree(data, 'businessId',undefined,undefined,undefined)
await nextTick(() => {
getTree().expandAll(true)
})
}
/** 转成树 */
function handleTree(data, id, parentId, children, rootId) {
id = id || 'id'
parentId = parentId || 'parentId'
children = children || 'children'
rootId = rootId || Math.min.apply(Math, data.map(item => { return item[parentId] })) || 0
// 对源数据深度克隆
const cloneData = JSON.parse(JSON.stringify(data))
// 循环所有项
const treeData = cloneData.filter(father => {
const branchArr = cloneData.filter(child => {
// 返回每一项的子级数组
return father[id] === child[parentId]
})
branchArr.length > 0 ? father.children = branchArr : ''
// 返回第一层
return father[parentId] === rootId
})
return treeData !== '' ? treeData : data
}
/**选中的数据*/
function handleSelect(keys) {
emit('select', keys[0]);
}
onMounted(() => {
fetch();
});
// 树的操作列表
const actionList = [
{
//全部操作按钮
render: (node) => {
return h(EllipsisOutlined, {
class: 'ml-2',
onClick: () => {
getRightMenuList(node)
},
});
},
},
];
function getRightMenuList(node: EventDataNode): Promise<ContextMenuItem[]> {
const menu = [
{
label: '查看详情',
handler: () => {
detailButton(node)
},
icon: 'ant-design:file-search-outlined',
},
{
label: '编辑',
handler: () => {
editButton(node)
},
icon: 'ant-design:edit-outlined',
},
{
label: '标准分类信息',
handler: () => {
standardsTypeButton(node)
},
icon: 'ant-design:file-text-twotone',
},
{
label: '新建标准分类',
handler: () => {
createStandardsTypeButton(node)
},
icon: 'ant-design:file-add-outlined',
},
{
label: '新建公共代码',
handler: () => {
createPublicCodeButton(node)
},
icon: 'ant-design:file-add-outlined',
},
{
label: '复制到',
handler: () => {
copyButton()
},
icon: 'ant-design:snippets-twotone',
},
{
label: '删除',
handler: () => {
deleteButton(node)
},
icon: 'ant-design:rest-outlined',
},
{
label: '移动',
handler: () => {
MoveButton()
},
icon: 'ant-design:folder-open-outlined',
},
];
return new Promise((resolve) => {
resolve(menu);
});
}
/**查看详情*/
function detailButton(record) {
router.push({
path: '/dataStandards/publicCode/detailPublicCode',
query: {
businessId:record.businessId,
},
});
}
/**移动*/
function MoveButton() {
openMoveModal(true, {
});
}
/**新建标准分类*/
function createStandardsTypeButton(record) {
openCreateStandardsTypeModal(true, {
record
});
}
/**新建公共代码*/
function createPublicCodeButton(record) {
openCreatePublicCodeModal(true, {
record
});
}
/**复制到 按钮*/
function copyButton() {
openCopyModal(true, {
});
}
/**删除 按钮*/
function deleteButton(record: Recordable) {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '确认批量删除选中数据吗?',
onOk() {
createMessage.success('删除成功!');
},
});
}
/** 修改 按钮*/
function handleUpdateButton(record: Recordable) {
}
/**编辑标准*/
function editButton(record) {
router.push({
path: '/dataStandards/publicCode/editDetailStandard',
query: {
businessId:record.businessId,
},
});
}
/**标准分类信息*/
function standardsTypeButton(record: Recordable) {
createMessage.success('标准分类信息')
}
/** 成功回调*/
function handleSuccess() {
}
</script>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment