Commit c55f569c by ningjihai

123

parent 265405b7
...@@ -2,12 +2,12 @@ ...@@ -2,12 +2,12 @@
<el-dialog <el-dialog
v-model="dialogVisible" v-model="dialogVisible"
:title="currentStep === 1 ? '添加策略 - 步骤1/2' : '添加策略 - 步骤2/2'" :title="currentStep === 1 ? '添加策略 - 步骤1/2' : '添加策略 - 步骤2/2'"
width="800px" width="80%"
:before-close="handleClose" :before-close="handleClose"
> >
<!-- 步骤1:基本信息 --> <!-- 步骤1:基本信息 -->
<div v-if="currentStep === 1" class="step-content"> <div v-if="currentStep === 1" class="step-content">
<el-form :model="formData" :rules="rules" ref="step1Form"> <el-form :model="formData" :rules="rules" ref="step1Form" label-width="120px">
<el-form-item label="策略名称" prop="name"> <el-form-item label="策略名称" prop="name">
<el-input <el-input
v-model="formData.name" v-model="formData.name"
...@@ -32,15 +32,21 @@ ...@@ -32,15 +32,21 @@
<div v-else class="step-content"> <div v-else class="step-content">
<div class="version-select"> <div class="version-select">
<span>根据发现版本设置脱敏:</span> <span>根据发现版本设置脱敏:</span>
<el-select v-model="selectedVersion" placeholder="选择发现版本"> <div class="flex-container align-center">
<el-option label="无版本" value="" /> <div>
<el-option 选择发现版本
v-for="version in versions" </div>
:key="version" <el-select class="version-select-content flex1" v-model="selectedVersion" placeholder="选择发现版本">
:label="version" <el-option label="无版本" value="" />
:value="version" <el-option
/> v-for="version in versions"
</el-select> :key="version"
:label="version"
:value="version"
/>
</el-select>
</div>
</div> </div>
<div class="field-selection"> <div class="field-selection">
...@@ -54,32 +60,17 @@ ...@@ -54,32 +60,17 @@
</div> </div>
<el-divider direction="vertical" /> <el-divider direction="vertical" />
<div class="table-container"> <div class="table-container">
<el-table :data="tableFields" border style="width: 100%"> <el-table :data="tableFields" border style="width: 100%" height="400px">
<el-table-column prop="isPk" label="主键" width="80" align="center"> <el-table-column prop="isPk" label="主键" width="80" align="center" fixed>
<template #default="{ row }"> <template #default="{ row }">
<span v-if="row.isPk">PK</span> <span v-if="row.isPk">PK</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="name" label="字段名" width="150" /> <el-table-column prop="name" label="字段名" min-widthwidth="150" />
<el-table-column prop="comment" label="注释" width="150" /> <el-table-column prop="comment" label="注释" min-width="150" />
<el-table-column prop="dataType" label="数据域" width="120" /> <el-table-column prop="dataType" label="数据域" min-width="120" />
<el-table-column label="脱敏算法" width="180"> <el-table-column prop="algorithm" label="脱敏算法" min-width="180" />
<template #default="{ row }"> <el-table-column label="操作" width="80" align="center" fixed="right">
<el-select
v-model="row.algorithm"
placeholder="选择算法"
size="small"
>
<el-option
v-for="algo in algorithms"
:key="algo.value"
:label="algo.label"
:value="algo.value"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="操作" width="80" align="center">
<template #default="{ row }"> <template #default="{ row }">
<el-button type="text" size="small" @click="handleSet(row)" <el-button type="text" size="small" @click="handleSet(row)"
>设置</el-button >设置</el-button
...@@ -115,14 +106,25 @@ ...@@ -115,14 +106,25 @@
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
<!-- 脱敏规则设置弹窗 -->
<DesensitizationRuleDialog
v-model="desensitizationDialogVisible"
:current-field="currentField"
@confirm="handleRuleConfirm"
/>
</template> </template>
<script setup> <script setup>
import DesensitizationRuleDialog from './DesensitizationRuleDialog.vue';
import { ref, reactive } from 'vue'; import { ref, reactive } from 'vue';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
const dialogVisible = ref(false); const dialogVisible = ref(false);
const currentStep = ref(1); const currentStep = ref(1);
const desensitizationDialogVisible = ref(false);
const currentField = ref({});
// 表单数据 // 表单数据
const formData = reactive({ const formData = reactive({
...@@ -142,22 +144,83 @@ const selectedVersion = ref(''); ...@@ -142,22 +144,83 @@ const selectedVersion = ref('');
const versions = ref(['v1.0', 'v2.0']); const versions = ref(['v1.0', 'v2.0']);
const treeData = ref([ const treeData = ref([
{ {
id: 'ry', id: 'system1',
label: '若依配测系统', label: '若依测试系统1',
children: [ children: [
{ {
id: 'gen_table', id: 'ry',
label: 'gen_table', label: 'ry',
type: 'table', children: [
}, {
{ id: 'tables',
id: 'gen_table_column', label: '表',
label: 'gen_table_column', children: [
type: 'table', {
}, 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 = { const treeProps = {
children: 'children', children: 'children',
...@@ -258,8 +321,18 @@ const fetchTableFields = (tableName) => { ...@@ -258,8 +321,18 @@ const fetchTableFields = (tableName) => {
// 设置字段 // 设置字段
const handleSet = (row) => { const handleSet = (row) => {
// 这里可以打开更详细的设置弹窗 currentField.value = row;
console.log('设置字段:', 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({ defineExpose({
...@@ -267,7 +340,7 @@ defineExpose({ ...@@ -267,7 +340,7 @@ defineExpose({
}); });
</script> </script>
<style scoped> <style scoped lang="scss">
.step-content { .step-content {
min-height: 400px; min-height: 400px;
} }
...@@ -283,7 +356,7 @@ defineExpose({ ...@@ -283,7 +356,7 @@ defineExpose({
.tree-container { .tree-container {
width: 250px; width: 250px;
height: 100%; height: 400px;
overflow-y: auto; overflow-y: auto;
padding-right: 10px; padding-right: 10px;
} }
...@@ -303,4 +376,23 @@ defineExpose({ ...@@ -303,4 +376,23 @@ defineExpose({
height: 100%; height: 100%;
margin: 0 10px; margin: 0 10px;
} }
.flex-container {
display: flex;
}
.align-center {
align-items: center;
}
.justify-between {
justify-content: space-between;
}
.flex1 {
flex: 1;
}
.version-select-content {
margin-left: 15px;
}
</style> </style>
\ No newline at end of file
<template>
<el-dialog
v-model="dialogVisible"
title="设置脱敏规则"
width="800px"
:before-close="handleClose"
>
<div class="desensitization-dialog">
<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>
</div>
<!-- 选项标签 -->
<span class="option-label">{{ domain.label }}</span>
</div>
</div>
</el-card>
</div>
<!-- <el-divider direction="vertical" /> -->
<!-- 右侧:选择字段脱敏规则 -->
<div class="algorithm-section">
<h4>选择字段脱敏规则</h4>
<div class="algorithm-list">
<el-radio-group v-model="selectedAlgorithm">
<el-radio
v-for="algorithm in currentAlgorithms"
:key="algorithm.value"
:label="algorithm.value"
class="algorithm-item"
>
{{ algorithm.label }}
</el-radio>
</el-radio-group>
</div>
</div>
</div>
<el-divider style="height: 1px;"/>
<!-- 底部按钮 -->
<div class="dialog-footer">
<el-button @click="resetRules">重置规则</el-button>
<el-button type="primary" @click="confirmRules">确定规则</el-button>
</div>
</div>
</el-dialog>
</template>
<script setup>
import { ref, computed, watch, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
const props = defineProps({
modelValue: {
type: Boolean,
default: false
},
currentField: {
type: Object,
default: () => ({})
}
})
const emit = defineEmits(['update:modelValue', 'confirm'])
// 弹窗显示控制
const dialogVisible = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value)
})
// 数据域选项(完全按照图片顺序)
const dataDomains = ref([
{ value: 'wang', label: '小王' },
{ value: 'test', label: '测试数据域' },
{ value: 'general', label: '通用规则' },
{ value: 'decrypt', label: '解密数据' },
{ value: 'mixed_id', label: '混合证件号' },
{ value: 'black_white', label: '黑白名单' },
{ value: 'english_address', label: '英文地址' },
{ value: 'name', label: '姓名' },
{ value: 'phone', label: '手机号码' },
{ value: 'age', label: '年龄' },
{ value: 'business_license', label: '营业执照' },
{ value: 'mixed_phone', label: '混合电话号码' }
])
// 脱敏算法选项
const algorithmOptions = {
// 通用规则
general: [
{ value: 'general_mask', label: '通用掩码' },
{ value: 'general_encrypt', label: '通用加密' },
{ value: 'general_hash', label: '通用哈希' }
],
// 姓名
name: [
{ value: 'name_mask', label: '姓名掩码' },
{ value: 'name_random', label: '姓名随机替换' },
{ value: 'name_encrypt', label: '姓名加密' }
],
// 手机号码
phone: [
{ value: 'phone_mask', label: '手机号掩码' },
{ value: 'phone_encrypt', label: '手机号加密' },
{ value: 'phone_hash', label: '手机号哈希' }
],
// 其他数据域的算法...
wang: [
{ value: 'wang_mask', label: '小王掩码' },
{ value: 'wang_encrypt', label: '小王加密' }
],
test: [
{ value: 'test_mask', label: '测试掩码' },
{ value: 'test_encrypt', label: '测试加密' }
],
// 默认算法
default: [
{ value: 'mask', label: '掩码处理' },
{ value: 'encrypt', label: '加密处理' },
{ value: 'hash', label: '哈希处理' }
]
}
// 选中的数据域和算法
const selectedDomain = ref('general')
const selectedAlgorithm = ref('')
// 计算当前可用的脱敏算法
const currentAlgorithms = computed(() => {
return algorithmOptions[selectedDomain.value] || algorithmOptions.default
})
// 选择数据域
const selectDomain = (value) => {
selectedDomain.value = value
const algorithms = currentAlgorithms.value
if (algorithms.length > 0) {
selectedAlgorithm.value = algorithms[0].value
}
}
// 重置选择函数
const resetSelection = () => {
selectedDomain.value = 'general'
const algorithms = currentAlgorithms.value
if (algorithms.length > 0) {
selectedAlgorithm.value = algorithms[0].value
}
}
// 根据算法推断数据域
const inferDomainFromAlgorithm = (algorithm) => {
for (const [domain, algorithms] of Object.entries(algorithmOptions)) {
if (algorithms.some(algo => algo.value === algorithm)) {
selectedDomain.value = domain
return
}
}
selectedDomain.value = 'general'
}
// 监听当前字段变化,恢复已保存的规则
watch(() => props.currentField, (newField) => {
if (newField && newField.algorithm) {
selectedAlgorithm.value = newField.algorithm
inferDomainFromAlgorithm(newField.algorithm)
} else {
resetSelection()
}
}, { immediate: true })
// 组件挂载时初始化
onMounted(() => {
resetSelection()
})
// 关闭弹窗
const handleClose = () => {
dialogVisible.value = false
}
// 重置规则
const resetRules = () => {
resetSelection()
ElMessage.info('规则已重置')
}
// 确定规则
const confirmRules = () => {
if (!selectedAlgorithm.value) {
ElMessage.warning('请选择脱敏规则')
return
}
emit('confirm', {
algorithm: selectedAlgorithm.value,
domain: selectedDomain.value,
fieldName: props.currentField.name
})
ElMessage.success('脱敏规则设置成功')
dialogVisible.value = false
}
</script>
<style scoped lang="scss">
.desensitization-dialog {
.dialog-content {
display: flex;
height: 400px;
}
.data-domain-section {
flex: 1;
display: flex;
flex-direction: column;
padding-right: 20px;
}
.algorithm-section {
flex: 1;
display: flex;
flex-direction: column;
padding-left: 20px;
}
.selector-header {
padding: 0 0 16px 0;
margin-bottom: 8px;
border-bottom: 1px dashed #dcdfe6;
.selector-title {
font-size: 16px;
font-weight: 900;
color: #000000;
line-height: 1.5;
display: block;
}
}
.options-list {
display: flex;
flex-direction: column;
gap: 0;
flex: 1;
overflow-y: auto;
}
.option-item {
display: flex;
align-items: center;
padding: 12px 0;
cursor: pointer;
transition: background-color 0.2s ease;
border-bottom: 1px dashed #e0e0e0;
&:last-child {
border-bottom: none;
}
&:hover {
background-color: #f8f9fa;
}
&.option-selected {
background-color: #f0f7ff;
.option-label {
color: #1890ff;
font-weight: 500;
}
}
}
.custom-radio {
display: flex;
align-items: center;
justify-content: center;
margin-right: 12px;
width: 20px;
height: 20px;
flex-shrink: 0;
}
.radio-outer {
width: 16px;
height: 16px;
border: 2px solid #c0c4cc;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
.option-selected & {
border-color: #1890ff;
background-color: #1890ff;
}
}
.radio-inner {
width: 6px;
height: 6px;
border-radius: 50%;
background-color: transparent;
transition: background-color 0.2s ease;
&.selected {
background-color: #ffffff;
}
}
.option-label {
font-size: 14px;
color: #606266;
line-height: 1.5;
flex: 1;
user-select: none;
.option-selected & {
color: #1890ff;
}
}
h4 {
margin: 0 0 16px 0;
font-size: 14px;
font-weight: 600;
color: #606266;
}
.algorithm-list {
flex: 1;
overflow-y: auto;
}
.algorithm-item {
display: block;
margin: 8px 0;
padding: 8px 12px;
border-radius: 4px;
transition: background-color 0.3s;
&:hover {
background-color: #f5f7fa;
}
:deep(.el-radio__label) {
font-size: 13px;
}
}
.el-divider {
// height: 360px;
// margin: 0 20px;
}
.dialog-footer {
display: flex;
justify-content: flex-end;
margin-top: 20px;
gap: 12px;
}
}
</style>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论