Commit 0b0f16b2 by 周海峰

Merge branch 'master' of https://code.palacesun.com/wuchao/nse-ui

parents f79e9253 a2ea7f0e
import { queryCharset } from './index'
// 连接类型
export const oracleisinsData = [
{ key: "服务名", value: "0", label: "服务名" },
{ key: "实例名", value: "1", label: "实例名" },
]
// 字符编码
export const getCode = (key) => {
if (!key) return
const list = JSON.parse(sessionStorage.getItem(key))
return list
}
// 全部字符编码
export const getCueryCharset = async () => {
if (sessionStorage.getItem('mysql')) return
const databaseTypeList = ['mysql', 'oracle', 'postgresql', 'hive', 'hive_tdh', 'mssqlserver', 'db2', 'mariadb', 'informix', 'sybase', 'sybaseiq', 'sybaseiq', 'gbase', 'gbase8s', 'gbase8a', 'kingbase', 'dm', 'oscar', 'uxdb', 'greenplum', 'teradata', 'highgo', 'cache', 'hadoop', 'gauss', 'oceanbase', 'mongodb', 'es', 'KAFKA', 'gaussdb']
for (let i = 0; i < databaseTypeList.length; i++) {
const data = await queryCharset({ dbtype: databaseTypeList[i] })
sessionStorage.setItem(databaseTypeList[i].toLowerCase(), JSON.stringify(data.data))
}
}
\ No newline at end of file
import request from '@/utils/request'
/**
* 资产库 - 查询用户列表
* @param {*} query
* @returns
*/
export function queryUser(query) {
return request({
url: '/console/user/queryAll',
method: 'get',
params: query
})
}
/**
* 资产库 - 查询列表
* @param {*} query
* @returns
*/
export function queryAll(query) {
return request({
url: '/core/datasystem/queryAll',
method: 'get',
params: query
})
}
/**
* 资产库 - 数据源明细
* @param {*} query
* @returns
*/
export function initEdit(query) {
return request({
url: '/core/datasystem/initEdit',
method: 'get',
params: query
})
}
/**
* 资产库 - 查询数据库版本
* @param {*} query
* @returns
*/
export function queryDatadis(query) {
return request({
url: '/core/datadis/queryAll',
method: 'get',
params: query
})
}
/**
* 资产库 - 数据库版本检测
* @param {*} data
* @returns
*/
export function checkVersion(data) {
return request({
url: '/core/datasystem/checkVersion',
method: 'post',
data: data
})
}
/**
* 资产库 - 测试数据源名称是否重复
* @param {*} data
* @returns
*/
export function checkRepeatName(data) {
return request({
url: '/core/datasystem/checkRepeatName',
method: 'post',
data: data
})
}
/**
* 资产库 - 测试连接
* @param {*} data
* @returns
*/
export function testConnect(data) {
return request({
url: '/core/datasystem/testConnect',
method: 'post',
data: data
})
}
/**
* 资产库 - 新增修改接口
* @param {*} data
* @returns
*/
export function add(data) {
return request({
url: '/core/datasystem/add',
method: 'post',
data: data
})
}
/**
* 资产库 - 关联检测
* @param {*} data
* @returns
*/
export function checkDatasystemUsed(data) {
return request({
url: '/core/datasystem/checkDatasystemUsed',
method: 'post',
data: data
})
}
/**
* 资产库 - 删除数据源
* @param {*} data
* @returns
*/
export function del(data) {
return request({
url: '/core/datasystem/del',
method: 'post',
data: data
})
}
/**
* 资产库 - 查询字符编码
* @param {*} query
* @returns
*/
export function queryCharset(query) {
return request({
url: '/core/datasystem/queryCharset',
method: 'get',
params: query
})
}
\ No newline at end of file
......@@ -45,7 +45,7 @@ const hasTagsView = computed(() => settings.tagsView)
const layout = computed(() => settings.layout)
function onScroll(val) {
appStore.updateScrollTop(val.scrollTop)
// appStore.updateScrollTop(val.scrollTop)
}
</script>
......
......@@ -105,18 +105,18 @@ export const constantRoutes = [
// component: () => import('@/views/projectHome/index'),
// hidden: true
// },
{
// 加密设置
path: '/classification',
component: () => import('@/views/Classification/index'),
hidden: true
},
{
// 加密管理
path: '/encryptionManagement',
component: () => import('@/views/EncryptionManagement/index'),
hidden: true
},
// {
// // 加密设置
// path: '/classification',
// component: () => import('@/views/Classification/index'),
// hidden: true
// },
// {
// // 加密管理
// path: '/encryptionManagement',
// component: () => import('@/views/EncryptionManagement/index'),
// hidden: true
// },
// {
// // 脱敏策略管理
// path: '/desensitizationStrategy',
......
<script setup lang="ts" name="QueryForm">
import { ref, computed } from 'vue'
import { ref, computed,onMounted } from 'vue'
import type { FormInstance } from 'element-plus'
import PageWrapperSearch from '@/components/Search/PageWrapperSearch.vue'
import { useDict } from '@/utils/dict'
import useAppStore from '@/store/modules/app'
const appStore = useAppStore()
const emit = defineEmits(['update:modelValue', 'query', 'reset'])
const employeesList = ref([])
const dataTypeList = ref([])
const props = defineProps<{
modelValue: any
}>()
......@@ -30,6 +32,10 @@ function onReset(formRef: FormInstance) {
emit('reset', formRef)
}
onMounted(() => {
})
</script>
<template>
......@@ -38,14 +44,22 @@ function onReset(formRef: FormInstance) {
@search="onSearch"
@reset="onReset"
label-width='100'>
<el-form-item label="数据源名称" prop="customerName">
<el-input v-model="queryForm.customerName" placeholder="请输入客户名称" clearable />
<el-form-item label="数据源名称" prop="sysname">
<el-input v-model="queryForm.sysname" placeholder="请输入客户名称" clearable />
</el-form-item>
<el-form-item label="IP" prop="customerName">
<el-input v-model="queryForm.customerName" placeholder="请输入客户名称" clearable />
<el-form-item label="IP" prop="ip">
<el-input v-model="queryForm.ip" placeholder="请输入客户名称" clearable />
</el-form-item>
<el-form-item label="数据源类型" prop="customerName">
<el-input v-model="queryForm.customerName" placeholder="请输入客户名称" clearable />
<el-form-item label="数据源类型" prop="dbtype">
<el-select v-model="queryForm.dbtype" placeholder="请选择">
<el-option
v-for="item in appStore.DATABASE_TYPE"
:key="item"
:label="item"
:value="item"
>
</el-option>
</el-select>
</el-form-item>
</page-wrapper-search>
</template>
<script setup name="AssetLibrary">
import { ref, toRefs, reactive, getCurrentInstance } from 'vue'
import { ref, toRefs, reactive, getCurrentInstance, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import QueryForm from './QueryForm.vue'
import FormGroup from './modules/formGroup.vue'
import img from '@/assets/images/assetLibrary/index.js'
import { queryAll, checkDatasystemUsed, del } from '@/api/assetLibrary/index'
import { getCueryCharset } from "@/api/assetLibrary/dict";
const emit = defineEmits(['page'])
const { proxy } = getCurrentInstance()
......@@ -11,7 +16,11 @@ const data = reactive({
name: ''
},
queryParams: {
customerName: null
sysname: '',
dbtype: '',
ip: '',
userId: '1',
projectId: ''
},
rules: {
}
......@@ -24,6 +33,18 @@ const modalData = reactive({
text: ''
})
// 数据源信息
const dataSource = reactive({
current: 0, // 当前索引
currentId: '', // 当前id
currentFlag: '',
list: [], // 数据源列表
del: {
id: '',
sysname: ''
}
})
// 新增
const handleAdd = () => {
emit('page', 'add')
......@@ -32,39 +53,77 @@ const handleAdd = () => {
// 筛选查询
const onQuery = () => {
console.log('onQuery')
// handleQuery()
getList()
}
// 查询组件事件重置
const onReset = (formQuery) => {
console.log('onReset')
formQuery.resetFields()
getList()
}
// 预览
const onPreview = () => {
console.log('onPreview')
const onPreview = (index) => {
dataSource.current = index
dataSource.currentId = dataSource.list[dataSource.current].id
dataSource.currentFlag = dataSource.list[dataSource.current].flag
}
// 删除
const onDelete = () => {
console.log('onPreview')
modalData.show = true
modalData.text = '删除后无法恢复,是否确认删除[若依配测系统]?'
const onDelete = (val) => {
const { id, sysname } = val
checkDatasystemUsed({ id: id }).then(res => {
console.log(res)
const flag = { res }
if (flag) {
dataSource.current = 0
dataSource.del.id = id
dataSource.del.sysname = sysname
modalData.text = `删除后无法恢复,是否确认删除[${sysname}]?`
modalData.show = true
}
})
}
// 确认删除回调
const modalConfirm = () => {
del(dataSource.del).then(res => {
const flag = { res }
if (flag) {
ElMessage.success({
message: '删除成功',
type: 'success',
})
getList()
}
})
modalData.show = false
}
// 编辑成功回调
const onConfirm = () => {
console.log('编辑成功')
// console.log('编辑成功')
getList()
}
// 列表查询
const getList = () => {
queryAll(queryParams.value).then(res => {
// console.log(res)
dataSource.list = res.data
dataSource.currentId = dataSource.list[dataSource.current].id
dataSource.currentFlag = dataSource.list[dataSource.current].flag
})
}
onMounted(() => {
getCueryCharset()
getList()
})
</script>
<template>
<div class="app-container scroller">
<div class="app-container">
<PageTitle>
<template #title>
资产库
......@@ -77,21 +136,22 @@ const onConfirm = () => {
<query-form ref="QueryFormRef" v-model="queryParams" @query="onQuery" @reset="onReset" />
<el-scrollbar>
<div class="flex-content">
<el-card class="box-card" :body-style="{ padding: '0px !important' }">
<img class="img" src="https://172.19.1.166:9005/static/img/MYSQL.535270a.png" alt="">
<el-card class="box-card" :class="dataSource.current === index ? 'active' : ''"
:body-style="{ padding: '0px !important' }" v-for="(item, index) in dataSource.list" :key="index">
<img class="img" :src="img[item.dbtype]" alt="">
<div class="title">
<div class="title-content">若依配测系统</div>
<div class="title-content">{{ item.sysname }}</div>
</div>
<div class="mask">
<div class="maskbutton">
<div class="maskbutton-item" @click="onDelete">
<div class="maskbutton-item" @click="onDelete(item)">
<el-icon :size="18">
<delete />
</el-icon>
<br>
<span>删除</span>
</div>
<div class="maskbutton-item" @click="onPreview">
<div class="maskbutton-item" @click="onPreview(index)" v-if="dataSource.current !== index">
<el-icon :size="18">
<View />
</el-icon>
......@@ -103,9 +163,9 @@ const onConfirm = () => {
</el-card>
</div>
</el-scrollbar>
<div class="mt20">
<div class="mt20" v-if="dataSource.list.length > 0" style="padding-bottom: 20px;">
<div class="mb20">数据源明细:{{ form.name }}</div>
<FormGroup :id="'1111'" page="edit" @confirm="onConfirm" />
<FormGroup :id="dataSource.currentId" :flag="dataSource.currentFlag" page="edit" @confirm="onConfirm" />
</div>
</div>
<Modal v-model="modalData.show" icon="error" :cancel="true" :text="modalData.text" @confirm="modalConfirm"></Modal>
......@@ -115,7 +175,7 @@ const onConfirm = () => {
<style lang="scss" scoped>
.flex-content {
display: flex;
height: 190px;
height: 205px;
.box-card {
position: relative;
flex-shrink: 0;
......@@ -193,6 +253,12 @@ const onConfirm = () => {
}
}
}
.active {
.title {
background: #2c9ef7;
color: #fff;
}
}
.box-card:hover {
width: 250px;
height: 190px;
......
<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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论