Commit 2373db28 by ningjihai

发现梳理

parent 6bc00674
......@@ -69,44 +69,102 @@ export function initEdit(taskid) {
})
}
// export function getdatascopeprojectlist(data) {
// return request({
// url: '/core/datascopeproject/getdatascopeprojectlist',
// method: 'post',
// data: data
// })
// }
// export function checkDatasystemName(data) {
// return request({
// url: '/core/tdataproject/checkDatasystemName',
// method: 'post',
// data: data
// })
// }
export function delDiscoverTask(data) {
return request({
url: '/core/tdatadiscovery/del',
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' //
})
}
// export function checkDatasystem(data) {
// return request({
// url: '/core/tdataproject/checkDatasystem',
// method: 'post',
// data: data
// })
// }
// export function queryByEditSort(data) {
// return request({
// url: '/core/tdatasource/queryByEditSort',
// method: 'post',
// data: data
// })
// }
// export function selectProDataSource(data) {
// return request({
// url: '/core/tdatasource/selectProDataSource',
// method: 'post',
// data: data
// })
// }
// export function queryShemas(data) {
// return request({
......
<script setup name="DiscoverProcess">
import { getCurrentInstance, reactive, ref, toRefs } from 'vue'
import { getCurrentInstance, reactive, ref, toRefs, onMounted ,onBeforeUnmount} from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useRouter } from 'vue-router'
import useAppStore from '@/store/modules/app'
import usePermissionStore from '@/store/modules/permission'
import { changeRoute } from '@/utils/switchRoute'
import WaterWaveChart from './modules/WaterWaveChart.vue'
// import AddDiscoveryDialog from './modules/AddDiscoveryDialog.vue' // 引入新增发现任务弹窗
import useAppStore from '@/store/modules/app'
import {
tdataMonitorQuery
} from '@/api/discover'
const appStore = useAppStore()
const ajaxFlag = ref(true)
const permissionStore = usePermissionStore()
const router = useRouter()
const emit = defineEmits(['page'])
......@@ -22,78 +30,142 @@ const props = defineProps({
default: () => ({})
}
})
function pageProjectManage() {
changeRoute()
function pageList() {
emit('page','list')
// changeRoute()
router.push({
path: '/project/Project'
})
// router.push({
// path: '/project/Project'
// })
}
// 进度数据
const progress = ref(100) // 100% 进度
const completedCount = ref(20) // 已完成表数量
const progress = ref(0) // 100% 进度
const tableSum = ref(0) // 已完成表数量
const totalCount = ref(20) // 总表数量
// 执行失败数据
const failureData = ref([
{
category: '数据连接',
severity: '高',
executeTime: '2025-08-23 10:30'
},
{
category: '权限验证',
severity: '中',
executeTime: '2025-08-23 10:25'
},
{
category: '资源限制',
severity: '低',
executeTime: '2025-08-23 10:20'
}
// {
// category: '数据连接',
// severity: '高',
// executeTime: '2025-08-23 10:30'
// },
// {
// category: '权限验证',
// severity: '中',
// executeTime: '2025-08-23 10:25'
// },
// {
// category: '资源限制',
// severity: '低',
// executeTime: '2025-08-23 10:20'
// }
])
// 已完成数据
const completedData = ref([
{ discoveryNo: 1, tableName: 'gen_table', schema: 'ry', discoveryTime: '2025-08-23 04:15:23' },
{ discoveryNo: 2, tableName: 'gen_table_column', schema: 'ry', discoveryTime: '2025-08-23 04:15:24' },
{ discoveryNo: 3, tableName: 'sys_config', schema: 'ry', discoveryTime: '2025-08-23 04:15:25' },
{ discoveryNo: 4, tableName: 'sys_dept', schema: 'ry', discoveryTime: '2025-08-23 04:15:26' },
{ discoveryNo: 5, tableName: 'sys_dict_data', schema: 'ry', discoveryTime: '2025-08-23 04:15:27' },
{ discoveryNo: 6, tableName: 'sys_dict_type', schema: 'ry', discoveryTime: '2025-08-23 04:15:28' },
{ discoveryNo: 7, tableName: 'sys_job', schema: 'ry', discoveryTime: '2025-08-23 04:15:29' },
{ discoveryNo: 8, tableName: 'sys_job_log', schema: 'ry', discoveryTime: '2025-08-23 04:15:30' },
{ discoveryNo: 9, tableName: 'sys_logininfor', schema: 'ry', discoveryTime: '2025-08-23 04:15:31' },
{ discoveryNo: 10, tableName: 'sys_menu', schema: 'ry', discoveryTime: '2025-08-23 04:15:32' },
{ discoveryNo: 11, tableName: 'sys_notice', schema: 'ry', discoveryTime: '2025-08-23 04:15:33' },
{ discoveryNo: 12, tableName: 'sys_oper_log', schema: 'ry', discoveryTime: '2025-08-23 04:15:34' },
{ discoveryNo: 13, tableName: 'sys_post', schema: 'ry', discoveryTime: '2025-08-23 04:15:35' },
{ discoveryNo: 14, tableName: 'sys_role', schema: 'ry', discoveryTime: '2025-08-23 04:15:36' }
// { discoveryNo: 1, tableName: 'gen_table', schema: 'ry', discoveryTime: '2025-08-23 04:15:23' },
// { discoveryNo: 2, tableName: 'gen_table_column', schema: 'ry', discoveryTime: '2025-08-23 04:15:24' },
// { discoveryNo: 3, tableName: 'sys_config', schema: 'ry', discoveryTime: '2025-08-23 04:15:25' },
// { discoveryNo: 4, tableName: 'sys_dept', schema: 'ry', discoveryTime: '2025-08-23 04:15:26' },
// { discoveryNo: 5, tableName: 'sys_dict_data', schema: 'ry', discoveryTime: '2025-08-23 04:15:27' },
// { discoveryNo: 6, tableName: 'sys_dict_type', schema: 'ry', discoveryTime: '2025-08-23 04:15:28' },
// { discoveryNo: 7, tableName: 'sys_job', schema: 'ry', discoveryTime: '2025-08-23 04:15:29' },
// { discoveryNo: 8, tableName: 'sys_job_log', schema: 'ry', discoveryTime: '2025-08-23 04:15:30' },
// { discoveryNo: 9, tableName: 'sys_logininfor', schema: 'ry', discoveryTime: '2025-08-23 04:15:31' },
// { discoveryNo: 10, tableName: 'sys_menu', schema: 'ry', discoveryTime: '2025-08-23 04:15:32' },
// { discoveryNo: 11, tableName: 'sys_notice', schema: 'ry', discoveryTime: '2025-08-23 04:15:33' },
// { discoveryNo: 12, tableName: 'sys_oper_log', schema: 'ry', discoveryTime: '2025-08-23 04:15:34' },
// { discoveryNo: 13, tableName: 'sys_post', schema: 'ry', discoveryTime: '2025-08-23 04:15:35' },
// { discoveryNo: 14, tableName: 'sys_role', schema: 'ry', discoveryTime: '2025-08-23 04:15:36' }
])
// 严重性标签类型
const getSeverityType = (severity) => {
const types = {
'高': 'danger',
'中': 'warning',
'低': 'info'
}
return types[severity] || 'info'
}
// 查看结果处理
const handleCatResult = () => {
console.log('查看详细结果')
emit('page','discoverResult')
appStore.setQueryData({
taskid: appStore.queryData.taskid,
resultid: appStore.queryData.resultid,
datasystemid: appStore.queryData.datasystemid,
discoverTaskName: appStore.queryData.discoverTaskName,
icon: appStore.queryData.icon,
breadPathData: [
{
path: appStore.queryData.breadPathData[0].path,
label: appStore.queryData.breadPathData[0].label
},
{
label: "查看结果"
}
]
});
emit('page','discoverResult', { listItem: props.listItem})
}
onMounted(() =>{
getMonitorDataRequest()
})
const timeOutId = ref(null)
onBeforeUnmount(() =>{
if (timeOutId.value) {
clearTimeout(timeOutId.value);
ajaxFlag.value = false
}
})
const getMonitorDataRequest = () => {
tdataMonitorQuery({
taskid: appStore.queryData.taskid || props.listItem.id,
projectid: sessionStorage.getItem('projectId')
}).then(res =>{
const { statistics, errorlist, dbdatainfo } = res.data
failureData.value = errorlist.map(item =>(({
...item,
category:item.errorType,
severity: item.seriousness,
executeTime: item.excutionTime
})))
tableSum.value = statistics.tableSum
if(statistics.count === 0){
progress.value = 0
}else{
progress.value = ((statistics.tableSum / statistics.count) * 100).toFixed(2)
}
completedData.value = JSON.parse(dbdatainfo.message);
console.log('completedData.value',completedData.value)
if (ajaxFlag && (progress.value < 100)) {
timeOutId.value = setTimeout(() => {
getMonitorDataRequest();
}, 1000)
}
})
}
</script>
<template>
<div class="app-container scroller">
<PageTitle :back="true" @back="pageProjectManage">
<PageTitle :back="true" @back="pageList">
<template #title>
返回项目管理
发现进度
</template>
</PageTitle>
......@@ -110,7 +182,7 @@ const handleCatResult = () => {
<WaterWaveChart :usage="progress" color="#1890ff" />
</div>
<div>完成表数:{{ completedCount }}</div>
<div class="complete">完成表数:{{ tableSum }}</div>
</div>
<!-- 执行失败表格 -->
......@@ -125,14 +197,8 @@ const handleCatResult = () => {
empty-text="暂无数据"
:header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
>
<el-table-column prop="category" label="类别" min-width="120" />
<el-table-column prop="severity" label="严重性" min-width="100">
<template #default="{ row }">
<el-tag :type="getSeverityType(row.severity)" size="small">
{{ row.severity }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="category" label="类别" min-width="120" fixed="left" />
<el-table-column prop="severity" label="严重性" min-width="100" />
<el-table-column prop="executeTime" label="执行时间" min-width="140" />
</el-table>
</div>
......@@ -142,23 +208,26 @@ const handleCatResult = () => {
<div class="right-panel">
<div class="completed-table-section">
<div class="section-header">
<span class="title">已完成</span>
<span class="title">已完成 </span>
<el-button
v-show="progress > 100.00 || progress == 100.00"
type="primary"
@click="handleCatResult"
>查看结果</el-button>
</div>
<el-table
:data="completedData"
height="600px"
height="612px"
style="width: 100%"
:header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
empty-text="暂无数据"
>
<el-table-column prop="discoveryNo" label="发现序号" width="100" align="center" />
<el-table-column prop="tableName" label="发现表名" min-width="150" />
<el-table-column prop="schema" label="SCHEMA" width="100" />
<el-table-column prop="discoveryTime" label="发现时间" width="160" />
<el-table-column label="发现序号" width="100" align="center" fixed>
<template #default="scope">{{ scope.$index + 1 }}</template>
</el-table-column>
<el-table-column prop="dbType" label="发现表名" min-width="150" />
<el-table-column prop="dbName" label="SCHEMA" width="100" />
<el-table-column prop="createTime" label="发现时间" width="160" fixed="right"/>
</el-table>
</div>
</div>
......@@ -211,7 +280,9 @@ const handleCatResult = () => {
flex-direction: column;
gap: 20px;
}
.complete {
width: 100px;
}
.right-panel {
flex: 1;
min-width: 0;
......
<script setup name="DiscoverResult">
import { getCurrentInstance, reactive, ref, toRefs } from 'vue'
import { getCurrentInstance, reactive, ref, toRefs,onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useRouter } from 'vue-router'
import useAppStore from '@/store/modules/app'
import usePermissionStore from '@/store/modules/permission'
import { changeRoute } from '@/utils/switchRoute'
import { getToken, removeToken } from '@/utils/auth'
import DomainRuleDialog from './modules/DomainRuleDialog.vue'
import MatchPreviewDialog from './modules/MatchPreviewDialog.vue'
import SaveVersionDialog from './modules/SaveVersionDialog.vue'
import {
tdiscoverresultQuery,
getdiscoverresultreport,
getarearule,
initExecute,
discoverresultsure,
discoverchangerule,
showmatchrate,
saveedition,
download
} from '@/api/discover/index'
import { Loading } from '@element-plus/icons-vue'
import { version } from 'nprogress'
const appStore = useAppStore()
const permissionStore = usePermissionStore()
......@@ -22,46 +38,343 @@ const props = defineProps({
default: () => ({})
}
})
function pageProjectManage() {
changeRoute()
router.push({
path: '/project/Project'
const requestParams = ref({
taskid: '',
page: 1,
rows: 10,
resultid: '',
filter: {
tablename: '', // 结果表名(resultid)
nschema: '', // schema名称
ntable: '', // 表名
nfield: '', // 字段名
checksure: '2', // 是否确认(0:未确认 1:已确认 2:全部
issensitive: '0', // 显示模式(0:显示全部 1:显示敏感)
}
})
const requestParamsCopy= ref({
taskid: '',
page: 1,
rows: 10,
resultid: '',
filter: {
tablename: '', // 结果表名(resultid)
nschema: '', // schema名称
ntable: '', // 表名
nfield: '', // 字段名
checksure: '2', // 是否确认(0:未确认 1:已确认 2:全部
issensitive: '0', // 显示模式(0:显示全部 1:显示敏感)
}
})
onMounted(() => {
requestParams.value.taskid = appStore.queryData.taskid;
requestParams.value.resultid = appStore.queryData.resultid;
requestParams.value.filter.tablename = appStore.queryData.resultid;
requestParamsCopy.value = JSON.parse(JSON.stringify(requestParams.value));
saveVersionDialog.value.params.taskid = appStore.queryData.taskid;
saveVersionDialog.value.params.resultid = appStore.queryData.resultid;
getResultListRequest();
displayTableAndFields();
getDiscoverRuleListRequest();
getVersionHistoryListRequest();
})
const totalCount = ref(0);
const collapseDataList = ref([]);
const listLoading = ref(false);
/**
* 请求敏感类型列表
*/
const getResultListRequest = () => {
listLoading.value = true
tdiscoverresultQuery(requestParams.value).then(response => {
const { list, total } = response.data
list.map(item => {
item.children.map(child => {
child.icon = [
{
label: '匹配预览',
name: "matchingPercent",
color: "rgb(50,176,115)",
icon: '&#xe7d2;',
key: "actStatus"
}
];
if (child.dataarea_id) {
child.icon.push({
label: child.dataarea_name,
icon: "",
color: "rgb(83,144,213)",
name: "setDataarea",
key: "dataarea_name"
})
} else {
child.icon.push({
label: '设置',
icon: "&#xe658;",
color: "rgb(83,144,213)",
name: "setDataarea",
// fontStyle: {color: 'red'},
key: "dataarea_name"
})
}
if (child.correlation) {
child.icon.push({
label: '疑似敏感',
name: "maybeKey",
color: "rgb(250, 114, 86)",
icon: '&#xe6a2;',
key: "actStatus"
})
}
child.button = child.issure === '1' ? [
{
label: '已确认',
name: 'confirm',
type: 'success',
key: 'confirm'
}
] : [
{
label: '未确认',
name: 'unconfirmed',
type: 'default',
key: 'confirm'
},
];
child.discoverInfo = `${child.tsampling}/${child.teffe_sampling}/${child.tmatching}`
})
})
collapseDataList.value = list;
totalCount.value = total;
listLoading.value = false
})
}
const reportData = ref([])
const displayTableAndFields = () => {
getdiscoverresultreport({taskid: appStore.queryData.taskid}).then(response => {
const { tasktime, tablecount, fieldcount, sensitivetable, sensitivefield } = response.data
reportData.value = [
{
label: "任务运行时间",
content: tasktime,
color: "rgb(50,179,115)"
},
{
label: "任务总对象数(表/文件)",
content: tablecount,
color: "rgb(250,114,86)"
},
{
label: "发现敏感对象数(表/文件)",
content: String(sensitivetable),
color: "rgb(255,189,91)"
},
{
label: "任务总列数",
content: String(fieldcount),
color: "rgb(83,144,213)"
},
{
label: "发现敏感列数",
content: String(sensitivefield),
color: "rgb(157,90,178)"
}
];
})
}
const searchregexParams= ref({
dataarea: '',
rulename: ''
})
const dataAreaInfo = ref({
dataArea: '',
rule: '',
dataAreaList: [],
ruleList: []
})
const getDiscoverRuleListRequest = () => {
getarearule({
project_id: sessionStorage.getItem("projectId"),
rulename: searchregexParams.value.dataarea
}).then(response => {
let data = response.data;
console.log(data)
if(data && data.length > 0) {
const newData = data.map(item => {
return {
dataarea: {
dataarea: item.dataareaname,
id: item.dataarea_id,
// areatype: item.areatype,
// ceatetime: item.createtime,
// createuser: item.createuser,
// flag: item.flag,
// hasarea: item.hasarea,
// note: item.note,
// remark: item.remark,
// updatetime: item.updatetime,
// updateuser: item.updateuser
},
data: item.regexlist.length > 0 ? item.regexlist.map(rule => {
return {
createtime: rule.createTime,
createuser: rule.createUser,
dataarea: item.dataarea,
datafile: null,
defaulttype: String(rule.defaulttype),
dictionaryClass: '',
discoverway: '',
name: rule.name,
note: null,
ruleid: String(rule.id),
tid: item.tid
}
}) : []
}
})
dataAreaInfo.value.dataAreaList = newData;
console.log('123123',dataAreaInfo.value.dataAreaList)
}
})
}
const all_version_data = ref({})
const project_version_data = ref({})
const task_version_data = ref([])
const getVersionHistoryListRequest = () => {
initExecute({
taskid: appStore.queryData.taskid,
projectid: sessionStorage.getItem("projectId")
}).then(response => {
const { data } = response.data;
let { all_version, project_version, task_version } = data;
task_version.sort((a, b) => new Date(b.datetime).getTime() - new Date(a.datetime).getTime());
all_version_data.value = all_version
project_version_data.value = project_version
task_version_data.value = task_version
})
}
const tableColumns= ref([
{
title: "序号",
align: "center",
width: 70,
key: "index"
},
{
title: "字段",
align: "center",
key: "tfields",
ellipsis: true,
tooltip: true,
width: 150
},
{
title: "字段说明",
align: "center",
key: "tfieldremark",
tooltip: true,
ellipsis: true,
},
{
title: "采样数/有效采样数/匹配率",
align: "center",
key: "discoverInfo",
},
{
title: "数据域",
align: "center",
key: "dataarea_name",
slot: "action",
},
{
title: "数据发现规则",
align: "center",
key: "sensitive_name"
},
{
title: "梳理",
align: "center",
key: "confirm",
slot: "button"
},
{
title: "操作",
align: "center",
key: "actStatus",
width: 260,
slot: "action"
}
])
function pageProjectManage() {
emit('page', 'discoverProcess', { listItem: props.listItem})
}
// 折叠面板激活项
const activeNames = ref('gen_table')
// gen_table 数据
const genTableData = ref([
{ index: 1, field: 'business_name', fieldDesc: '', samplingInfo: '0/0/0%', dataDomain: '设置', discoveryRule: '', status: '未确认' },
{ index: 2, field: 'business_name...', fieldDesc: '', samplingInfo: '0/0/0%', dataDomain: '设置', discoveryRule: '', status: '未确认' },
{ index: 3, field: 'class_name', fieldDesc: '', samplingInfo: '0/0/0%', dataDomain: '设置', discoveryRule: '', status: '未确认' },
{ index: 4, field: 'create_by', fieldDesc: '', samplingInfo: '0/0/0%', dataDomain: '设置', discoveryRule: '', status: '未确认' },
{ index: 5, field: 'create_time', fieldDesc: '', samplingInfo: '0/0/0%', dataDomain: '设置', discoveryRule: '', status: '未确认' },
{ index: 6, field: 'function_author', fieldDesc: '', samplingInfo: '0/0/0%', dataDomain: '设置', discoveryRule: '', status: '未确认' }
])
// const genTableData = ref([
// { index: 1, field: 'business_name', fieldDesc: '', samplingInfo: '0/0/0%', dataDomain: '设置', discoveryRule: '', status: '未确认' },
// { index: 2, field: 'business_name...', fieldDesc: '', samplingInfo: '0/0/0%', dataDomain: '设置', discoveryRule: '', status: '未确认' },
// { index: 3, field: 'class_name', fieldDesc: '', samplingInfo: '0/0/0%', dataDomain: '设置', discoveryRule: '', status: '未确认' },
// { index: 4, field: 'create_by', fieldDesc: '', samplingInfo: '0/0/0%', dataDomain: '设置', discoveryRule: '', status: '未确认' },
// { index: 5, field: 'create_time', fieldDesc: '', samplingInfo: '0/0/0%', dataDomain: '设置', discoveryRule: '', status: '未确认' },
// { index: 6, field: 'function_author', fieldDesc: '', samplingInfo: '0/0/0%', dataDomain: '设置', discoveryRule: '', status: '未确认' }
// ])
// 其他表格数据
const allTableNames = ref([
{ name: 'gen_table', sensitiveCount: 1 },
{ name: 'ry.gen_table_column', sensitiveCount: 0 },
{ name: 'ry.sys_config', sensitiveCount: 0 },
{ name: 'ry.sys_dept', sensitiveCount: 1 },
{ name: 'ry.sys_dict_data', sensitiveCount: 0 },
{ name: 'ry.sys_dict_type', sensitiveCount: 0 },
{ name: 'ry.sys_job', sensitiveCount: 0 },
{ name: 'ry.sys_job_log', sensitiveCount: 0 },
{ name: 'ry.sys_logininfor', sensitiveCount: 0 },
{ name: 'ry.sys_menu', sensitiveCount: 0 }
])
// const allTableNames = ref([
// { name: 'gen_table', sensitiveCount: 1 },
// { name: 'ry.gen_table_column', sensitiveCount: 0 },
// { name: 'ry.sys_config', sensitiveCount: 0 },
// { name: 'ry.sys_dept', sensitiveCount: 1 },
// { name: 'ry.sys_dict_data', sensitiveCount: 0 },
// { name: 'ry.sys_dict_type', sensitiveCount: 0 },
// { name: 'ry.sys_job', sensitiveCount: 0 },
// { name: 'ry.sys_job_log', sensitiveCount: 0 },
// { name: 'ry.sys_logininfor', sensitiveCount: 0 },
// { name: 'ry.sys_menu', sensitiveCount: 0 }
// ])
const selectAll = ref(false)
/**
* 显示敏感字段切换
*/
const displaySensitive = ()=>{
console.log('显示敏感字段切换')
console.log('显示敏感字段切换',selectAll.value)
requestParams.value.filter.issensitive = selectAll.value === true ? '1' : '0'
getResultListRequest()
// console.log(requestParams.value.filter.issensitive)
}
/**
......@@ -70,24 +383,150 @@ const displaySensitive = ()=>{
const batchSettingRules = ()=>{
console.log('批量设置规则')
}
const confirmAllLoading = ref(false)
/**
* 确认所有
*/
const confirmAll = ()=>{
console.log('确认所有')
confirmAllLoading.value = true
discoverresultsure({
resultid: appStore.queryData.resultid,
filter: {
...requestParams.value.filter,
issure: '1'
}
}).then((res) => {
if(res.flag){
ElMessage.success(res.msg)
confirmAllLoading.value = false
getResultListRequest()
}else{
confirmAllLoading.value = false
ElMessage.error(res.msg)
}
})
}
const cancelAllLoading = ref(false)
/**
* 取消所有
*/
const cancelAll = ()=>{
console.log('取消所有')
ElMessageBox.confirm(
`确认取消所有么?`,
'警告',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}
).then(() => {
cancelAllLoading.value = true
discoverresultsure({
resultid: appStore.queryData.resultid,
filter: {
...requestParams.value.filter,
issure: '0'
}
}).then((res) => {
if(res.flag){
ElMessage.success(res.msg)
cancelAllLoading.value = false
getResultListRequest()
}else{
cancelAllLoading.value = false
ElMessage.error(res.msg)
}
})
})
}
const saveVersionDialog=ref({
status: false,
params: {
versionname: '',
taskid: '',
flag: '1',
resultid: '',
projectid: '',
},
dataList: []
})
/**
* 打开版本弹窗
*/
const openSaveDialog = () => {
saveVersionDialog.value.status = true
}
/**
* 保存
*/
const handleSave = ()=>{
console.log('保存')
// 处理保存
const handleSave = async (versionName) => {
if (!versionName.trim()) {
ElMessage.warning('请输入版本名称')
return
}
try {
const params = {
...saveVersionDialog.value.params,
versionname: versionName,
version: 'task_version',
projectid: sessionStorage.getItem('projectId')
}
// 检查版本名是否重复
const isDuplicate = task_version_data.value.some(
item => item.versionname === versionName.trim()
)
if (isDuplicate) {
await ElMessageBox.confirm('版本名重复,是否需要覆盖?', '提示', {
confirmButtonText: '确认覆盖',
cancelButtonText: '取消',
type: 'warning'
})
// 覆盖保存
await saveedition({
...params,
flag: '2'
})
} else {
// 新增保存
await saveedition({
...params,
flag: '0'
})
}
ElMessage.success('保存成功!')
saveVersionDialog.value.status = false
saveVersionDialog.value.params.versionname = ''
getVersionHistoryListRequest()
} catch (error) {
console.log(error)
if (error !== 'cancel') {
ElMessage.error('保存失败')
console.error('Save error:', error)
}
}
}
// 关闭弹窗时的处理
const handleSaveDialogClose = () => {
saveVersionDialog.value.params.versionname = ''
}
/**
* 后退
*/
......@@ -99,80 +538,246 @@ const handleChangeTableByCollapse = ()=>{
console.log('切换表格显示方式')
}
const domainRuleDialogRef = ref()
const domainRuleDialogVisible = ref(false)
const currentRowData = ref(null)
// 打开设置弹窗
const openSettingDialog = (row) => {
currentRowData.value = row
// 方法1:使用ref调用子组件方法
// domainRuleDialogRef.value?.openDialog()
// 方法2:或者直接控制visible
domainRuleDialogVisible.value = true
const confirmKey = (row) =>{
discoverresultsure({
resultid: appStore.queryData.resultid,
tableid: row.id,
filter: {
...requestParams.value.filter,
issure: row.issure === '0' ? '1' : '0'
}
}).then(() => {
getResultListRequest();
})
}
const setDataareaDialog= ref({
title: '',
status: false,
cacheData: {}
})
const setDataarea = (row) => {
console.log('row',row)
setDataareaDialog.value.status = true;
setDataareaDialog.value.cacheData = row;
if (searchregexParams.value.dataarea.trim().length > 0) {
searchregexParams.value.dataarea = '';
getDiscoverRuleListRequest();
}
if (row.dataarea_id) {
dataAreaInfo.value.dataArea = row.dataarea_id;
} else {
dataAreaInfo.value.dataArea = '';
}
if (row.sensitive_id) {
console.log('dataAreaInfo.value.dataAreaList',dataAreaInfo.value.dataAreaList)
dataAreaInfo.value.ruleList = dataAreaInfo.value.dataAreaList.filter(item => item.dataarea.id === row.dataarea_id)[0].data;
console.log(' dataAreaInfo.value.ruleList', dataAreaInfo.value.ruleList)
dataAreaInfo.value.rule = row.sensitive_id;
} else {
dataAreaInfo.value.rule = '';
dataAreaInfo.value.ruleList = []
console.log('123')
}
}
const selectRule = ref({
})
// 处理规则确认
const handleRuleConfirm = (ruleData) => {
console.log('选择的规则:', ruleData)
if (currentRowData.value) {
currentRowData.value.discoveryRule = ruleData.ruleLabel
currentRowData.value.dataDomain = ruleData.domainLabel
// 同时保存value值用于下次打开时回显
currentRowData.value.discoveryRuleValue = ruleData.rule
currentRowData.value.dataDomainValue = ruleData.domain
selectRule.value = ruleData
handlerSubmitRule()
}
const handlerSubmitRule = () => {
console.log('setDataareaDialog.value.cacheData',setDataareaDialog.value.cacheData)
discoverchangerule({
resultid: appStore.queryData.resultid,
tableid: setDataareaDialog.value.cacheData.id,
filter: {
...requestParams.value.filter,
nareaid: selectRule.value.domainValue,
nareaname:selectRule.value.domain.dataarea,
ntruleid: selectRule.value.ruleValue,
ntrulename: selectRule.value.rule
}
}).then(() => {
getResultListRequest();
setDataareaDialog.value.status = false;
})
}
const resetRule = () =>{
discoverchangerule({
resultid: appStore.queryData.resultid,
tableid: setDataareaDialog.value.cacheData.id,
filter: {
...requestParams.value.filter,
nareaid: '',
nareaname: '',
ntruleid: '',
ntrulename: ''
}
}).then(() => {
getResultListRequest();
setDataareaDialog.value.status = false;
const { cacheData } = JSON.parse(JSON.stringify(setDataareaDialog.value));
ElMessageBox.confirm(
JSON.stringify(cacheData.value) === '{}' ? "撤销重置数据域操作!" : `撤销${cacheData.value.ttables}.${cacheData.value.tfields}的重置数据域操作!`,
'警告',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}
).then(() => {
discoverchangerule({
resultid: appStore.queryData.resultid,
tableid: cacheData.value.id,
filter: {
...requestParams.value.filter,
nareaid: cacheData.value.dataarea_id,
nareaname: cacheData.value.dataarea_name,
ntruleid: String(cacheData.value.sensitive_id),
ntrulename: cacheData.value.sensitive_name
}
}).then(() => {
getResultListRequest()
})
})
const matchPreviewDialogRef = ref()
const matchPreviewDialogVisible = ref(false)
const currentRowDataPreview = ref(null)
// 模拟匹配数据
const matchedData = ref([
{ value: '15888888888' },
{ value: '15888888888' },
{ value: '15888888888' },
{ value: '15888888888' },
{ value: '15888888888' },
{ value: '15888888888' },
{ value: '15888888888' },
{ value: '15888888888' },
{ value: '15888888888' },
{ value: '15888888888' }
])
})
}
const unmatchedData = ref([]) // 空数组,显示"暂无数据"
const closeDomainRuleDialog = () => {
dataAreaInfo.value.rule = '';
dataAreaInfo.value.ruleList = []
dataAreaInfo.value.dataArea = '';
searchregexParams.value.dataarea = '';
setDataareaDialog.value.status = false
console.log(1234)
}
const matchingDiscoverDialog = ref({
status: false,
sampleViewList: [],
unmatchDataList: []
})
/**
* 匹配预览
* @param row
*/
const matchingPercent = (row) =>{
showmatchrate({
projectid: sessionStorage.getItem('projectId'),
taskid: appStore.queryData.taskid,
ttables: row.ttables,
tfields: row.tfields,
dataarea_name: row.dataarea_name
}).then(response => {
const { matchurl, unmatchurl } = response.data;
matchingDiscoverDialog.value.status = true;
matchingDiscoverDialog.value.sampleViewList = matchurl.map(item => ({label: item}));
matchingDiscoverDialog.value.unmatchDataList = unmatchurl.map(item => ({label: item}));
})
}
// 关闭弹窗时清空数据
const handleMatchDialogClose = () => {
matchingDiscoverDialog.value.sampleViewList = []
matchingDiscoverDialog.value.unmatchDataList = []
}
// 打开匹配预览弹窗
const handleMatch = (row) => {
console.log('匹配预览:', row)
currentRowDataPreview.value = row
// 根据行数据获取匹配结果(这里需要根据实际业务逻辑实现)
// const matchResult = getMatchResult(row)
// matchedData.value = matchResult.matched
// unmatchedData.value = matchResult.unmatched
// 打开弹窗
matchPreviewDialogVisible.value = true
/**
* 导出当前查询结果信息文件
*/
const exportData = () => {
download({
resultid: appStore.queryData.resultid
}).then(res => {
let blob = new Blob([res], {
type: 'application/vnd.ms-excel,charset=utf-8'
})
let elink = document.createElement('a');
elink.href = URL.createObjectURL(blob);
elink.download = appStore.queryData.discoverTaskName + "发现结果.xlsx"
document.body.appendChild(elink);
elink.click();
document.body.removeChild(elink);
})
}
// 获取匹配结果的方法(需要根据实际业务实现)
const getMatchResult = (row) => {
// 这里应该是您的业务逻辑,返回匹配和不匹配的数据
return {
matched: matchedData.value,
unmatched: unmatchedData.value
}
const handleImport = () => {
uploadRef.value?.$el.querySelector('input[type="file"]')?.click()
}
const uploadRef = ref(null)
const uploadHeaders = ref({ Token: getToken() })
const uploadAction = computed(() => import.meta.env.VITE_APP_BASE_API + '/core/tdiscoverresult/importdata')
const uploadData = ref({
resultid: appStore.queryData.resultid
})
/**
* 上传前的验证
*/
const beforeUpload = (file) => {
console.log(file)
const isExcel = file.type === 'application/vnd.ms-excel' ||
file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ||
file.name.endsWith('.xlsx') ||
file.name.endsWith('.xls')
if (!isExcel) {
ElMessage.error('只能上传 Excel 文件!')
return false
}
const isLt10M = file.size / 1024 / 1024 < 10
if (!isLt10M) {
ElMessage.error('文件大小不能超过 10MB!')
return false
}
// 显示加载中
ElMessage.info('文件上传中...')
return true
}
/**
* 上传成功处理
*/
const handleSuccess = (response, file, fileList) => {
if (response.flag) {
ElMessage.success('导入成功!')
// 刷新数据列表
getResultListRequest()
} else {
ElMessage.error(response.msg || '导入失败')
}
}
/**
* 上传失败处理
*/
const handleError = (error, file, fileList) => {
console.error('Upload error:', error)
ElMessage.error('文件上传失败,请重试')
}
</script>
......@@ -180,41 +785,44 @@ const getMatchResult = (row) => {
<div class="app-container scroller">
<PageTitle :back="true" @back="pageProjectManage">
<template #title>
返回项目管理
查看结果
</template>
<template #buttons>
<el-button
type="success"
@click="batchSettingRules"
>
批量设置规则
</el-button>
<el-checkbox style="margin-right: 10px;" v-model="selectAll" @change="displaySensitive" class="select-all-checkbox">仅显示敏感字段</el-checkbox>
<el-button
type="primary"
:Loading="confirmAllLoading"
@click="confirmAll"
>
确认所有
</el-button>
<el-button
type="danger"
:loading="cancelAllLoading"
@click="cancelAll"
>
取消所有
</el-button>
<el-button
type="success"
@click="handleSave"
@click="openSaveDialog"
>
保存
保存版本
</el-button>
<el-button
type="info"
@click="handleBack"
type="primary"
@click="exportData"
>
后退
导出
</el-button>
<el-button
type="success"
@click="handleImport"
>
导入
</el-button>
<el-checkbox style="margin-left: 10px;" v-model="selectAll" @change="displaySensitive" class="select-all-checkbox">仅显示敏感字段</el-checkbox>
</template>
</PageTitle>
......@@ -227,100 +835,116 @@ const getMatchResult = (row) => {
<h2 class="report-title">发现任务执行报告</h2>
<div class="overview-cards">
<el-descriptions :column="4" border>
<el-descriptions-item label="任务运行时间">
<span class="highlight-text">911毫秒</span>
</el-descriptions-item>
<el-descriptions-item label="任务总对象数">
<span class="highlight-text">20</span>
</el-descriptions-item>
<el-descriptions-item label="发现敏感对象数(表/文件)">
<span class="highlight-text">2</span>
</el-descriptions-item>
<el-descriptions-item label="任务总列数">
<span class="highlight-text">225</span>
</el-descriptions-item>
<el-descriptions-item label="发现敏感列数">
<span class="highlight-text">2</span>
<el-descriptions-item :label="item.label" v-for="(item,index) in reportData" :key="index">
<span class="highlight-text">{{item.content}}</span>
</el-descriptions-item>
</el-descriptions>
</div>
</div>
<!-- 下方:详细数据表格 -->
<div class="detailed-data">
<el-collapse v-model="activeNames" accordion @change="handleChangeTableByCollapse">
<el-collapse v-model="activeNames" v-if="collapseDataList.length > 0" v-loading="listLoading" accordion @change="handleChangeTableByCollapse">
<!-- 第一个表格 -->
<el-collapse-item :name="item.name" v-for="(item, index) in allTableNames" :key="index">
<el-collapse-item :name="item.tfields" v-for="(item, index) in collapseDataList" :key="index">
<template #title>
<span class="collapse-title">{{ item.name }}</span>
<span class="sensitive-count">{{item.sensitiveCount}}个敏感数据</span>
</template>
<el-table :data="genTableData" height="300px" style="width: 100%;">
<el-table-column prop="index" label="序号" width="60" align="center" fixed />
<el-table-column prop="field" label="字段" min-width="120" />
<el-table-column prop="fieldDesc" label="字段说明" min-width="150" />
<el-table-column label="采样数/有效采样数/匹配率" min-width="180">
<template #default="{ row }">
{{ row.samplingInfo }}
<span class="collapse-title">{{ item.tfields }}</span>
<span class="sensitive-count">{{item.dataarea_name}}</span>
</template>
</el-table-column>
<el-table-column prop="dataDomain" label="数据域" min-width="100">
<template #default="scope">
<el-button
v-if="scope.row.discoveryRule"
type="primary"
plain
size="small"
@click="openSettingDialog(scope.row)"
>
{{ scope.row.discoveryRule }}
</el-button>
<el-button
v-else
plain
type="primary"
size="small"
@click="openSettingDialog(scope.row)"
<el-table :data="item.children" height="300px" style="width: 100%;">
<el-table-column
v-for="(tableItem,tableIndex) in tableColumns"
:fixed="tableIndex === tableColumns.length - 1 ? 'right' : ''"
:key="tableIndex"
:prop="tableItem.key"
:label="tableItem.title"
:width="tableItem.width"
:align="tableItem.align"
:show-overflow-tooltip="tableItem.tooltip"
>
设置
</el-button>
</template>
</el-table-column>
<el-table-column prop="discoveryRule" label="数据发现规则" min-width="150" />
<el-table-column prop="status" label="梳理" min-width="100">
<template #default>
<el-tag size="small">未确认</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" min-width="120" align="center" fixed="right">
<template #default="scope">
<el-button type="primary" size="small" @click="handleMatch(scope.row)">匹配预览</el-button>
<div v-if="tableItem.key === 'index'">{{ scope.$index + 1 }}</div>
<el-button type="primary" link v-if="tableItem.key === 'dataarea_name'" @click="setDataarea(scope.row)">{{ scope.row.icon.find(x =>x.key === 'dataarea_name').label }}</el-button>
<el-button type="primary" plain v-if="tableItem.key === 'confirm'" size="small" @click="confirmKey(scope.row)">{{ scope.row.button[0].label }}</el-button>
<el-button type="success" plain v-if="tableItem.key === 'actStatus'" size="small" @click="matchingPercent(scope.row)">{{ scope.row.icon.find(x =>x.key === 'actStatus').label }}</el-button>
</template>
</el-table-column>
</el-table>
</el-collapse-item>
</el-collapse>
<el-empty description="暂无数据" v-else/>
</div>
<pagination
v-show="totalCount > 0"
:total="totalCount"
v-model:page="requestParams.page"
v-model:limit="requestParams.rows"
@pagination="getResultListRequest"
/>
</div>
</div>
<DomainRuleDialog
v-model="domainRuleDialogVisible"
:selected-data="currentRowData ? {
domain: currentRowData.dataDomainValue,
rule: currentRowData.discoveryRuleValue
} : {}"
v-model="setDataareaDialog.status"
:title="setDataareaDialog.title || '设置数据域与字段发现规则'"
:data-area-list="dataAreaInfo.dataAreaList"
:selected-data-area="dataAreaInfo.dataArea"
:selected-rule="dataAreaInfo.rule"
:ruleList="dataAreaInfo.ruleList"
@update:selectedDataArea="(value) => dataAreaInfo.dataArea = value"
@update:selectedRule="(value) => dataAreaInfo.rule = value"
@confirm="handleRuleConfirm"
@handleReset="resetRule"
@closeDialog="closeDomainRuleDialog"
/>
<!-- 匹配预览弹窗 -->
<MatchPreviewDialog
v-model="matchPreviewDialogVisible"
:matched-data="matchedData"
:unmatched-data="unmatchedData"
v-model="matchingDiscoverDialog.status"
:matched-data="matchingDiscoverDialog.sampleViewList"
:unmatched-data="matchingDiscoverDialog.unmatchDataList"
@close="handleMatchDialogClose"
/>
<SaveVersionDialog
v-model="saveVersionDialog.status"
title="保存发现模板"
:history-list="task_version_data"
:version-name="saveVersionDialog.params.versionname"
@update:versionName="(value) => saveVersionDialog.params.versionname = value"
@save="handleSave"
@close="handleSaveDialogClose"
/>
<el-upload
ref="uploadRef"
class="hidden-upload"
:action="uploadAction"
:headers="uploadHeaders"
:data="uploadData"
:before-upload="beforeUpload"
:on-success="handleSuccess"
:on-error="handleError"
:show-file-list="false"
accept=".xlsx,.xls"
/>
</div>
</template>
......@@ -351,7 +975,7 @@ const getMatchResult = (row) => {
.discovery-report-container {
padding: 20px;
background: #f5f7fa;
min-height: 100vh;
// min-height: 100vh;
}
.report-overview {
......
......@@ -13,8 +13,10 @@ import SmartDiscoveryDialog from './modules/SmartDiscoveryDialog.vue'
import {
queryDiscoverList
queryDiscoverList,
delDiscoverTask,
executetask,
initExecute
} from '@/api/discover/index'
const route = useRoute()
......@@ -166,25 +168,71 @@ const handleEdit = (row) => {
const handleDiscover = (row) => {
console.log('发现:', row)
ElMessage.success(`开始发现任务: ${row.taskName}`)
emit('page', 'discoverProcess', { listItem: row})
// router.push({
// path: '/user/discoverProcess',
// query: {
// taskid: row.id,
// resultid: row.resultid,
// datasystemid: row.datasystemid,
// discoverTaskName: row.tname,
// btnStatus: '1'
// }
// })
}
ElMessageBox.confirm('确认执行发现任务吗?'+ `[${row.taskName}]`, '确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
executetask({
taskid: row.id,
version: '',
datasystemid: row.datasystemid,
taskname: row.tname,
projectid: sessionStorage.getItem('projectId'),
project: sessionStorage.getItem('projectName')
}).then(res => {
if(res.flag){
appStore.setQueryData({
taskid: row.id,
btnStatus: '1',
resultid: row.resultid,
datasystemid: row.datasystemid,
discoverTaskName: row.tname,
icon: route.meta.icon,
breadPathData: [
{
path: route.path,
label: route.meta.title
},
{
label: "发现进度"
}
]
});
emit('page', 'discoverProcess', { listItem: row})
}else{
ElMessage.error(res.msg)
}
})
})
}
/**
* 监控
* @param row
*/
const handleMonitor = (row) => {
console.log('监控:', row)
ElMessage.warning(`监控任务: ${row.taskName}`)
appStore.setQueryData({
taskid: row.id,
btnStatus: row.taskstatus,
resultid: row.resultid,
datasystemid: row.datasystemid,
discoverTaskName: row.tname,
icon: route.meta.icon,
breadPathData: [
{
path: route.path,
label: route.meta.title
},
{
label: "发现进度"
}
]
});
emit('page', 'discoverProcess', { listItem: row})
}
const smartDiscoveryDialogRef = ref()
const smartData = ref(null)
......@@ -193,7 +241,6 @@ const handleMoreCommand = (command, row) => {
case 'smartDiscover':
smartData.value = row
handleSmartDiscover(row)
ElMessage.info(`智能发现: ${row.taskName}`)
break
case 'viewHistory':
ElMessage.info(`查看历史: ${row.taskName}`)
......@@ -206,18 +253,103 @@ const handleMoreCommand = (command, row) => {
emit('page','discoverResult')
break
case 'delete':
ElMessage.error(`删除任务: ${row.taskName}`)
ElMessageBox.confirm('删除后无法恢复,是否确认删除?', '确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
delDiscoverTask({
dataSystemName: 'orcl202',
taskid: row.id
}).then(res => {
if(res.flag){
ElMessage.success(res.msg)
handleQuery()
}else{
ElMessage.error(res.msg)
}
})
})
break
}
}
const smartDiscoveryDialogVisible = ref(false)
const AIDIscoverVersionListDialog = ref({
status: false,
cacheData: {},
columnsData: [
{
title: '发现版本',
key: 'name',
align: 'left'
},
{
title: "操作",
slot: "action",
key: "action",
align: "center",
width: 150
}
],
tableData: []
})
// 打开智能发现弹窗
const handleSmartDiscover = (row) => {
// 可以传入特定的发现版本数据
const discoveryData = [
{ version: row.resultid || '12123' } // 使用任务的结果ID或默认值
initExecute({
taskid: row.id,
projectid: sessionStorage.getItem('projectId')
}).then(res => {
const {task_version, project_version, all_version} = res.data
const allVersionList = [];
AIDIscoverVersionListDialog.value.status = true
AIDIscoverVersionListDialog.value.cacheData = row
if (JSON.stringify(all_version) !== '{}') {
allVersionList.push({
...project_version,
version: all_version.versionname,
type: 'all_version',
versionname: `全局版本:${all_version.versionname}`
})
}
if (JSON.stringify(project_version) !== '{}') {
allVersionList.push({
...project_version,
version: project_version.versionname,
type: 'project_version',
versionname: `项目版本:${project_version.versionname}`
})
}
task_version.map(item => {
item.version = item.versionname;
item.type = item.filename;
item.versionname = `任务版本:${item.versionname}`;
allVersionList.push(item);
})
AIDIscoverVersionListDialog.value.tableData = allVersionList.map(item => {
return {
...item,
name: item.versionname,
icon: [
{
...appStore.tableButtonIcons.discover,
key: 'action',
name: 'handleDiscover',
label: '智能发现',
}
]
}
})
})
// 优先使用ref方式
if (smartDiscoveryDialogRef.value?.openDialog) {
......
<template>
<el-dialog
v-model="dialogVisible"
title="设置数据域与字段发现规则"
width="700px"
:title="title"
width="800px"
destroy-on-close
:before-close="handleClose"
>
<div class="domain-rule-dialog">
<!-- 搜索区域 -->
<div class="search-section">
<el-input
v-model="domainSearch"
v-model="searchText"
placeholder="请输入数据域名称"
size="small"
prefix-icon="Search"
clearable
class="search-input"
/>
</div>
<div class="content-section">
<!-- 左侧数据域列表 -->
<div class="domain-list">
<div class="section-title">选择数据域</div>
<div class="content-container">
<!-- 左侧:选择数据域 -->
<div class="left-panel">
<div class="panel-header">
<h4>选择数据域</h4>
</div>
<div class="list-container">
<el-radio-group v-model="selectedDomain" @change="handleDomainChange">
<div
v-for="domain in filteredDomains"
:key="domain.value"
class="domain-item"
v-for="item in filteredDataAreaList"
:key="item.dataarea.id"
class="list-item"
:class="{ 'item-selected': selectedDataArea === item.dataarea.id }"
@click="selectDataArea(item)"
>
<el-radio :label="domain.value">
{{ domain.label }}
</el-radio>
<div class="item-content">
<span class="item-text">{{ item.dataarea.dataarea }}</span>
</div>
</div>
</el-radio-group>
</div>
</div>
<!-- 右侧字段发现规则 -->
<div class="rule-list">
<div class="section-title">选择字段发现规则</div>
<!-- 右侧:选择字段发现规则 -->
<div class="right-panel">
<div class="panel-header">
<h4>选择字段发现规则</h4>
</div>
<div class="list-container">
<el-radio-group v-model="selectedRule">
<div
v-for="rule in currentRules"
:key="rule.value"
class="rule-item"
v-for="rule in combinedRuleList"
:key="rule.ruleid"
class="list-item"
:class="{ 'item-selected': selectedRule === rule.ruleid }"
@click="selectRule(rule)"
>
<el-radio :label="rule.value">
{{ rule.label }}
</el-radio>
<div class="item-content">
<span class="item-text">{{ rule.name }}</span>
<span class="item-desc" v-if="rule.note">
{{ rule.note }}
</span>
</div>
</div>
<div v-if="combinedRuleList.length === 0" class="empty-tip">
<el-empty description="暂无数据" />
</极速>
</div>
</el-radio-group>
</div>
</div>
</div>
<!-- 底部按钮 -->
<div class="dialog-footer">
<el-button @click="handleReset">重置规则</el-button>
......@@ -69,149 +81,110 @@ import { ref, computed, watch } from 'vue'
import { ElMessage } from 'element-plus'
const props = defineProps({
// 已选择的规则数据 { domain: '', rule: '' }
selectedData: {
type: Object,
default: () => ({})
modelValue: {
type: Boolean,
default: false
},
title: {
type: String,
default: '设置数据域与字段发现规则'
},
dataAreaList: {
type: Array,
default: () => []
},
ruleList: {
type: Array,
default: () => []
},
selectedDataArea: {
type: String,
default: ''
},
selectedRule: {
type: String, // 明确为字符串类型
default: ''
}
})
const emit = defineEmits(['update:modelValue', 'confirm'])
const emit = defineEmits(['update:modelValue', 'confirm', 'update:selectedDataArea', 'update:selectedRule','closeDialog','handleReset'])
const dialogVisible = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value)
})
const domainSearch = ref('')
const selectedDomain = ref('')
const selectedRule = ref('')
// 数据域选项(根据图片内容)
const domains = ref([
{ value: 'general', label: '通用规则' },
{ value: 'decrypt', label: '解密数据' },
{ value: 'mixed_id', label: '混合证件号' },
{ value: 'email', label: '电子邮件' },
{ value: 'social_security', label: '社保卡号' },
{ value: 'business_license', label: '营业执照' },
{ value: 'postal_code', label: '邮政编码' },
{ value: 'chinese_address', label: '中文地址' },
{ value: 'mixed_phone', label: '混合电话号码' },
{ value: 'name', label: '姓名' },
{ value: 'bank_card', label: '银行卡号' },
{ value: 'ip_address', label: 'IP地址' }
])
// 字段发现规则映射
const rulesMapping = {
general: [
{ value: 'general_rule_1', label: '通用规则查询' },
{ value: 'general_rule_2', label: '通用数据发现' }
],
decrypt: [
{ value: 'decrypt_rule_1', label: '解密数据查询' },
{ value: 'decrypt_rule_2', label: '数据解密规则' }
],
mixed_id: [
{ value: 'mixed_id_rule_1', label: '按混合证件号字段查询' }
],
email: [
{ value: 'email_rule_1', label: '邮箱字段查询' },
{ value: 'email_rule_2', label: '电子邮件规则' }
],
social_security: [
{ value: 'ss_rule_1', label: '社保卡号查询' }
],
business_license: [
{ value: 'bl_rule_1', label: '营业执照查询' }
],
postal_code: [
{ value: 'pc_rule_1', label: '邮政编码查询' }
],
chinese_address: [
{ value: 'address_rule_1', label: '中文地址查询' }
],
mixed_phone: [
{ value: 'phone_rule_1', label: '混合电话查询' }
],
name: [
{ value: 'name_rule_1', label: '姓名字段查询' }
],
bank_card: [
{ value: 'bank_rule_1', label: '银行卡号查询' }
],
ip_address: [
{ value: 'ip_rule_1', label: 'IP地址查询' }
]
}
const searchText = ref('')
const localSelectedDataArea = ref(props.selectedDataArea)
const localSelectedRule = ref(props.selectedRule)
const internalRuleList = ref([])
// 合并的规则列表:优先使用父组件传入的 ruleList,如果没有则使用内部生成的规则列表
const combinedRuleList = computed(() => {
return props.ruleList.length > 0 ? props.ruleList : internalRuleList.value
})
// 过滤后的数据域列表
const filteredDomains = computed(() => {
if (!domainSearch.value) return domains.value
return domains.value.filter(domain =>
domain.label.toLowerCase().includes(domainSearch.value.toLowerCase())
const filteredDataAreaList = computed(() => {
if (!searchText.value) return props.dataAreaList
return props.dataAreaList.filter(item =>
item.dataarea.dataarea.toLowerCase().includes(searchText.value.toLowerCase())
)
})
// 当前选中的数据域对应的规则
const currentRules = computed(() => {
return rulesMapping[selectedDomain.value] || []
})
// 选择数据域
const selectDataArea = (item) => {
localSelectedDataArea.value = item.dataarea.id
localSelectedRule.value = ''
// 打开弹窗时初始化
const initDialog = () => {
if (props.selectedData.domain) {
selectedDomain.value = props.selectedData.domain
selectedRule.value = props.selectedData.rule || ''
} else {
// 默认选择第一个数据域和第一个规则
selectedDomain.value = domains.value[0]?.value || ''
if (selectedDomain.value && currentRules.value.length > 0) {
selectedRule.value = currentRules.value[0].value
}
}
// 更新内部规则列表(如果父组件没有传入 ruleList)
internalRuleList.value = item.data || []
emit('update:selectedDataArea', item.dataarea.id)
emit('update:selectedRule', '')
}
// 处理数据域变化
const handleDomainChange = (domainValue) => {
if (rulesMapping[domainValue] && rulesMapping[domainValue].length > 0) {
selectedRule.value = rulesMapping[domainValue][0].value
} else {
selectedRule.value = ''
}
// 选择规则
const selectRule = (rule) => {
localSelectedRule.value = rule.ruleid // 存储完整规则对象
emit('update:selectedRule', rule.ruleid)
}
// 重置规则
const handleReset = () => {
selectedDomain.value = domains.value[0]?.value || ''
if (selectedDomain.value && currentRules.value.length > 0) {
selectedRule.value = currentRules.value[0].value
}
domainSearch.value = ''
localSelectedDataArea.value = ''
localSelectedRule.value = ''
internalRuleList.value = []
emit('update:selectedDataArea', '')
emit('update:selectedRule', '')
emit('handleReset')
// ElMessage.info('规则已重置')
}
// 确规则
// 确规则
const handleConfirm = () => {
if (!selectedDomain.value) {
if (!localSelectedDataArea.value) {
ElMessage.warning('请选择数据域')
return
}
if (!selectedRule.value) {
if (!localSelectedRule.value) {
ElMessage.warning('请选择字段发现规则')
return
}
const selectedDomainObj = domains.value.find(d => d.value === selectedDomain.value)
const selectedRuleObj = currentRules.value.find(r => r.value === selectedRule.value)
const selectedDataArea = props.dataAreaList.find(
item => item.dataarea.id === localSelectedDataArea.value
)
// 从合并的规则列表中查找选中的规则
const selectedRule = combinedRuleList.value.find(
rule => rule.ruleid === localSelectedRule.value
)
console.log('selectedRule',selectedRule)
emit('confirm', {
domain: selectedDomain.value,
domainLabel: selectedDomainObj?.label || '',
rule: selectedRule.value,
ruleLabel: selectedRuleObj?.label || ''
domain: selectedDataArea.dataarea,
domainValue: selectedDataArea.dataarea.id,
rule: selectedRule.name,
ruleValue: selectedRule.ruleid
})
dialogVisible.value = false
......@@ -219,24 +192,37 @@ const handleConfirm = () => {
// 关闭弹窗
const handleClose = () => {
console.log('关闭')
internalRuleList.value = []
dialogVisible.value = false
emit('closeDialog')
}
// 监听弹窗显示状态
watch(dialogVisible, (newVal) => {
if (newVal) {
initDialog()
}
// 监听外部传入的选中值变化
watch(() => props.selectedDataArea, (newVal) => {
localSelectedDataArea.value = newVal
})
const openDialog = () => {
initDialog()
dialogVisible.value = true
}
// 暴露方法
defineExpose({
openDialog
watch(() => props.selectedRule, (newVal) => {
localSelectedRule.value = newVal
})
// 监听数据域列表变化,初始化选中状态
watch(() => props.dataAreaList, (newList) => {
console.log('传入子组件的 左侧数组',newList)
if (newList.length > 0 && localSelectedDataArea.value) {
const selectedItem = newList.find(
item => item.dataarea.id === localSelectedDataArea.value
)
if (selectedItem) {
internalRuleList.value = selectedItem.data || []
}
}
}, { immediate: true })
// 监听父组件传入的 ruleList 变化
watch(() => props.ruleList, (newRuleList) => {
console.log('父组件传入的 ruleList:', newRuleList)
}, { immediate: true })
</script>
<style scoped lang="scss">
......@@ -244,65 +230,103 @@ defineExpose({
.search-section {
margin-bottom: 16px;
.search-input {
width: 100%;
:deep(.el-input__inner) {
height: 32px;
}
}
}
.content-section {
.content-container {
display: flex;
gap: 20px;
height: 400px;
border: 1px solid #dcdfe6;
border-radius: 4px;
overflow: hidden;
}
.domain-list,
.rule-list {
.left-panel,
.right-panel {
flex: 1;
display: flex;
flex-direction: column;
min-width: 0;
}
.section-title {
.panel-header {
padding: 12px 16px;
background: #f0f7ff;
border-bottom: 1px solid #dcdfe6;
h4 {
margin: 0;
font-size: 14px;
font-weight: 600;
color: #303133;
margin-bottom: 12px;
padding-left: 4px;
}
}
.list-container {
flex: 1;
border: 1px solid #dcdfe6;
border-radius: 4px;
padding: 12px;
overflow-y: auto;
padding: 8px;
}
.domain-item,
.rule-item {
padding: 8px 0;
.list-item {
padding: 10px 12px;
margin-bottom: 4px;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s ease;
border: 1px solid transparent;
:deep(.el-radio) {
width: 100%;
&:hover {
background-color: #f5f7fa;
border-color: #dcdfe6;
}
.el-radio__label {
font-size: 13px;
&.item-selected {
background-color: #e6f7ff;
border-color: #1890ff;
.item-text {
color: #1890ff;
font-weight: 500;
}
}
}
.domain-item {
&:hover {
background-color: #f5f7fa;
border-radius: 4px;
.item-content {
display: flex;
flex-direction: column;
gap: 4px;
}
.item-text {
font-size: 13px;
color: #606266;
font-weight: 500;
}
.rule-item {
&:hover {
background-color: #f5f7fa;
border-radius: 4px;
.item-count {
font-size: 12px;
color: #909399;
}
.item-desc {
font-size: 12px;
color: #909399;
font-style: italic;
}
.empty-tip {
padding: 20px;
text-align: center;
color: #909399;
font-size: 13px;
}
.dialog-footer {
......@@ -314,7 +338,20 @@ defineExpose({
border-top: 1px solid #e0e0e0;
}
}
:deep(.el-radio-group) {
display: block;
// 响应式设计
@media (max-width: 768px) {
.domain-rule-dialog {
.content-container {
flex-direction: column;
height: 500px;
}
.left-panel,
.right-panel {
flex: none;
height: 200px;
}
}
}
</style>
\ No newline at end of file
......@@ -6,47 +6,76 @@
:before-close="handleClose"
>
<div class="match-preview-dialog">
<div class="match-content">
<div class="content-container">
<!-- 左侧:匹配的数据 -->
<div class="match-section">
<div class="section-header">
<span class="section-title">匹配的数据</span>
<div class="left-panel">
<div class="panel-header">
<h4>匹配的数据</h4>
<span class="count-badge">{{ matchedData.length }}</span>
</div>
<div class="table-container">
<el-table
:data="matchedData"
style="width: 100%"
height="400"
:header-cell-style="{ background: '#f0f7ff', color: '#606266', fontWeight: 'bold' }"
empty-text="暂无匹配数据"
:header-cell-style="{ background: '#f0f7ff', color: '#606266' }"
>
<el-table-column
type="index"
label="序号"
width="60"
align="center"
/>
<el-table-column
prop="label"
label="数据值"
min-width="150"
show-overflow-tooltip
>
<el-table-column prop="value" label="匹配的数据" align="center">
<template #default="{ row }">
<span class="match-value">{{ row.value }}</span>
<span class="data-value">{{ row.label }}</span>
</template>
</el-table-column>
</el-table>
</div>
</div>
<!-- 分隔线 -->
<div class="divider"></div>
<!-- 右侧:不匹配的数据 -->
<div class="match-section">
<div class="section-header">
<span class="section-title">不匹配的数据</span>
<div class="right-panel">
<div class="panel-header">
<h4>不匹配的数据</h4>
<span class="count-badge">{{ unmatchedData.length }}</span>
</div>
<div class="table-container">
<el-table
:data="unmatchedData"
style="width: 100%"
height="400"
:header-cell-style="{ background: '#f0f7ff', color: '#606266', fontWeight: 'bold' }"
empty-text="暂无数据"
:header-cell-style="{ background: '#f0f7ff', color: '#606266' }"
>
<el-table-column
type="index"
label="序号"
width="60"
align="center"
/>
<el-table-column
prop="label"
label="数据值"
min-width="150"
show-overflow-tooltip
>
<el-table-column prop="value" label="不匹配的数据" align="center">
<template #default="{ row }">
<span class="unmatch-value">{{ row.value }}</span>
<span class="data-value">{{ row.label }}</span>
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
<!-- 底部按钮 -->
<div class="dialog-footer">
......@@ -57,78 +86,117 @@
</template>
<script setup>
import { ref, computed } from 'vue'
import { ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
const props = defineProps({
modelValue: {
type: Boolean,
default: false
},
// 匹配的数据
matchedData: {
type: Array,
default: () => []
},
// 不匹配的数据
unmatchedData: {
type: Array,
default: () => []
}
})
const emit = defineEmits(['update:modelValue'])
const emit = defineEmits(['update:modelValue', 'close'])
const dialogVisible = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value)
})
// 关闭弹窗
// 处理关闭
const handleClose = () => {
dialogVisible.value = false
emit('close')
}
// 暴露方法
defineExpose({
openDialog: () => {
dialogVisible.value = true
// 监听对话框关闭,清空数据
watch(dialogVisible, (newVal) => {
if (!newVal) {
// 对话框关闭时,可以通过父组件清空数据
emit('close')
}
})
</script>
<style scoped lang="scss">
.match-preview-dialog {
.match-content {
.content-container {
display: flex;
gap: 20px;
margin-bottom: 20px;
gap: 0;
height: 450px;
}
.match-section {
.left-panel,
.right-panel {
flex: 1;
background: #fafafa;
border-radius: 6px;
overflow: hidden;
display: flex;
flex-direction: column;
min-width: 0;
}
.section-header {
background: #f0f7ff;
.panel-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
background: #f0f7ff;
border-bottom: 1px solid #dcdfe6;
.section-title {
h4 {
margin: 0;
font-size: 14px;
font-weight: 600;
color: #303133;
}
.count-badge {
background: #1890ff;
color: white;
padding: 2px 8px;
border-radius: 10px;
font-size: 12px;
font-weight: 500;
}
}
.table-container {
flex: 1;
border: 1px solid #dcdfe6;
border-top: none;
border-radius: 0 0 4px 4px;
overflow: hidden;
}
.divider {
width: 1px;
background: #dcdfe6;
margin: 0 10px;
}
.data-value {
font-family: 'Monaco', 'Consolas', monospace;
font-size: 13px;
color: #303133;
}
:deep(.el-table) {
border-radius: 0;
border: none;
.el-table__header-wrapper {
th {
background: #f0f7ff !important;
.cell {
font-weight: 600;
}
}
}
......@@ -145,19 +213,10 @@ defineExpose({
}
}
.match-value {
color: #52c41a;
font-weight: 500;
}
.unmatch-value {
color: #ff4d4f;
font-weight: 500;
}
.dialog-footer {
display: flex;
justify-content: center;
margin-top: 20px;
padding-top: 16px;
border-top: 1px solid #e0e0e0;
}
......@@ -166,12 +225,21 @@ defineExpose({
// 响应式设计
@media (max-width: 768px) {
.match-preview-dialog {
.match-content {
.content-container {
flex-direction: column;
height: 600px;
}
.left-panel,
.right-panel {
flex: none;
height: 250px;
}
.match-section {
min-height: 200px;
.divider {
width: 100%;
height: 1px;
margin: 10px 0;
}
}
}
......
<template>
<el-dialog
v-model="dialogVisible"
:title="title"
width="600px"
:before-close="handleClose"
>
<div class="save-version-dialog">
<!-- 历史版本表格 -->
<div class="history-section">
<h4 class="section-title">历史版本</h4>
<el-table
:data="historyList"
height="200"
empty-text="暂无历史版本"
:header-cell-style="{ background: '#f0f7ff', color: '#606266' }"
>
<el-table-column
prop="versionname"
label="版本名称"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
prop="operator"
label="操作人"
width="100"
align="center"
/>
<el-table-column
prop="savetime"
label="保存时间"
width="160"
align="center"
/>
</el-table>
</div>
<!-- 保存版本区域 -->
<div class="save-section">
<h4 class="section-title">保存版本</h4>
<div class="save-input-container">
<el-input
v-model="versionName"
placeholder="请输入版本名称"
clearable
class="version-input"
@keyup.enter="handleSave"
/>
<el-button
type="primary"
@click="handleSave"
:loading="saving"
class="save-btn"
>
保存
</el-button>
</div>
</div>
</div>
</el-dialog>
</template>
<script setup>
import { ref, computed, watch } from 'vue'
import { ElMessage } from 'element-plus'
const props = defineProps({
modelValue: {
type: Boolean,
default: false
},
title: {
type: String,
default: '保存发现模板'
},
historyList: {
type: Array,
default: () => []
},
versionName: {
type: String,
default: ''
}
})
const emit = defineEmits([
'update:modelValue',
'update:versionName',
'save',
'close'
])
const dialogVisible = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value)
})
const versionName = computed({
get: () => props.versionName,
set: (value) => emit('update:versionName', value)
})
const saving = ref(false)
// 处理保存
const handleSave = async () => {
if (!versionName.value.trim()) {
ElMessage.warning('请输入版本名称')
return
}
saving.value = true
try {
emit('save', versionName.value.trim())
} catch (error) {
console.error('Save error:', error)
} finally {
saving.value = false
}
}
// 关闭弹窗
const handleClose = () => {
dialogVisible.value = false
emit('close')
}
// 监听对话框显示状态,重置状态
watch(dialogVisible, (newVal) => {
if (!newVal) {
saving.value = false
}
})
</script>
<style scoped lang="scss">
.save-version-dialog {
.section-title {
margin: 0 0 12px 0;
font-size: 14px;
font-weight: 600;
color: #303133;
}
.history-section {
margin-bottom: 20px;
:deep(.el-table) {
border: 1px solid #dcdfe6;
border-radius: 4px;
.el-table__header-wrapper {
th {
background: #f0f7ff;
font-weight: 600;
.cell {
font-weight: 600;
}
}
}
.el-table__body-wrapper {
.el-table__row {
&:nth-child(even) {
background-color: #fafafa;
}
&:hover {
background-color: #f5f7fa;
}
}
}
}
}
.save-section {
.save-input-container {
display: flex;
gap: 12px;
align-items: center;
.version-input {
flex: 1;
:deep(.el-input__inner) {
height: 36px;
}
}
.save-btn {
height: 36px;
min-width: 80px;
}
}
}
}
// 响应式设计
@media (max-width: 640px) {
.save-version-dialog {
.save-input-container {
flex-direction: column;
align-items: stretch;
.save-btn {
width: 100%;
}
}
}
}
</style>
\ No newline at end of file
......@@ -150,28 +150,25 @@ const addLoading = ref(false)
const showAddDialog = () => {
addLoading.value = true
checkProjectNum().then(res=>{
console.log('checkProjectNum',res)
if(res.flag){
currentProject.value = null
dialogVisible.value = true
// getdatascopeprojectlist().then(res=>{
// addLoading.value = true
// checkProjectNum().then(res=>{
// console.log('checkProjectNum',res)
// if(res.flag){
// currentProject.value = null
// dialogVisible.value = true
// })
addLoading.value = false
}else{
addLoading.value = false
}
}).catch(err=>{
addLoading.value = false
})
//
// addLoading.value = false
// }else{
// addLoading.value = false
// }
// }).catch(err=>{
// addLoading.value = false
// })
currentProject.value = null
dialogVisible.value = true
addLoading.value = false
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论