Commit 56d9ad54 by ningjihai

脱敏策略管理

parent 3dc0b19a
<script setup name="ProjectManageList">
import { getCurrentInstance, reactive, ref, toRefs } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import QueryForm from './QueryForm.vue'
import AddStrategyDialog from './modules/AddStrategyDialog.vue'
import { useRouter } from 'vue-router'
import useAppStore from '@/store/modules/app'
import usePermissionStore from '@/store/modules/permission'
import { changeRoute } from '@/utils/switchRoute'
import QueryForm from './QueryForm.vue'
import AddStrategyDialog from './modules/AddStrategyDialog.vue'
import StrategyDetailDialog from './modules/StrategyDetailDialog.vue' // 引入详情组件
import EditStrategyDialog from './modules/EditStrategyDialog.vue' // 引入编辑策略组件
const appStore = useAppStore()
const permissionStore = usePermissionStore()
const router = useRouter()
......@@ -79,13 +84,15 @@ const tableData = ref([
status: '1'
}
])
const strategyDetailDialogRef = ref()
const showDetail = (row) => {
console.log('查看详情:', row)
strategyDetailDialogRef.value.openDialog(row)
}
// 编辑策略弹窗引用
const editStrategyDialogRef = ref()
const editStrategy = (row) => {
console.log('编辑策略:', row)
console.log('编辑策略:', row)
editStrategyDialogRef.value.openDialog(row)
}
const deleteStrategy = (row) => {
......@@ -199,6 +206,11 @@ const toggleStatus = (row) => {
<!-- 新增策略弹窗 -->
<AddStrategyDialog ref="addStrategyDialogRef" />
<!-- 详情弹窗 -->
<StrategyDetailDialog ref="strategyDetailDialogRef" />
<!-- 编辑策略弹窗 -->
<EditStrategyDialog ref="editStrategyDialogRef" />
</div>
</template>
......
......@@ -9,32 +9,30 @@
<div class="dialog-content">
<!-- 左侧:选择数据域(按照图片样式重写) -->
<div class="data-domain-section">
<el-card>
<div class="selector-header">
<span class="selector-title">选择数据域</span>
</div>
<div class="options-list">
<div
v-for="domain in dataDomains"
:key="domain.value"
class="option-item"
:class="{ 'option-selected': selectedDomain === domain.value }"
@click="selectDomain(domain.value)"
>
<!-- 自定义单选按钮 -->
<div class="custom-radio">
<div class="radio-outer">
<div class="radio-inner" :class="{ selected: selectedDomain === domain.value }"></div>
</div>
<!-- <el-card></el-card> -->
<div class="selector-header">
<span class="selector-title">选择数据域</span>
</div>
<div class="options-list">
<div
v-for="domain in dataDomains"
:key="domain.value"
class="option-item"
:class="{ 'option-selected': selectedDomain === domain.value }"
@click="selectDomain(domain.value)"
>
<!-- 自定义单选按钮 -->
<div class="custom-radio">
<div class="radio-outer">
<div class="radio-inner" :class="{ selected: selectedDomain === domain.value }"></div>
</div>
<!-- 选项标签 -->
<span class="option-label">{{ domain.label }}</span>
</div>
<!-- 选项标签 -->
<span class="option-label">{{ domain.label }}</span>
</div>
</el-card>
</div>
</div>
<!-- <el-divider direction="vertical" /> -->
......@@ -249,7 +247,7 @@ const confirmRules = () => {
.selector-header {
padding: 0 0 16px 0;
margin-bottom: 8px;
border-bottom: 1px dashed #dcdfe6;
// border-bottom: 1px dashed #dcdfe6;
.selector-title {
font-size: 16px;
......@@ -261,6 +259,11 @@ const confirmRules = () => {
}
.options-list {
border: thin solid #dcdfe6;
border-radius: 5px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.04);
padding: 10px;
display: flex;
flex-direction: column;
gap: 0;
......@@ -291,6 +294,9 @@ const confirmRules = () => {
color: #1890ff;
font-weight: 500;
}
.radio-outer {
background-color: #1890ff;
}
}
}
......
<template>
<el-dialog
v-model="dialogVisible"
title="编辑策略"
width="80%"
:before-close="handleClose"
>
<div class="edit-strategy-dialog">
<!-- 版本选择区域 -->
<div class="version-section">
<div class="version-header">
<span class="version-title">根据发现版本设置脱敏:</span>
</div>
<div class="version-selector">
<span class="selector-label">选择发现版本:</span>
<el-select v-model="selectedVersion" placeholder="选择发现版本" class="version-select">
<el-option label="无版本" value="" />
<el-option
v-for="version in versions"
:key="version"
:label="version"
:value="version"
/>
</el-select>
</div>
</div>
<!-- 主体内容区域 -->
<div class="content-section">
<!-- 左侧树形结构 -->
<div class="tree-section">
<el-tree
:data="treeData"
:props="treeProps"
@node-click="handleNodeClick"
highlight-current
default-expand-all
:current-node-key="currentNodeKey"
/>
</div>
<el-divider direction="vertical" />
<!-- 右侧表格 -->
<div class="table-section">
<el-table
:data="tableFields"
border
style="width: 100%"
height="400px"
:header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
>
<el-table-column prop="isPk" label="主键" width="80" align="center" fixed>
<template #default="{ row }">
<span v-if="row.isPk">PK</span>
</template>
</el-table-column>
<el-table-column prop="name" label="字段名" min-width="150" />
<el-table-column prop="comment" label="注释" min-width="150" />
<el-table-column prop="dataType" label="数据域" min-width="120" />
<el-table-column prop="algorithm" label="脱敏算法" min-width="180" />
<el-table-column label="操作" width="80" align="center" fixed="right">
<template #default="{ row }">
<el-button type="text" size="small" @click="handleSet(row)">设置</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleSave">保存</el-button>
</div>
</template>
<!-- 脱敏规则设置弹窗 -->
<DesensitizationRuleDialog
v-model="desensitizationDialogVisible"
:current-field="currentField"
@confirm="handleRuleConfirm"
/>
</el-dialog>
</template>
<script setup>
import { ref, reactive } from 'vue'
import { ElMessage } from 'element-plus'
import DesensitizationRuleDialog from './DesensitizationRuleDialog.vue'
const dialogVisible = ref(false)
const desensitizationDialogVisible = ref(false)
const currentField = ref({})
const selectedVersion = ref('')
const currentNodeKey = ref('sys_config') // 默认选中sys_config表
// 版本选项
const versions = ref(['v1.0', 'v2.0', 'v3.0'])
// 树形数据(根据图片内容)
const treeData = ref([
{
id: 'system1',
label: '若依测试系统1',
children: [
{
id: 'ry',
label: 'ry',
children: [
{
id: 'tables',
label: '表',
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' }
]
}
]
}
]
}
])
const treeProps = {
children: 'children',
label: 'label'
}
// 表格字段数据(根据图片中的sys_config表)
const tableFields = ref([
{ isPk: true, name: 'config_id', comment: '参数主键', dataType: 'int', algorithm: '' },
{ isPk: false, name: 'config_key', comment: '参数键名', dataType: 'varchar', algorithm: '' },
{ isPk: false, name: 'config_name', comment: '参数名称', dataType: 'varchar', algorithm: '' },
{ isPk: false, name: 'config_type', comment: '系统内置 (Y是 N否)', dataType: 'char', algorithm: '' },
{ isPk: false, name: 'create_by', comment: '创建者', dataType: 'varchar', algorithm: '' },
{ isPk: false, name: 'create_time', comment: '创建时间', dataType: 'datetime', algorithm: '' },
{ isPk: false, name: 'remark', comment: '备注', dataType: 'varchar', algorithm: '' }
])
// 打开弹窗
const openDialog = (row) => {
dialogVisible.value = true
// 初始化数据,可以根据传入的row数据填充表单
if (row) {
// 这里可以根据实际需求初始化数据
selectedVersion.value = row.version || ''
}
}
// 关闭弹窗
const handleClose = () => {
dialogVisible.value = false
}
// 保存策略
const handleSave = () => {
// 这里调用API保存数据
console.log('保存策略:', {
version: selectedVersion.value,
fields: tableFields.value
})
ElMessage.success('策略保存成功')
dialogVisible.value = false
}
// 树节点点击
const handleNodeClick = (node) => {
if (node.type === 'table') {
currentNodeKey.value = node.id
fetchTableFields(node.label)
}
}
// 获取表字段数据
const fetchTableFields = (tableName) => {
// 模拟不同表的字段数据
const mockData = {
'sys_config': [
{ isPk: true, name: 'config_id', comment: '参数主键', dataType: 'int', algorithm: '' },
{ isPk: false, name: 'config_key', comment: '参数键名', dataType: 'varchar', algorithm: '' },
{ isPk: false, name: 'config_name', comment: '参数名称', dataType: 'varchar', algorithm: '' },
{ isPk: false, name: 'config_type', comment: '系统内置 (Y是 N否)', dataType: 'char', algorithm: '' },
{ isPk: false, name: 'create_by', comment: '创建者', dataType: 'varchar', algorithm: '' },
{ isPk: false, name: 'create_time', comment: '创建时间', dataType: 'datetime', algorithm: '' },
{ isPk: false, name: 'remark', comment: '备注', dataType: 'varchar', algorithm: '' }
],
'gen_table': [
{ isPk: true, name: 'table_id', comment: '编号', dataType: 'bigint', algorithm: '' },
{ isPk: false, name: 'table_name', comment: '表名称', dataType: 'varchar', algorithm: '' }
],
'default': [
{ isPk: false, name: 'field_name', comment: '字段名称', dataType: 'varchar', algorithm: '' }
]
}
tableFields.value = mockData[tableName] || mockData.default
}
// 设置字段脱敏规则
const handleSet = (row) => {
currentField.value = row
desensitizationDialogVisible.value = true
}
// 处理规则确认
const handleRuleConfirm = (ruleData) => {
// 更新表格中的算法字段
const fieldIndex = tableFields.value.findIndex(field => field.name === ruleData.fieldName)
if (fieldIndex !== -1) {
tableFields.value[fieldIndex].algorithm = ruleData.algorithm
ElMessage.success(`字段 ${ruleData.fieldName} 的脱敏规则已更新`)
}
}
defineExpose({
openDialog
})
</script>
<style scoped lang="scss">
.edit-strategy-dialog {
.version-section {
margin-bottom: 20px;
.version-header {
margin-bottom: 12px;
}
.version-title {
font-size: 14px;
font-weight: 600;
color: #606266;
}
.version-selector {
display: flex;
align-items: center;
gap: 12px;
.selector-label {
font-size: 14px;
color: #606266;
white-space: nowrap;
}
.version-select {
width: 200px;
}
}
}
.content-section {
display: flex;
height: 400px;
.tree-section {
width: 300px;
height: 100%;
overflow-y: auto;
padding-right: 16px;
}
.table-section {
flex: 1;
height: 100%;
overflow-y: auto;
}
}
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 12px;
}
}
:deep(.el-tree) {
.el-tree-node__content {
height: 36px;
&:hover {
background-color: #f5f7fa;
}
}
.is-current > .el-tree-node__content {
background-color: #f0f7ff;
color: #1890ff;
}
}
</style>
\ No newline at end of file
<template>
<el-dialog
v-model="dialogVisible"
title="详情"
width="80%"
:before-close="handleClose"
>
<div class="strategy-detail">
<!-- 基础信息区域 -->
<div class="basic-info">
<div class="info-grid">
<div class="info-item">
<span class="info-label">策略名称:</span>
<span class="info-value">{{ detailData.name || '-' }}</span>
</div>
<div class="info-item">
<span class="info-label">备注:</span>
<span class="info-value">{{ detailData.remark || '-' }}</span>
</div>
<div class="info-item">
<span class="info-label">创建人:</span>
<span class="info-value">{{ detailData.creator || 'admin' }}</span>
</div>
<div class="info-item">
<span class="info-label">创建时间:</span>
<span class="info-value">{{ detailData.createTime || '2025-08-22 17:12:50' }}</span>
</div>
<div class="info-item">
<span class="info-label">状态:</span>
<el-tag :type="detailData.status === '1' ? 'success' : 'info'">
{{ detailData.status === '1' ? '已启用' : '未启用' }}
</el-tag>
</div>
</div>
</div>
<el-divider />
<!-- 字段信息区域 -->
<div class="field-info">
<h3 class="section-title">字段信息</h3>
<div class="field-content">
<!-- 左侧树形结构 -->
<div class="tree-section">
<el-tree
:data="treeData"
:props="treeProps"
@node-click="handleNodeClick"
highlight-current
default-expand-all
/>
</div>
<el-divider direction="vertical" />
<!-- 右侧表格 -->
<div class="table-section">
<el-table
:data="tableFields"
border
style="width: 100%"
height="400px"
:header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
>
<el-table-column prop="pk" label="逐渐" min-width="150" />
<el-table-column prop="name" label="字段名" min-width="150" />
<el-table-column prop="comment" label="注释" min-width="150" />
<el-table-column prop="dataType" label="数据域" min-width="120" />
<el-table-column prop="algorithm" label="脱敏算法" min-width="180" />
</el-table>
</div>
</div>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="handleClose">关闭</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref, reactive } from 'vue'
const dialogVisible = ref(false)
const detailData = ref({})
const tableFields = ref([])
// 树形数据(与之前一致)
const treeData = ref([
{
id: 'system1',
label: '若依测试系统1',
children: [
{
id: 'ry',
label: 'ry',
children: [
{
id: 'tables',
label: '表',
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'
}
]
}
]
}
]
}
])
const treeProps = {
children: 'children',
label: 'label'
}
// 打开弹窗
const openDialog = (row) => {
detailData.value = { ...row }
dialogVisible.value = true
// 默认显示第一个表的字段
if (treeData.value[0]?.children?.[0]?.children?.[0]?.children?.[0]) {
fetchTableFields(treeData.value[0].children[0].children[0].children[0].label)
}
}
// 关闭弹窗
const handleClose = () => {
dialogVisible.value = false
}
// 树节点点击
const handleNodeClick = (node) => {
if (node.type === 'table') {
fetchTableFields(node.label)
}
}
// 获取表字段数据
const fetchTableFields = (tableName) => {
// 模拟数据,实际应该调用API
const mockData = {
'gen_table': [
{ pk: '', name: 'class_name', comment: '实体类名称', dataType: 'varchar', algorithm: 'aa' },
{ pk: '', name: 'create_by', comment: '创建者', dataType: 'varchar', algorithm: 'bb' },
{ pk: '', name: 'create_time', comment: '创建时间', dataType: 'datetime', algorithm: 'cc' },
{ pk: '', name: 'function_author', comment: '生成功能作者', dataType: 'varchar', algorithm: 'dd' },
{ pk: '', name: 'function_name', comment: '生成功能名', dataType: 'varchar', algorithm: 'ee' },
{ pk: '', name: 'gen_path', comment: '生成路径(不填默认...', dataType: 'varchar', algorithm: 'ff' },
{ pk: '', name: 'gen_type', comment: '生成代码方式(Ozip...', dataType: 'varchar', algorithm: 'gg' }
],
'gen_table_column': [
{ pk: '', name: 'column_id', comment: '编号', dataType: 'bigint', algorithm: 'hh' },
{ pk: '', name: 'table_id', comment: '归属表编号', dataType: 'bigint', algorithm: 'ii' },
{ pk: '', name: 'column_name', comment: '列名称', dataType: 'varchar', algorithm: 'jj' }
],
'default': [
{ pk: '', name: 'business_name', comment: '生成业务名', dataType: '通用规则', algorithm: 'aa' }
]
}
tableFields.value = mockData[tableName] || mockData.default
}
defineExpose({
openDialog
})
</script>
<style scoped lang="scss">
.strategy-detail {
.section-title {
margin: 0 0 16px 0;
font-size: 16px;
font-weight: 600;
color: #303133;
}
.basic-info {
margin-bottom: 20px;
.info-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
}
.info-item {
display: flex;
align-items: center;
}
.info-label {
min-width: 80px;
color: #606266;
font-weight: 500;
}
.info-value {
color: #303133;
}
}
.field-info {
.field-content {
display: flex;
height: 400px;
}
.tree-section {
width: 300px;
height: 100%;
overflow-y: auto;
padding-right: 16px;
}
.table-section {
flex: 1;
height: 100%;
overflow-y: auto;
}
}
.el-divider {
margin: 20px 0;
}
.dialog-footer {
display: flex;
justify-content: flex-end;
}
}
:deep(.el-tree) {
.el-tree-node__content {
height: 36px;
&:hover {
background-color: #f5f7fa;
}
}
.is-current > .el-tree-node__content {
background-color: #f0f7ff;
color: #1890ff;
}
}
</style>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论