Commit 324b045b by ningjihai

加密校验

parent 1bb0d687
import request from '@/utils/request'
/**
* 发现梳理列表
* @param {*} query
* @returns
*/
export function getGlobalOriginalConfig(data) {
return request({
url: '/console/user/getGlobalOriginalConfig',
method: 'post',
data: data
})
}
export function queryDatasystemInfo(data) {
return request({
url: '/core/digestmain/queryDatasystemInfo',
method: 'post',
data: data
})
}
export function querytableInfo(data) {
return request({
url: '/core/digestmain/querytableInfo',
method: 'post',
data: data
})
}
export function getfield(data) {
return request({
url: '/core/digestmain/getfield',
method: 'post',
data: data
})
}
export function save(data) {
return request({
url: '/core/digestmain/save',
method: 'post',
data: data
})
}
export function generatedigest(data) {
return request({
url: '/core/digestmain/generatedigest',
method: 'post',
data: data
})
}
export function checkdigest(data) {
return request({
url: '/core/digestmain/checkdigest',
method: 'post',
data: data
})
}
// export function initEdit(taskid) {
// return request({
// url: '/core/tdatadiscovery/initEdit?taskid=' + taskid,
// method: 'get'
// })
// }
export function deletedigest(data) {
return request({
url: '/core/digestmain/deletedigest',
method: 'post',
data: data
})
}
// export function executetask(data) {
// return request({
// url: '/core/discoverytask/executetask',
// method: 'post',
// data: data
// })
// }
// export function tdataMonitorQuery(query) {
// return request({
// url: '/core/tdataMonitor/query',
// method: 'get',
// params: query
// })
// }
// export function getdiscoverresultreport(query) {
// return request({
// url: '/core/tdiscoverresult/getdiscoverresultreport',
// method: 'get',
// params: query
// })
// }
// export function getarearule(query) {
// return request({
// url: '/core/tdiscoverresult/getarearule',
// method: 'get',
// params: query
// })
// }
// export function initExecute(query) {
// return request({
// url: '/core/tdatadiscovery/initExecute',
// method: 'get',
// params: query
// })
// }
// export function tdiscoverresultQuery(data) {
// return request({
// url: '/core/tdiscoverresult/query',
// method: 'post',
// data: data
// })
// }
// export function discoverresultsure(data) {
// return request({
// url: '/core/tdiscoverresult/discoverresultsure',
// method: 'post',
// data: data
// })
// }
// export function discoverchangerule(data) {
// return request({
// url: '/core/tdiscoverresult/discoverchangerule',
// method: 'post',
// data: data
// })
// }
// export function showmatchrate(data) {
// return request({
// url: '/core/tdiscoverresult/showmatchrate',
// method: 'post',
// data: data
// })
// }
// export function saveedition(data) {
// return request({
// url: '/core/tdiscoverresult/saveedition',
// method: 'post',
// data: data
// })
// }
// export function download(query) {
// return request({
// url: '/core/tdiscoverresult/download',
// method: 'get',
// params: query,
// responseType: 'blob' //
// })
// }
<template>
<div class="tree-filter-container">
<!-- 搜索框 -->
<div class="search-box">
<el-input
v-model="filterText"
placeholder="输入关键字过滤"
clearable
prefix-icon="el-icon-search"
/>
</div>
<!-- 树形结构 -->
<el-tree
ref="treeRef"
class="filter-tree"
:data="treeData"
:props="defaultProps"
:filter-node-method="filterNode"
:expand-on-click-node="false"
node-key="id"
highlight-current
@node-click="handleNodeClick"
>
<template #default="{ node, data }">
<span class="custom-tree-node">
<i class="iconfont node-icon" v-html="data.icon.text" :style="`color: ${data.icon.color};`"></i>
<span>{{ node.label }}</span>
<template v-if="data.type === 'category'">
<i class="el-icon-collection icon-category" style="margin-left:6px;color:#F7BA2A;font-size:16px;"></i>
</template>
<template v-if="data.type === 'system'">
<i class="el-icon-s-platform icon-system" style="margin-left:6px;color:#409EFF;font-size:16px;"></i>
</template>
<template v-if="data.type === 'database'">
<i class="el-icon-s-data icon-database" style="margin-left:6px;color:#67C23A;font-size:16px;"></i>
</template>
<template v-if="data.type === 'table'">
<i class="el-icon-s-grid icon-table" style="margin-left:6px;color:#E6A23C;font-size:16px;"></i>
</template>
</span>
</template>
</el-tree>
</div>
</template>
<script setup>
import { ref, watch } from 'vue'
const emit = defineEmits(['node-click'])
const props = defineProps({
treeData: {
type: Array,
default: () => [
{
id: 'system',
label: '若依配测系统',
type: 'system',
children: [
{
id: 'database',
label: 'ry',
type: 'database',
children: [
{
id: 'tables',
label: '表',
type: 'category',
children: [
{ id: 'gen_table', label: 'gen_table', type: 'table' },
{ id: 'gen_table_column', label: 'gen_table_column', type: 'table' },
{ id: 'sys_config', label: 'sys_config', type: 'table' },
{ id: 'sys_dept', label: 'sys_dept', type: 'table' },
{ id: 'sys_dict_data', label: 'sys_dict_data', type: 'table' },
{ id: 'sys_dict_type', label: 'sys_dict_type', type: 'table' },
{ id: 'sys_job', label: 'sys_job', type: 'table' },
{ id: 'sys_job_log', label: 'sys_job_log', type: 'table' },
{ id: 'sys_logininfor', label: 'sys_logininfor', type: 'table' },
{ id: 'sys_menu', label: 'sys_menu', type: 'table' },
{ id: 'sys_notice', label: 'sys_notice', type: 'table' },
{ id: 'sys_oper_log', label: 'sys_oper_log', type: 'table' },
{ id: 'sys_post', label: 'sys_post', type: 'table' },
{ id: 'sys_role', label: 'sys_role', type: 'table' },
{ id: 'sys_role_dept', label: 'sys_role_dept', type: 'table' },
{ id: 'sys_role_menu', label: 'sys_role_menu', type: 'table' },
{ id: 'sys_user', label: 'sys_user', type: 'table' },
{ id: 'sys_user_online', label: 'sys_user_online', type: 'table' },
{ id: 'sys_user_post', label: 'sys_user_post', type: 'table' },
{ id: 'sys_user_role', label: 'sys_user_role', type: 'table' }
]
}
]
}
]
}
]
}
})
const filterText = ref('')
const treeRef = ref(null)
const defaultProps = {
children: 'children',
label: 'label'
}
// 根据节点类型获取图标
const getNodeIcon = (type) => {
const iconMap = {
system: 'el-icon-s-platform',
database: 'el-icon-s-data',
category: 'el-icon-folder-opened',
table: 'el-icon-s-grid'
}
return iconMap[type] || 'el-icon-document'
}
// 过滤树节点
const filterNode = (value, data) => {
if (!value) return true
return data.label.toLowerCase().includes(value.toLowerCase())
}
// 监听过滤文本变化
watch(filterText, (val) => {
treeRef.value.filter(val)
})
// 节点点击事件
const handleNodeClick = (data) => {
emit('node-click', data)
}
</script>
<style scoped>
.tree-filter-container {
/* max-height: 700px; */
overflow-y: auto;
width: 100%;
height: 100%;
/* background-color: #f5f5f5; */
padding: 10px;
border-right: 1px solid #e6e6e6;
}
.search-box {
margin-bottom: 10px;
}
.filter-tree {
background-color: transparent;
}
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
font-size: 14px;
}
.node-icon {
margin-right: 6px;
color: #606266;
}
.icon-category {
vertical-align: middle;
}
.icon-system {
vertical-align: middle;
}
.icon-database {
vertical-align: middle;
}
.icon-table {
vertical-align: middle;
}
:deep(.el-tree-node__content) {
height: 36px;
}
</style>
\ No newline at end of file
<script setup name="ProjectManageList">
import { getCurrentInstance, reactive, ref, watch, onMounted, computed,nextTick} from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useRouter } from 'vue-router'
import useAppStore from '@/store/modules/app'
import { changeRoute } from '@/utils/switchRoute'
import TreeFilter from './TreeFilter.vue'
import {
queryShemas
} from '@/api/desensitizationStrategy'
import { queryTables, query } from '@/api/classification/classification.js'
import {
getGlobalOriginalConfig,
queryDatasystemInfo,
querytableInfo,
getfield,
save,
generatedigest,
checkdigest,
deletedigest
} from '@/api/verification/index.js'
const appStore = useAppStore()
const router = useRouter()
const emit = defineEmits(['page'])
const { proxy } = getCurrentInstance()
const strategyDialogRef = ref()
const currentNodeData = ref(null)
const currentNodeLevel = computed(() => {
if (!currentNodeData.value) return 1
if (currentNodeData.value.type === 'system') return 1
if (currentNodeData.value.type === 'database') return 2
if (currentNodeData.value.type === 'category') return 3
if (currentNodeData.value.type === 'table') return 4
return 0
})
// 标签页相关
const activeTab = ref('basicInfo')
const tableData = ref([])
const basicInfoData = ref({})
const threadIsoriginal = ref('')
const initStatus = () => {
getGlobalOriginalConfig({ projectId: sessionStorage.getItem('projectId')}).then( res => {
console.log(res.data)
threadIsoriginal.value = res.data.toString()
})
}
onMounted(async () => {
initStatus()
tableData.value = []
basicInfoData.value = {}
const res = await query({ project_id: sessionStorage.getItem('projectId') })
if (res && res.data) {
treeData.value = res.data.map(item => ({
...item,
label: item.sysname,
type: 'system',
icon: getNodeIcon(item.dbtype),
children: []
}))
// 赋值触发监听,默认查询第一个数据源的基本信息
// currentNodeData.value = treeData.value[0]
}
})
const backProject = () => {
changeRoute()
}
const getNodeIcon = (type) => {
return appStore.DATABASE_ICONS[type]
}
// 树形数据
const treeData = ref([])
// 生成唯一标识符
function uuid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
// 树节点点击处理
const handleNodeClick = async (data) => {
currentNodeData.value = data
// 重置标签页状态
activeTab.value = 'basicInfo'
tableData.value = []
basicInfoData.value = {}
// 点击一层系统数据,查 schemas
if (data.type === 'system') {
const res = await queryShemas({
dataSystemId: data.tid,
dataType: data.dbtype,
projectid: sessionStorage.getItem('projectId') || ''
})
if (res && res.data) {
data.children = res.data.map(schema => ({
id: uuid(),
dataSystemId: data.tid,
dbtype: data.dbtype,
projectid: sessionStorage.getItem('projectId') || '',
schema: schema.text,
type: 'database',
label: schema.text,
parent: data,
icon: getNodeIcon('DATABASE'),
children: []
}))
treeData.value = [...treeData.value]
}
}
// 点击二层数据库,构造一层
if (data.type === 'database') {
data.children = [
{
id: uuid(),
label: '表',
type: 'category',
dataSystemId: data.dataSystemId,
dbtype: data.dbtype,
projectid: data.projectid,
schema: data.schema,
parent: data,
icon: getNodeIcon('queryTables'),
children: []
}
]
}
// 点击三层 category,查表
if (data.type === 'category') {
const res = await queryTables({
dataSystemId: data.dataSystemId,
schema: data.schema
})
if (res && res.data) {
data.children = res.data.map(table => ({
dataSystemId: data.dataSystemId,
dbtype: data.dbtype,
projectid: data.projectid,
schema: data.schema,
realName: table.realName,
showName: table.showName,
id: uuid(),
label: table.showName,
type: 'table',
icon: getNodeIcon('TABLE'),
parent: data
}))
treeData.value = [...treeData.value]
}
}
}
// 标签页切换处理
const handleTabChange = (tabName) => {
if (tabName === 'dataStructure' && currentNodeLevel.value === 4) {
// 切换到数据结构标签页时,加载表结构数据
loadTableStructureData()
}
}
const status = ref('')
const viewFiledList = ref('')
// 加载表结构数据
const loadTableStructureData = async () => {
if (!currentNodeData.value) return
try {
// 这里需要根据实际API调整
const res = await getfield({
taskid: '',
schema: currentNodeData.value.parent.parent.label,
tablename: currentNodeData.value.label,
datasystemid: currentNodeData.value.parent.parent.dataSystemId,
projectid: currentNodeData.value.project_id
})
if (res && res.data) {
let type = currentNodeData.value.parent.parent.dbtype
let arr = []
status.value = res.data.flag
tableData.value = res.data.list.map(item => {
if(item.isDigest === '1') {
item._checked = true
arr.push(item.columnname)
}
if(item.typename === 'VARCHAR' || item.typename === 'CHAR'|| item.typename === 'NVARCHAR' || item.typename === 'VARCHAR2') {
}else {
item._disabled = true
}
if (type === 'MONGODB') {
item._disabled = false
}
if(type === 'ES' && item.typename === 'TEXT') {
item._disabled = false
}
if(type === 'ORACLE' && item.typename === 'VARCHAR2') {
item._disabled = false
}
if(status.value === '1') {
item._disabled = true
}
return item
})
viewFiledList.value = arr.join('#')
console.log('tableData.value',tableData.value)
// tableData.value = [
// {
// fieldName: 'job_id',
// fieldType: 'BIGINT',
// length: 19,
// precision: 0,
// comment: '任务ID',
// constraint: 'PK'
// }]
}
} catch (error) {
console.error('加载表结构数据失败:', error)
ElMessage.error('加载表结构数据失败')
}
}
const selectable = (row, index) => {
// 如果 row._disabled 为 true,则不可选择
return !row._disabled
}
// 监听 currentNodeLevel 和 currentNodeData,自动加载基本信息
watch([currentNodeLevel, currentNodeData], async ([level, node]) => {
if (!node) return
console.log('node',node)
// 重置数据
tableData.value = []
basicInfoData.value = {}
if (level === 1) {
// 加载系统基本信息
try {
const res = await queryDatasystemInfo({datasystemid: node.tid, projectid: node.project_id})
if (res && res.data) {
basicInfoData.value = res.data
}
} catch (error) {
console.error('加载系统信息失败:', error)
}
} else if (level === 4) {
console.log('第四层',node)
// return
// 加载表基本信息
try {
const res = await querytableInfo({
schema: node.parent.parent.label,
table: node.label,
datasystemid: node.parent.parent.dataSystemId,
projectid: node.projectid
})
if (res && res.data) {
basicInfoData.value = res.data
}
} catch (error) {
console.error('加载表信息失败:', error)
}
}
})
// 表格引用
const tableRef = ref(null)
// 监听 tableData 变化,设置默认选中
watch(tableData, (newVal) => {
nextTick(() => {
if (newVal && newVal.length > 0 && tableRef.value) {
// 筛选出需要选中的行
const selectedRows = newVal.filter(row => row._checked === true)
// 设置选中
selectedRows.forEach(row => {
tableRef.value.toggleRowSelection(row, true)
})
}
})
}, { deep: true })
const handleSelectionChange = (array) => {
console.log('选中的行数据', array)
let list = []
array.forEach(element => {
console.log('element.columnname',element.columnname)
list.push(element.columnname)
});
console.log('list',list)
viewFiledList.value = list.join('#')
console.log('viewFiledList.value',viewFiledList.value)
}
// 按钮点击事件处理
const handleSave = () => {
save({
projectid: currentNodeData.value.projectid,
datasystemid: currentNodeData.value.parent.parent.dataSystemId,
table: currentNodeData.value.label,
schema: currentNodeData.value.parent.parent.label,
digestfield: viewFiledList.value
}).then( res => {
if(res.flag){
ElMessage.success('保存成功')
loadTableStructureData()
}else{
ElMessage.error(res.msg)
}
}).catch(err => {
console.log('保存失败',err)
})
}
const generateSummary = () => {
generatedigest({
schema: currentNodeData.value.parent.parent.label,
table: currentNodeData.value.label,
datasystemid: currentNodeData.value.parent.parent.dataSystemId,
projectid: currentNodeData.value.projectid
}).then(res => {
if(res.flag){
loadTableStructureData()
nextTick(() => {
// 在这处理复选框 和 其他状态改变操作
tableData.value = tableData.value.map( item => {
return {
...item,
_disabled : true
}
})
})
ElMessage.success('生成摘要成功!')
}else{
ElMessage.error(res.msg)
}
})
}
const checkDataIntegrity = () => {
checkdigest({
projectid: currentNodeData.value.projectid,
datasystemid: currentNodeData.value.parent.parent.dataSystemId,
table: currentNodeData.value.label,
schema: currentNodeData.value.parent.parent.label,
}).then( res => {
if(res.flag){
ElMessage.success('校验成功!')
}else{
ElMessage.error(res.msg)
}
})
}
const resetSummary = () => {
deletedigest({
projectid: currentNodeData.value.projectid,
datasystemid: currentNodeData.value.parent.parent.dataSystemId,
table: currentNodeData.value.label,
schema: currentNodeData.value.parent.parent.label,
}).then( res => {
if(res.flag){
loadTableStructureData()
ElMessage.success('重置成功!')
}else{
ElMessage.error(res.msg)
}
})
}
</script>
<template>
<div class="app-container scroller">
<PageTitle :back="true" @back="backProject">
<template #title>
返回项目管理
</template>
<template #buttons>
<!-- 操作按钮可以根据需要添加 -->
</template>
</PageTitle>
<div class="app-container__body">
<div class="layout-container">
<!-- 左侧树形结构 -->
<div class="left-panel">
<TreeFilter :tree-data="treeData" @node-click="handleNodeClick"/>
</div>
<!-- 右侧内容区域 -->
<div class="right-panel">
<el-card class="content-card">
<!-- 标签页 -->
<el-tabs v-model="activeTab" @tab-change="handleTabChange">
<el-tab-pane label="基本信息" name="basicInfo" style="height: 100%;">
<div class="tab-content">
<el-descriptions
v-if="Object.keys(basicInfoData).length > 0"
:column="2"
border
:title="currentNodeLevel === 4 ? currentNodeData.showName : currentNodeData.label"
>
<template v-if="currentNodeLevel === 1 || currentNodeLevel === 2 || currentNodeLevel === 3">
<el-descriptions-item label="项目名称">{{ basicInfoData.projectName }}</el-descriptions-item>
<el-descriptions-item label="数据源名称">{{ basicInfoData.datasystemName }}</el-descriptions-item>
<el-descriptions-item label="数据源类型">{{ basicInfoData.dbType }}</el-descriptions-item>
<el-descriptions-item label="所属IP">{{ basicInfoData.ip }}</el-descriptions-item>
<el-descriptions-item label="管理的SCHEMA">{{ basicInfoData.schema }}</el-descriptions-item>
<el-descriptions-item label="校验表数量">{{ basicInfoData.digesttablecount }}</el-descriptions-item>
<el-descriptions-item label="未校验表数量">{{ basicInfoData.undigesttablecount }}</el-descriptions-item>
<el-descriptions-item label="生成校验列字段数量">{{ basicInfoData.digestcolumncount }}</el-descriptions-item>
</template>
<template v-else-if="currentNodeLevel === 4">
<el-descriptions-item label="字段总数">{{ basicInfoData.totalColumnCount }}</el-descriptions-item>
<el-descriptions-item label="校验字段数量">{{ basicInfoData.digestColumnCount }}</el-descriptions-item>
<el-descriptions-item label="最后操作人">{{ basicInfoData.operator }}</el-descriptions-item>
<el-descriptions-item label="最后操作时间">{{ basicInfoData.operationTime }}</el-descriptions-item>
</template>
</el-descriptions>
<el-empty v-else description="暂无数据" />
</div>
</el-tab-pane>
<!-- 在第二个标签页的内容区域 -->
<el-tab-pane label="数据结构" name="dataStructure" style="height: 100%;" :disabled="currentNodeLevel !== 4">
<div class="tab-content tab-table-content">
<!-- 操作按钮区域 -->
<div class="table-operations">
<el-button type="primary" @click="handleSave">
保存
</el-button>
<el-button type="success" @click="generateSummary">
生成摘要
</el-button>
<el-button type="default" @click="checkDataIntegrity">
数据完整性校验
</el-button>
<el-button type="primary" @click="resetSummary">
重置摘要
</el-button>
</div>
<!-- 表格区域 - 纯展示模式 -->
<div class="table-container" v-if="tableData.length > 0">
<el-table
ref="tableRef"
:data="tableData"
height="100%"
style="width: 100%"
:header-cell-style="{ background: '#f0f7ff', color: '#606266', fontWeight: '600' }"
stripe
border
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" :selectable="selectable" width="55" />
<!-- 约束列 -->
<el-table-column
prop="primarykey"
label="约束"
min-width="120"
align="center"
>
<template #default="{ row }">
<span v-if="row.primarykey === '1'" class="constraint-pk">PK</span>
</template>
</el-table-column>
<!-- 字段名列 -->
<el-table-column
prop="columnname"
label="字段名"
min-width="120"
show-overflow-tooltip
/>
<!-- 类型列 -->
<el-table-column
prop="typename"
label="类型"
min-width="120"
align="center"
/>
<!-- 长度列 -->
<el-table-column
prop="columnsize"
label="长度"
min-width="120"
align="center"
>
</el-table-column>
<!-- 精度列 -->
<el-table-column
prop="decimaldigits"
label="精度"
min-width="120"
align="center"
>
</el-table-column>
<!-- 注释列 -->
<el-table-column
prop="remarks"
label="注释"
min-width="200"
show-overflow-tooltip
/>
</el-table>
</div>
<el-empty v-else description="暂无数据或请先选择表" />
</div>
</el-tab-pane>
</el-tabs>
</el-card>
</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.layout-container {
display: flex;
height: 100%;
gap: 20px;
}
.left-panel {
width: 300px;
min-width: 300px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.right-panel {
flex: 1;
min-width: 0;
}
.content-card {
height: 100%;
:deep(.el-card__body) {
height: 100%;
// padding: 0;
}
}
.tab-content {
padding: 20px;
height: 100%;
overflow-y: auto;
}
.tab-table-content {
display: flex;
flex-direction: column;
overflow: hidden;
height: 100%;
.table-container {
margin-top: 20px;
flex: 1;
height: 100%;
}
}
:deep(.el-tabs) {
height: 100%;
.el-tabs__content {
height: calc(100% - 55px);
// overflow-y: auto;
}
}
:deep(.el-descriptions) {
.el-descriptions__title {
font-size: 16px;
font-weight: 600;
color: #303133;
margin-bottom: 16px;
}
.el-descriptions__body {
background: #f0f7ff;
.el-descriptions__table {
tbody {
tr:hover {
background: #e6f7ff;
}
}
}
}
}
:deep(.el-table) {
border-radius: 4px;
.el-table__header-wrapper {
th {
background: #f0f7ff;
font-weight: 600;
}
}
.el-table__body-wrapper {
.el-table__row {
&:nth-child(even) {
background-color: #fafafa;
}
&:hover {
background-color: #f5f7fa;
}
}
}
}
// 响应式设计
@media (max-width: 768px) {
.layout-container {
flex-direction: column;
height: auto;
}
.left-panel {
width: 100%;
height: 300px;
}
.right-panel {
width: 100%;
}
}
</style>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论