Commit 4b137e14 by 周海峰

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

parents 934e77d1 5a397830
<template> <template>
<div v-if="!item.hidden"> <div v-if="!item.hidden">
{{ item }}
<template v-if="hasOneShowingChild(item.children, item) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow"> <template v-if="hasOneShowingChild(item.children, item) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow">
<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)"> <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)">
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{ 'submenu-title-noDropdown': !isNest }"> <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{ 'submenu-title-noDropdown': !isNest }" @click="pageLuyou(onlyOneChild)">
<!-- <svg-icon v-if="onlyOneChild.meta.icon" :icon-class="onlyOneChild.meta.icon"/> --> <!-- <svg-icon :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)"/> -->
<template #title> <template #title><span class="menu-title" :title="hasTitle(onlyOneChild.meta.title)" >{{ onlyOneChild.meta.title }}</span></template>
<span class="menu-title" :title="hasTitle(onlyOneChild.meta.title)">
{{ onlyOneChild.meta.title }}
</span>
</template>
<!-- <span class="menu-title" :title="hasTitle(onlyOneChild.meta.title)">
{{ onlyOneChild.meta.title }}
</span> -->
</el-menu-item> </el-menu-item>
</app-link> </app-link>
</template> </template>
<el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body> <el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" teleported >
<template #title> <template v-if="item.meta" #title>
<!-- <svg-icon v-if="item.meta && item.meta.icon" :icon-class="item.meta.icon" /> --> <span class="menu-title" :title="hasTitle(item.meta.title)" @click="pageLuyou(item)">{{ item.meta.title }}</span>
<span class="menu-title" :title="hasTitle(item.meta.title)">
{{ item.meta.title }}
</span>
</template> </template>
<sidebar-item <sidebar-item
v-for="child in item.children" v-for="(child, index) in item.children"
:key="child.path" :key="child.path + index"
:is-nest="true" :is-nest="true"
:item="child" :item="child"
:base-path="resolvePath(child.path)" :base-path="resolvePath(child.path)"
...@@ -40,9 +30,9 @@ ...@@ -40,9 +30,9 @@
import { isExternal } from '@/utils/validate' import { isExternal } from '@/utils/validate'
import AppLink from './Link' import AppLink from './Link'
import { getNormalPath } from '@/utils/ruoyi' import { getNormalPath } from '@/utils/ruoyi'
import { ref } from 'vue'
const props = defineProps({ const props = defineProps({
// route object
item: { item: {
type: Object, type: Object,
required: true required: true
...@@ -59,6 +49,12 @@ const props = defineProps({ ...@@ -59,6 +49,12 @@ const props = defineProps({
const onlyOneChild = ref({}) const onlyOneChild = ref({})
function pageLuyou(route) {
console.log(route)
}
function hasOneShowingChild(children = [], parent) { function hasOneShowingChild(children = [], parent) {
if (!children) { if (!children) {
children = [] children = []
...@@ -71,10 +67,12 @@ function hasOneShowingChild(children = [], parent) { ...@@ -71,10 +67,12 @@ function hasOneShowingChild(children = [], parent) {
return true return true
}) })
// When there is only one child router, the child router is displayed by default
if (showingChildren.length === 1) { if (showingChildren.length === 1) {
return true return true
} }
// Show parent if there are no child router to display
if (showingChildren.length === 0) { if (showingChildren.length === 0) {
onlyOneChild.value = { ...parent, path: '', noShowingChildren: true } onlyOneChild.value = { ...parent, path: '', noShowingChildren: true }
return true return true
...@@ -83,17 +81,25 @@ function hasOneShowingChild(children = [], parent) { ...@@ -83,17 +81,25 @@ function hasOneShowingChild(children = [], parent) {
return false return false
} }
function resolvePath(routePath) { function resolvePath(routePath, routeQuery) {
if (isExternal(routePath)) { if (isExternal(routePath)) {
return routePath return routePath
} }
if (isExternal(props.basePath)) { if (isExternal(props.basePath)) {
return props.basePath return props.basePath
} }
if (routeQuery) {
let query = JSON.parse(routeQuery)
return { path: getNormalPath(props.basePath + '/' + routePath), query: query }
}
return getNormalPath(props.basePath + '/' + routePath) return getNormalPath(props.basePath + '/' + routePath)
} }
function hasTitle(title) { function hasTitle(title){
return title && title.length > 5 ? title : "" if (title.length > 5) {
return title
} else {
return ""
}
} }
</script> </script>
\ No newline at end of file
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
:item="route" :item="route"
:base-path="route.path" :base-path="route.path"
/> />
<!-- <div v-for="(route, index) in processedRouters">{{ route.path }}</div> -->
</el-menu> </el-menu>
</el-scrollbar> </el-scrollbar>
</div> </div>
...@@ -30,31 +31,17 @@ import SidebarItem from './SidebarItem' ...@@ -30,31 +31,17 @@ import SidebarItem from './SidebarItem'
import variables from '@/assets/styles/variables.module.scss' import variables from '@/assets/styles/variables.module.scss'
import useAppStore from '@/store/modules/app' import useAppStore from '@/store/modules/app'
import useSettingsStore from '@/store/modules/settings' import useSettingsStore from '@/store/modules/settings'
import usePermissionStore from '@/store/modules/permission'
import { computed } from 'vue' import { computed } from 'vue'
const route = useRoute() const route = useRoute()
const appStore = useAppStore() const appStore = useAppStore()
const settingsStore = useSettingsStore() const settingsStore = useSettingsStore()
const permissionStore = usePermissionStore()
console.log('appStore.navList',appStore.navList) console.log('appStore.navList',appStore.navList)
// 转换接口数据为组件需要的格式 // 转换接口数据为组件需要的格式
const processedRouters = computed(() => { const processedRouters = computed(() => {
return appStore.navList.map(item => ({ return permissionStore.sidebarRouters
path: item.path,
meta: {
title: item.name,
icon: item.iconCls,
id: item.id
},
children: item.children?.map(child => ({
path: child.path,
name: child.name,
meta: {
title: child.name,
icon: child.iconCls,
id: child.id
}
})) || []
}))
}) })
const showLogo = computed(() => settingsStore.sidebarLogo) const showLogo = computed(() => settingsStore.sidebarLogo)
......
...@@ -11,7 +11,7 @@ import usePermissionStore from '@/store/modules/permission' ...@@ -11,7 +11,7 @@ import usePermissionStore from '@/store/modules/permission'
NProgress.configure({ showSpinner: false }) NProgress.configure({ showSpinner: false })
const whiteList = ['/login', '/register', '/assetLibrary', '/projectHome', '/projectManage','/classification'] const whiteList = ['/login', '/register', '/assetLibrary', '/projectHome', '/projectManage','/classification','/desensitizationStrategy']
const isWhiteList = (path) => { const isWhiteList = (path) => {
return whiteList.some(pattern => isPathMatch(pattern, path)) return whiteList.some(pattern => isPathMatch(pattern, path))
......
...@@ -63,7 +63,7 @@ export const constantRoutes = [ ...@@ -63,7 +63,7 @@ export const constantRoutes = [
hidden: true hidden: true
}, },
{ {
// 项目首页 // 项目管理
path: '/projectManage', path: '/projectManage',
component: () => import('@/views/projectManage/index'), component: () => import('@/views/projectManage/index'),
hidden: true hidden: true
...@@ -86,6 +86,12 @@ export const constantRoutes = [ ...@@ -86,6 +86,12 @@ export const constantRoutes = [
component: () => import('@/views/EncryptionManagement/index'), component: () => import('@/views/EncryptionManagement/index'),
hidden: true hidden: true
}, },
{
// 脱敏策略管理
path: '/desensitizationStrategy',
component: () => import('@/views/desensitizationStrategy/index'),
hidden: true
},
{ {
path: '', path: '',
component: Layout, component: Layout,
......
...@@ -31,7 +31,11 @@ const usePermissionStore = defineStore( ...@@ -31,7 +31,11 @@ const usePermissionStore = defineStore(
this.topbarRouters = routes this.topbarRouters = routes
}, },
setSidebarRouters(routes) { setSidebarRouters(routes) {
this.sidebarRouters = routes console.log('this.sidebarRoutersold',routes)
let list = filterAsyncRouter(routes)
this.sidebarRouters = list
console.log('this.sidebarRouters',this.sidebarRouters)
}, },
allsidebarRouters(routes){ allsidebarRouters(routes){
this.sidebarRoutersall = routes this.sidebarRoutersall = routes
...@@ -56,26 +60,39 @@ const usePermissionStore = defineStore( ...@@ -56,26 +60,39 @@ const usePermissionStore = defineStore(
resolve(rewriteRoutes) resolve(rewriteRoutes)
}) })
}) })
} },
} }
}) })
// 遍历后台传来的路由字符串,转换为组件对象 // 遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) { function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
return asyncRouterMap.filter(route => { return asyncRouterMap.filter(route => {
if (type && route.children) { if (type && route.children) {
route.children = filterChildren(route.children) route.children = filterChildren(route.children)
} }
if (route.component) { if (route.component) {
// Layout ParentView 组件特殊处理 // Layout ParentView 组件特殊处理
if (route.component === 'Layout') { if (route.component === 'Layout') {
console.log(1111)
route.component = Layout route.component = Layout
} else if (route.component === 'ParentView') { } else if (route.component === 'ParentView') {
console.log(2222)
route.component = ParentView route.component = ParentView
} else if (route.component === 'InnerLink') { } else if (route.component === 'InnerLink') {
console.log(3333)
route.component = InnerLink route.component = InnerLink
} else { } else {
route.component = loadView(route.component) console.log(4444)
// console.log('route.component111',route.component)
// if(route.component === '/project/Project/index'){
route.component = loadView(route.component)
// }
// console.log('route.component',route.component)
} }
} }
if (route.children != null && route.children && route.children.length) { if (route.children != null && route.children && route.children.length) {
...@@ -119,9 +136,12 @@ export function filterDynamicRoutes(routes) { ...@@ -119,9 +136,12 @@ export function filterDynamicRoutes(routes) {
} }
export const loadView = (view) => { export const loadView = (view) => {
// console.log('view',view)
// console.log('modules',modules)
let res let res
for (const path in modules) { for (const path in modules) {
const dir = path.split('views/')[1].split('.vue')[0] const dir = path.split('views/')[1].split('.vue')[0]
// console.log('dir',dir)
if (dir === view) { if (dir === view) {
res = () => modules[path]() res = () => modules[path]()
} }
......
++ "b/src/views/Classification/\345\212\240\345\257\206\350\256\276\347\275\256.md"
...@@ -52,7 +52,35 @@ function getList() { ...@@ -52,7 +52,35 @@ function getList() {
}, 3000); }, 3000);
} }
const tableData = ref([
{
id: '1',
index: 1,
dataSource: '若依配...',
schema: 'ry',
tableName: 'sys_user',
fieldName: 'phone...',
fieldType: 'VARCHAR...',
comment: '手机号码',
algorithm: '姓名加密规则',
secretKey: '姓名加...',
description: '',
status: '加密完成'
},
{id: '2',
index: 2,
dataSource: '若依配...',
schema: 'ry',
tableName: 'sys_user',
fieldName: 'user_n...',
fieldType: 'VARCHAR...',
comment: '用户名昵称',
algorithm: '姓名加密规则',
secretKey: '姓名加...',
description: '',
status: '加密完成'
}
]);
function pageProjectManage() { function pageProjectManage() {
let filterMenus = appStore.navStatus.type === 'manage' ? appStore.allnavList.filter(item => item.type === '1') : appStore.allnavList.filter(item => item.type === '2') let filterMenus = appStore.navStatus.type === 'manage' ? appStore.allnavList.filter(item => item.type === '1') : appStore.allnavList.filter(item => item.type === '2')
...@@ -87,7 +115,46 @@ function bulkEncryption(){ ...@@ -87,7 +115,46 @@ function bulkEncryption(){
function batchDecryption() { function batchDecryption() {
console.log('批量解密') console.log('批量解密')
} }
// 弹窗控制
const dialogVisible = ref(false);
const detailData = ref({
fieldName: '',
status: '',
startTime: '',
endTime: '',
totalRows: 0,
processedRows: 0,
rejectedRows: 0
});
function handleDetail(val) {
// 这里根据实际数据结构调整
detailData.value = {
id: '123',
fieldName: 'phonenumber',
status: '解密完成',
startTime: '2025-08-21 16:55:...',
endTime: '2025-08-21 16:56:...',
totalRows: 4,
processedRows: 0,
rejectedRows: 0
};
dialogVisible.value = true;
}
// 查看日志
const showLog = () => {
console.log('查看日志');
dialogVisibleLog.value = true;
// 这里可以打开日志弹窗或跳转到日志页面
};
const dialogVisibleLog = ref(false);
const logData = ref('12312312312312312')
const downLoad = () => {
console.log('下载日志');
};
</script> </script>
...@@ -128,6 +195,41 @@ function batchDecryption() { ...@@ -128,6 +195,41 @@ function batchDecryption() {
@query="onQuery" @query="onQuery"
@reset="onReset"/> @reset="onReset"/>
<div class="warning-alert">
<div class="warning-content flex-container align-center">
<el-icon color="#f33"><Warning /></el-icon>
<span class="warning-text">警告:存在未加密字段,请完成历史数据加密操作,否则无法正常使用插件!</span>
</div>
</div>
<el-table
:data="tableData"
border
style="width: 100%"
:header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
>
<el-table-column prop="index" label="序号" width="60" align="center" />
<el-table-column prop="dataSource" label="数据源名" width="120" show-overflow-tooltip />
<el-table-column prop="schema" label="SCHEMA" width="100" align="center" />
<el-table-column prop="tableName" label="表名" width="120" show-overflow-tooltip />
<el-table-column prop="fieldName" label="字段名" width="120" show-overflow-tooltip />
<el-table-column prop="fieldType" label="字段类型" width="120" show-overflow-tooltip />
<el-table-column prop="comment" label="注释" min-width="120" show-overflow-tooltip />
<el-table-column prop="algorithm" label="加密算法" min-width="150" show-overflow-tooltip />
<el-table-column prop="secretKey" label="密钥" width="120" show-overflow-tooltip />
<el-table-column prop="description" label="描述信息" width="120" show-overflow-tooltip />
<el-table-column prop="status" label="执行状态" width="120" align="center">
<template #default="scope">
<el-tag :type="scope.row.status === '加密完成' ? 'success' : 'warning'">
{{ scope.row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="100" align="center" fixed="right">
<template #default="scope">
<el-button type="primary" size="small" @click="handleDetail(scope.row.id)">详情</el-button>
</template>
</el-table-column>
</el-table>
<pagination <pagination
v-show="total > 0" v-show="total > 0"
...@@ -138,7 +240,68 @@ function batchDecryption() { ...@@ -138,7 +240,68 @@ function batchDecryption() {
/> />
</div> </div>
</div> </div>
<!-- 详情弹窗 -->
<el-dialog
v-model="dialogVisible"
title="数据详情"
width="50%"
>
<el-descriptions
border
:column="1"
size="large"
class="detail-container"
>
<el-descriptions-item label="字段名">
{{ detailData.fieldName || 'phonenumber' }}
</el-descriptions-item>
<el-descriptions-item label="状态">
<el-tag :type="detailData.status === '解密完成' ? 'success' : 'warning'">
{{ detailData.status || '解密完成' }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="开始时间">
{{ detailData.startTime || '2025-08-21 16:55:...' }}
</el-descriptions-item>
<el-descriptions-item label="结束时间">
{{ detailData.endTime || '2025-08-21 16:56:...' }}
</el-descriptions-item>
<el-descriptions-item label="加密/解密总行数">
{{ detailData.totalRows || 4 }}
</el-descriptions-item>
<el-descriptions-item label="已加密/解密行数">
{{ detailData.processedRows || 0 }}
</el-descriptions-item>
<el-descriptions-item label="拒绝条数">
{{ detailData.rejectedRows || 0 }}
</el-descriptions-item>
</el-descriptions>
<template #footer>
<el-button type="primary" @click="showLog">查看日志</el-button>
</template>
</el-dialog>
<el-dialog
v-model="dialogVisibleLog"
title="日志"
width="50%"
>
<div class="flex-container flex-wrap" v-html="logData">
</div>
<template #footer>
<el-button type="primary" @click="downLoad">下载</el-button>
</template>
</el-dialog>
</div> </div>
</template> </template>
...@@ -172,4 +335,22 @@ function batchDecryption() { ...@@ -172,4 +335,22 @@ function batchDecryption() {
.flex1 { .flex1 {
flex: 1; flex: 1;
} }
.flex-wrap {
flex-direction: wrap;
}
.warning-alert {
width: 100%;
padding: 12px 16px;
margin: 10px 0;
border: 2px solid #f56c6c;
border-radius: 4px;
background-color: #fef0f0;
display: flex;
align-items: center;
}
.warning-text {
color: #606266;
font-size: 14px;
margin-left: 10px;
}
</style> </style>
\ No newline at end of file
++ "b/src/views/EncryptionManagement/\345\212\240\345\257\206\347\256\241\347\220\206.md"
<script setup lang="ts" name="QueryForm">
import { computed,ref,watch } from 'vue'
import type { FormInstance } from 'element-plus'
import PageWrapperSearch from '@/components/search/PageWrapperSearch.vue'
// import { useDict } from '@/utils/dict'
// import { listDept } from '@/api/system/dept'// 部门
// const { approve_status, invoice_status} = useDict('approve_status', 'invoice_status')
const emit = defineEmits(['update:modelValue', 'query', 'reset'])
const invoice_status_filter = ref([])
const employeesList = ref([])
const props = defineProps<{
modelValue: any
}>()
const queryForm = computed({
get() {
return props.modelValue
},
set(val: any) {
console.log('query computed', val)
emit('update:modelValue', val)
}
})
// 搜索
function onSearch() {
emit('query')
}
// 重置
function onReset(formRef: FormInstance) {
queryForm.value.title = ''
queryForm.value.remark = ''
queryForm.value.status = ''
emit('reset', formRef)
}
</script>
<template>
<!-- el-form -->
<page-wrapper-search
:model="queryForm"
@search="onSearch"
@reset="onReset">
<el-form-item label="策略名" prop="title">
<el-input
v-model="queryForm.title"
placeholder="请输入策略名"
clearable
/>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input
v-model="queryForm.remark"
placeholder="请输入备注"
clearable
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select
v-model="queryForm.status"
placeholder="请选择状态"
clearable
>
<el-option label="已启用" value="active" />
<el-option label="未启用" value="inactive" />
</el-select>
</el-form-item>
</page-wrapper-search>
</template>
<style scoped>
</style>
<script setup lang="ts" name="projectManageIndex">
import { ref } from 'vue'
import list from './list.vue'
const widget = {
list: list
}
const page = ref('list')
const params = ref({})
function onChangePage(val: string, param?: any) {
page.value = val
params.value = param ?? {}
}
</script>
<template>
<component :is="widget[page]" v-bind="params" @page="onChangePage" />
</template>
<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'
const appStore = useAppStore()
const permissionStore = usePermissionStore()
const router = useRouter()
const emit = defineEmits(['page'])
const { proxy } = getCurrentInstance()
// 新增策略弹窗引用
const addStrategyDialogRef = ref()
function onReset(formQuery) {
console.log('onReset')
formQuery.resetFields()
handleQuery()
}
function onQuery() {
handleQuery()
}
// 搜索按钮操作
function handleQuery() {
console.log('queryParams',queryParams.value)
queryParams.value.pageNum = 1
getList()
}
const data = reactive({
queryParams: {
pageNum: 1,
pageSize: 8
}
})
// 表格数据
const { queryParams } = toRefs(data)
const total = ref(3)
const loading = ref(false)
// 查询列表
function getList() {
loading.value = true
setTimeout(() => {
loading.value = false
}, 3000)
}
function pageProjectManage() {
let filterMenus = appStore.navStatus.type === 'manage' ?
appStore.allnavList.filter(item => item.type === '1') :
appStore.allnavList.filter(item => item.type === '2')
// 设置导航菜单
appStore.setNav(filterMenus)
permissionStore.setSidebarRouters(filterMenus)
let type = appStore.navStatus.type === 'manage' ? 'project' : 'manage'
appStore.setNavStatus({type: type})
router.push({
path: '/projectManage'
})
}
/**
* 新增策略
*/
function handleAdd(){
addStrategyDialogRef.value.openDialog()
}
const tableData = ref([
{
index: 1,
id: '1',
name: '测试',
remark: '123',
createTime: '2025-08-21 16:55:30',
creator: 'admin',
status: '1'
}
])
const showDetail = (row) => {
console.log('查看详情:', row)
}
const editStrategy = (row) => {
console.log('编辑策略:', row)
}
const deleteStrategy = (row) => {
ElMessageBox.confirm(
`确定删除【${row.name}】策略吗?`,
'删除确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
beforeClose: (action, instance, done) => {
if (action === 'confirm') {
instance.confirmButtonLoading = true
// 这里调用实际删除API
setTimeout(() => {
done()
// 删除成功后刷新数据或从tableData中移除
tableData.value = tableData.value.filter(item => item.index !== row.index)
}, 1000)
} else {
done()
}
}
}
).then(() => {
ElMessage({
type: 'success',
message: '删除成功',
})
}).catch(() => {
// 用户取消删除
})
}
const toggleStatus = (row) => {
console.log('启用/禁用:', row)
// changeRoleStatus(row.roleId, row.status).then(() => {
// ElMessage({
// type: 'success',
// message: '操作成功',
// })
// })
}
</script>
<template>
<div class="app-container scroller">
<PageTitle :back="true" @back="pageProjectManage">
<template #title>
返回项目管理
</template>
<template #buttons>
<el-button
type="primary"
@click="handleAdd"
>
新增策略
</el-button>
</template>
</PageTitle>
<div class="app-container__body">
<div>
<query-form
ref="QueryFormRef"
v-model="queryParams"
@query="onQuery"
@reset="onReset"/>
<el-table
:data="tableData"
border
style="width: 100%"
:header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
>
<el-table-column prop="index" label="序号" width="80" align="center" />
<el-table-column prop="name" label="策略名称" min-width="120" />
<el-table-column prop="remark" label="备注" min-width="120" show-overflow-tooltip />
<el-table-column prop="createTime" label="创建时间" min-width="180" />
<el-table-column prop="creator" label="创建人" width="120" />
<el-table-column prop="status" label="状态" width="100" align="center">
<template #default="{ row }">
<el-tag :type="row.status === '1' ? 'success' : 'info'">
{{ row.status === '1' ? '已启用' : '未启用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="280" align="center">
<template #default="{ row }">
<el-button link type="primary" size="small" @click="showDetail(row)">详情</el-button>
<el-divider direction="vertical" />
<el-button link type="primary" size="small" @click="editStrategy(row)">编辑策略</el-button>
<el-divider direction="vertical" />
<el-button link type="danger" size="small" @click="deleteStrategy(row)">删除</el-button>
<el-divider direction="vertical" />
<el-button link type="success" size="small" @click="toggleStatus(row)">
{{ row.status === '1' ? '禁用' : '启用' }}
</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</div>
</div>
<!-- 新增策略弹窗 -->
<AddStrategyDialog ref="addStrategyDialogRef" />
</div>
</template>
<style lang="scss" scoped>
.table-item {
margin-top: var(--container-pd);
margin-bottom: 20px;
}
.item-content {
margin: 8px 0;
.label {
width: 80px;
color: #888;
}
}
.flex-container {
display: flex;
}
.align-center {
align-items: center;
}
.justify-between {
justify-content: space-between;
}
.flex1 {
flex: 1;
}
.flex-wrap {
flex-direction: wrap;
}
</style>
\ No newline at end of file
<template>
<el-dialog
v-model="dialogVisible"
:title="currentStep === 1 ? '添加策略 - 步骤1/2' : '添加策略 - 步骤2/2'"
width="800px"
:before-close="handleClose"
>
<!-- 步骤1:基本信息 -->
<div v-if="currentStep === 1" class="step-content">
<el-form :model="formData" :rules="rules" ref="step1Form">
<el-form-item label="策略名称" prop="name">
<el-input
v-model="formData.name"
placeholder="请输入策略名称"
clearable
/>
</el-form-item>
<el-form-item label="备注">
<el-input
v-model="formData.remark"
type="textarea"
:rows="4"
placeholder="请输入策略备注信息"
maxlength="200"
show-word-limit
/>
</el-form-item>
</el-form>
</div>
<!-- 步骤2:字段设置 -->
<div v-else class="step-content">
<div class="version-select">
<span>根据发现版本设置脱敏:</span>
<el-select v-model="selectedVersion" placeholder="选择发现版本">
<el-option label="无版本" value="" />
<el-option
v-for="version in versions"
:key="version"
:label="version"
:value="version"
/>
</el-select>
</div>
<div class="field-selection">
<div class="tree-container">
<el-tree
:data="treeData"
:props="treeProps"
@node-click="handleNodeClick"
highlight-current
/>
</div>
<el-divider direction="vertical" />
<div class="table-container">
<el-table :data="tableFields" border style="width: 100%">
<el-table-column prop="isPk" label="主键" width="80" align="center">
<template #default="{ row }">
<span v-if="row.isPk">PK</span>
</template>
</el-table-column>
<el-table-column prop="name" label="字段名" width="150" />
<el-table-column prop="comment" label="注释" width="150" />
<el-table-column prop="dataType" label="数据域" width="120" />
<el-table-column label="脱敏算法" width="180">
<template #default="{ row }">
<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 }">
<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="handleCancel">取消</el-button>
<el-button
v-if="currentStep === 1"
type="primary"
@click="handleNextStep"
>下一步</el-button
>
<el-button
v-else
type="primary"
@click="handlePrevStep"
>上一步</el-button
>
<el-button
v-if="currentStep === 2"
type="primary"
@click="handleConfirm"
>确定</el-button
>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref, reactive } from 'vue';
import { ElMessage } from 'element-plus';
const dialogVisible = ref(false);
const currentStep = ref(1);
// 表单数据
const formData = reactive({
name: '',
remark: '',
});
// 表单验证规则
const rules = reactive({
name: [
{ required: true, message: '请填写策略名称', trigger: 'blur' },
],
});
// 步骤2数据
const selectedVersion = ref('');
const versions = ref(['v1.0', 'v2.0']);
const treeData = ref([
{
id: 'ry',
label: '若依配测系统',
children: [
{
id: 'gen_table',
label: 'gen_table',
type: 'table',
},
{
id: 'gen_table_column',
label: 'gen_table_column',
type: 'table',
},
// 其他表...
],
},
]);
const treeProps = {
children: 'children',
label: 'label',
};
const tableFields = ref([]);
const algorithms = ref([
{ value: 'mask', label: '掩码处理' },
{ value: 'encrypt', label: '加密处理' },
{ value: 'hash', label: '哈希处理' },
]);
// 打开弹窗
const openDialog = () => {
dialogVisible.value = true;
currentStep.value = 1;
resetForm();
};
// 重置表单
const resetForm = () => {
formData.name = '';
formData.remark = '';
selectedVersion.value = '';
tableFields.value = [];
};
// 关闭弹窗
const handleClose = () => {
dialogVisible.value = false;
};
// 取消
const handleCancel = () => {
dialogVisible.value = false;
};
// 下一步
const handleNextStep = () => {
if (!formData.name) {
ElMessage.error('请填写策略名称');
return;
}
currentStep.value = 2;
};
// 上一步
const handlePrevStep = () => {
currentStep.value = 1;
};
// 确定
const handleConfirm = () => {
// 这里调用API提交数据
console.log('提交数据:', {
...formData,
version: selectedVersion.value,
fields: tableFields.value,
});
dialogVisible.value = false;
ElMessage.success('策略添加成功');
};
// 树节点点击
const handleNodeClick = (node) => {
if (node.type === 'table') {
// 模拟根据表名获取字段
fetchTableFields(node.label);
}
};
// 获取表字段
const fetchTableFields = (tableName) => {
// 这里应该是API调用,根据表名获取字段
// 模拟数据
if (tableName === 'gen_table_column') {
tableFields.value = [
{
isPk: true,
name: 'column_id',
comment: '编号',
dataType: 'bigint',
algorithm: '',
},
{
isPk: false,
name: 'table_id',
comment: '归属表编号',
dataType: 'bigint',
algorithm: '',
},
// 其他字段...
];
} else {
tableFields.value = [];
}
};
// 设置字段
const handleSet = (row) => {
// 这里可以打开更详细的设置弹窗
console.log('设置字段:', row);
};
defineExpose({
openDialog,
});
</script>
<style scoped>
.step-content {
min-height: 400px;
}
.version-select {
margin-bottom: 20px;
}
.field-selection {
display: flex;
height: 400px;
}
.tree-container {
width: 250px;
height: 100%;
overflow-y: auto;
padding-right: 10px;
}
.table-container {
flex: 1;
height: 100%;
overflow-y: auto;
}
.dialog-footer {
display: flex;
justify-content: flex-end;
}
.el-divider {
height: 100%;
margin: 0 10px;
}
</style>
\ No newline at end of file
++ "b/src/views/desensitizationStrategy/\350\204\261\346\225\217\347\255\226\347\225\245\347\256\241\347\220\206.md"
...@@ -1076,12 +1076,13 @@ function goTarget(url) { ...@@ -1076,12 +1076,13 @@ function goTarget(url) {
} }
function checkMenuType() { function checkMenuType() {
console.log('appStore.navStatus',appStore.navStatus.type) // console.log('appStore.allnavList',appStore.allnavList)
// console.log('appStore.navStatus',appStore.navStatus.type)
let filterMenus = appStore.navStatus.type === 'manage' ? appStore.allnavList.filter(item => item.type === '1') : appStore.allnavList.filter(item => item.type === '2') let filterMenus = appStore.navStatus.type === 'manage' ? appStore.allnavList.filter(item => item.type === '1') : appStore.allnavList.filter(item => item.type === '2')
// 设置导航菜单 // 设置导航菜单
console.log('filterMenus',filterMenus)
appStore.setNav(filterMenus) appStore.setNav(filterMenus)
permissionStore.setSidebarRouters(filterMenus) permissionStore.setSidebarRouters(filterMenus)
......
...@@ -115,100 +115,81 @@ function handleLogin() { ...@@ -115,100 +115,81 @@ function handleLogin() {
proxy.$refs.loginRef.validate(valid => { proxy.$refs.loginRef.validate(valid => {
if (valid) { if (valid) {
loading.value = true loading.value = true
// // 勾选了需要记住密码设置在 cookie 中设置记住用户名和密码
// if (loginForm.value.rememberMe) {
// Cookies.set("username", loginForm.value.username, { expires: 30 })
// Cookies.set("password", encrypt(loginForm.value.password), { expires: 30 })
// Cookies.set("rememberMe", loginForm.value.rememberMe, { expires: 30 })
// } else {
// // 否则移除
// Cookies.remove("username")
// Cookies.remove("password")
// Cookies.remove("rememberMe")
// }
// 调用action的登录方法
userStore.login(loginForm.value).then(async (res) => { userStore.login(loginForm.value).then(async (res) => {
console.log('res123',res) console.log('res123',res)
let data = res let data = res
let menus = data.menus.map(item => { let menus = transformRoutes(data.menus)
item.children = item.submenu.map(child => { console.log(1234,menus)
return { // return
children: [],
iconCls: child.icon,
id: child.id,
keepAlive: false,
name: child.menuname,
path: child.url,
requireAuth: false
}
})
return {
children: item.children,
name: item.menuname,
id: item.id,
path: item.url,
type: item.type,
iconCls: item.icon
}
})
// 用户数据整理 // 用户数据整理
let user = data.tsysUser; let user = data.tsysUser
let loginBlankMenus = false let loginBlankMenus = false
if(data.menus.length === 0) { if(data.menus.length === 0) {
loginBlankMenus = true loginBlankMenus = true
} }
try {
await appStore.signOut('login') try {
let filterMenus = appStore.navStatus.type === 'manage' ? menus.filter(item => item.type === '1') : menus.filter(item => item.type === '2') await appStore.signOut('login')
// 设置导航菜单
appStore.setNav(filterMenus)
appStore.setallNav(menus)
// console.log('menus',menus)
// return
console.log('menus',filterMenus) let filterMenus = appStore.navStatus.type === 'manage' ? menus.filter(item => item.type === '1') : menus.filter(item => item.type === '2')
appStore.setNav(filterMenus)
appStore.setallNav(menus)
permissionStore.allsidebarRouters(menus) // console.log('menus',filterMenus)
permissionStore.setSidebarRouters(filterMenus) // permissionStore.allsidebarRouters(menus)
// 设置用户信息
appStore.setUserInfo({user, licenseRst: data.licenseRst, loginBlankMenus})
// 存储token和登录信息
window.sessionStorage.setItem('__token', data.token)
const loginStr = JSON.stringify({
username: loginForm.value.username,
password: encryptPassword(loginForm.value.password)
})
window.sessionStorage.setItem('login', loginStr)
const query = route.query
const otherQueryParams = Object.keys(query).reduce((acc, cur) => {
if (cur !== "redirect") {
acc[cur] = query[cur]
}
return acc
}, {})
router.push({ path:"/index", query: otherQueryParams }) permissionStore.setSidebarRouters(filterMenus)
// router.push({ path: redirect.value || "/index", query: otherQueryParams }) // console.log('filterMenus',filterMenus)
// 设置导航菜单
// return
// 设置用户信息
appStore.setUserInfo({user, licenseRst: data.licenseRst, loginBlankMenus})
// 存储token和登录信息
window.sessionStorage.setItem('__token', data.token)
const loginStr = JSON.stringify({
username: loginForm.value.username,
password: encryptPassword(loginForm.value.password)
})
window.sessionStorage.setItem('login', loginStr)
const query = route.query
const otherQueryParams = Object.keys(query).reduce((acc, cur) => {
if (cur !== "redirect") {
acc[cur] = query[cur]
}
return acc
}, {})
// console.log('generateRoute', getRout)
// router.push({ path:"/index", query: otherQueryParams })
router.push({ path: redirect.value || "/index", query: otherQueryParams })
// if(redirect.value){ // if(redirect.value){
// console.log('redirect',redirect.value) // console.log('redirect',redirect.value)
// router.push({ path: redirect.value, query: otherQueryParams }) // router.push({ path: redirect.value, query: otherQueryParams })
// router.push({ path: redirect.value, query: otherQueryParams }) // router.push({ path: redirect.value, query: otherQueryParams })
// console.log(2134) // console.log(2134)
// }else{ // }else{
// router.push({ path: "/", query: otherQueryParams }) // router.push({ path: "/", query: otherQueryParams })
// } // }
// console.log('redirect',redirect.value) // console.log('redirect',redirect.value)
// 跳转页面或其他操作 // 跳转页面或其他操作
// router.push('/dashboard') // router.push('/dashboard')
} catch (error) { } catch (error) {
console.error('登录处理失败:', error) console.error('登录处理失败:', error)
} }
...@@ -222,7 +203,70 @@ function handleLogin() { ...@@ -222,7 +203,70 @@ function handleLogin() {
} }
}) })
} }
function transformRoutes(originalRoutes) {
return originalRoutes.map(route => {
// 基础转换 - 所有路由都使用Layout作为父组件
const transformedRoute = {
path: route.url.replace(/^\//, ''),
name: route.menuname.replace(/\s+/g, ''),
hidden: false,
type: route.type,
component: 'Layout', // 所有路由都使用Layout
redirect: 'noRedirect',
meta: {
title: route.menuname,
icon: route.icon,
noCache: false,
link: null
},
children: [] // 初始化children数组
};
// 处理有子路由的情况
if (route.submenu && route.submenu.length > 0) {
transformedRoute.children = route.submenu.map(child => ({
path: child.url.startsWith('/') ? child.url.substring(1) : child.url,
name: child.menuname.replace(/\s+/g, ''),
hidden: false,
component:`${route.url.replace(/^\//, '')}/${child.url}/index`,
meta: {
title: child.menuname,
icon: child.icon,
noCache: false,
link: null
}
}));
}
// 处理没有子路由的情况 - 将自己作为唯一的子路由
else {
transformedRoute.children.push({
path: '',
name: `${route.menuname.replace(/\s+/g, '')}`,
hidden: false,
component: `${route.url.replace(/^\//, '')}/index`,
meta: {
title: route.menuname,
icon: route.icon,
noCache: false,
link: null
}
});
}
// 添加alwaysShow属性给有子路由的项
if (transformedRoute.children && transformedRoute.children.length > 0) {
transformedRoute.alwaysShow = true;
}
// console.log('transformedRoute',transformedRoute)
return transformedRoute;
});
}
// // 使用示例
// const transformedRoutes = transformRoutes(originalRoutes);
// console.log(transformedRoutes);
function encryptPassword(plainPassword) { function encryptPassword(plainPassword) {
const sm4Config = { const sm4Config = {
key: "GJstSK_YBD=gSOFT", key: "GJstSK_YBD=gSOFT",
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论